BizzyDizzy
BizzyDizzy

Reputation: 279

JAX-RS security - principal not persisting

First the code:

Application setting class:

    @ApplicationPath("/rest")
    public class ApplicationConfig extends Application {

    }

JAX-RS resource class:

    @Path("/test")
    @RequestScoped
    public class TestWS {
        @POST
        @Path("/login")
        @Produces(MediaType.TEXT_PLAIN)
        public Response login(@Context HttpServletRequest req){
            req.getSession().setAttribute("test","test");
            System.out.println(req.getSession().getId());
            if(req.getUserPrincipal() == null){
                String authHeader = req.getHeader(HttpHeaders.AUTHORIZATION);
                if(authHeader != null && !authHeader.isEmpty()){
                    String base64Credentials = authHeader.substring("Basic".length()).trim();
                    String credentials = new String(Base64.getDecoder().decode(base64Credentials),
                            Charset.forName("UTF-8"));
                    final String[] values = credentials.split(":",2);
                    try {
                        req.login(values[0], values[1]);
                        System.out.println(req.getUserPrincipal().toString());
                    }
                    catch(ServletException e){
                        e.printStackTrace();
                        return Response.status(Response.Status.UNAUTHORIZED).build();
                    }
                }
            }else{
                System.out.println(req.getUserPrincipal());
                System.out.println(req.isUserInRole("User"));
                req.getServletContext().log("Skipped logged because already logged in!");
            }

            req.getServletContext().log("Authentication Demo: successfully retrieved User Profile!");
            return Response.ok().build();
        }

        @Path("/ping")
        @Produces(MediaType.APPLICATION_JSON)
        @GET
        public String ping(@Context HttpServletRequest req){
            Object test = req.getSession().getAttribute("test");
            System.out.println(req.getSession().getId());
            System.out.println(test);
            System.out.println(req.getUserPrincipal());
            System.out.println(req.isUserInRole("User"));
            return "{\"status\":\"ok\"}";
        }
    }

web.xml:

    <?xml version="1.0" encoding="UTF-8" ?>
    <web-app>
        <context-param>
            <param-name>resteasy.role.based.security</param-name>
            <param-value>true</param-value>
        </context-param>

        <security-constraint>
            <web-resource-collection>
                <web-resource-name>rest</web-resource-name>
                <url-pattern>/rest/*</url-pattern>
                <http-method>GET</http-method>
                <http-method>POST</http-method>
                <http-method>PUT</http-method>
                <http-method>DELETE</http-method>
            </web-resource-collection>
            <user-data-constraint>
                <transport-guarantee>NONE</transport-guarantee>
            </user-data-constraint>
        </security-constraint>

        <welcome-file-list>
            <welcome-file>index.html</welcome-file>
        </welcome-file-list>

        <session-config>
            <session-timeout>30</session-timeout>
        </session-config>

        <login-config>
            <auth-method>BASIC</auth-method>
            <realm-name>PBKDF2DatabaseDomain</realm-name>
        </login-config>
    </web-app>

jboss-web.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <jboss-web>
        <context-root>/</context-root>
        <security-domain>PBKDF2DatabaseDomain</security-domain>
    </jboss-web>

security settings in standalone.xml

    <subsystem xmlns="urn:jboss:domain:security:1.2">
        <security-domains>
            <security-domain name="PBKDF2DatabaseDomain" cache-type="default">
                <authentication>
                    <login-module code="de.rtner.security.auth.spi.SaltedDatabaseServerLoginModule" flag="required" module="de.rtner.PBKDF2">
                        <module-option name="dsJndiName" value="java:jboss/datasources/developmentDS"/>
                        <module-option name="principalsQuery" value="SELECT password FROM ur.user WHERE username=?"/>
                        <module-option name="rolesQuery" value="select distinct r.NAME, 'Roles' from ur.user_roles ur left join ur.ct_role r on ur.ROLE_ID = r.ID left join ur.user u on ur.USER_ID = u.ID where u.username =?"/>
                    </login-module>
                </authentication>
            </security-domain>
...

Now my problem is that after I use /login method with BASIC auth header in request I already get user principal so it prints out:

Skipped logged because already logged in!

And if I remove BASIC auth header from request and I call login again I still get the same print out - so I already have user principal in request - no need to req.login.

BUT if I call ping method user principal is null even tho session id is the same and session attribute is set. What am I doing wrong? I would like that user principal would persist on /ping like it does on /login.

I am using Wildfly 10 (RESTeasy jax-rs implementation)

My question is similalr to: JBOSS AS7 jax-rs jaas and annotations

But the fix - setting session attribute in login method didn't work for me.

Upvotes: 1

Views: 1424

Answers (1)

R&#233;mi Bantos
R&#233;mi Bantos

Reputation: 1967

Some hints and remarks:

  • As REST is stateless I would manage the session on client side instead of persisting a client state on server side.
  • Stateless authentication can be achieved by sending back your BASIC authentication header for every requests/service that you want to be restricted to authenticated users only such as /login and /ping
  • /login service could just throw a WebApplicationException when user is not well authenticated, recognized, in order for your client application to manage it and display an authentication error message to the end user. Then you could use @RolesAllowed('something') annotation with JAAS above your /ping method to restrict access to authenticated user having 'something' role. Thus you would not need to write authentication or authorization code anymore and benefit from an authorization layer in your app.

All this can be achieved on Wildfly with Java EE 7 and without extra libraries.

Upvotes: 1

Related Questions