jersey security and session management

Solution 1:

Session management is the purview of the container in which Jersey is deployed. In most production cases, it will be deployed within a container that performs session management.

The code below is a simple example of a jersey resource that gets the session object and stores values in the session and retrieves them on subsequent calls.

@Path("/helloworld")
public class HelloWorld {

    @GET
    @Produces("text/plain")
    public String hello(@Context HttpServletRequest req) {

        HttpSession session= req.getSession(true);
        Object foo = session.getAttribute("foo");
        if (foo!=null) {
            System.out.println(foo.toString());
        } else {
            foo = "bar";
            session.setAttribute("foo", "bar");
        }
        return foo.toString();


    }
}

Solution 2:

I thought that sessions is something we should never use in RESTful applications...

Yegor is right. We shouldn't never maintain state in the server side a la conventional web application. If you want to build a decoupled SOA-oriented application you don't need to use any API/framework for REST web services. If you need, or want, to maintain the global client-server state in the server side you are implicitly building what we could describe as a SOA-oriented [web]app, but using Jersey like a [web] development framework of sorts. Inadvertently you are twisting the nature of a web service (REST or otherwise). You can do it in the way it's been suggested in the first answer, but you mustn't. The final result is not a web service, just a regular app constructed with web services' tools.

-_o

Solution 3:

Yes it's possible. Jersey documentation says:

Security information of a request is available by injecting a JAX-RS SecurityContext instance using @Context annotation. The injected security context instance provides the equivalent of the functionality available on HttpServletRequest API. The injected security context depends on the actual Jersey application deployment. For example, for a Jersey application deployed in a Servlet container, the Jersey SecurityContext will encapsulate information from a security context retrieved from the Servlet request. In case of a Jersey application deployed on a Grizzly server, the SecurityContext will return information retrieved from the Grizzly request.

Example:

@Path("basket")
public ShoppingBasketResource get(@Context SecurityContext sc) {
    if (sc.isUserInRole("PreferredCustomer") {
        return new PreferredCustomerShoppingBasketResource();
    } else {
        return new ShoppingBasketResource();
    }
}

or

@Path("resource")
@Singleton
public static class MyResource {
    // Jersey will inject proxy of Security Context
    @Context
    SecurityContext securityContext;

    @GET
    public String getUserPrincipal() {
        return securityContext.getUserPrincipal().getName();
    }
}

Or if you want security out of the box with annotations check these docs.

Jersey also allows you to customize the SecurityContext:

The SecurityContext can be directly retrieved from ContainerRequestContext via getSecurityContext() method. You can also replace the default SecurityContext in a request context with a custom one using the setSecurityContext(SecurityContext) method. If you set a custom SecurityContext instance in your ContainerRequestFilter, this security context instance will be used for injection into JAX-RS resource class fields. This way you can implement a custom authentication filter that may setup your own SecurityContext to be used. To ensure the early execution of your custom authentication request filter, set the filter priority to AUTHENTICATION using constants from Priorities. An early execution of you authentication filter will ensure that all other filters, resources, resource methods and sub-resource locators will execute with your custom SecurityContext instance.

See examples on how to use request filters with Jersey. And check my following example:

import javax.annotation.Priority;
import javax.ws.rs.Priorities;

@Provider
@Priority(Priorities.AUTHENTICATION)
public class AuthRequestFilter implements ContainerRequestFilter {
    @Context
    HttpServletRequest webRequest;

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        final HttpSession session = webRequest.getSession();

        requestContext.setSecurityContext(new SecurityContext() {
            @Override
            public Principal getUserPrincipal() {
                return new PrincipalImpl((String)session.getAttribute("USER_NAME"));
            }

            @Override
            public boolean isUserInRole(String s) {
                return false;
            }

            @Override
            public boolean isSecure() {
                return false;
            }

            @Override
            public String getAuthenticationScheme() {
                return null;
            }
        });
    }
}

Warning! This was introduced in Jersey 2.4. Glassfish 4.0.0 uses old Jersey 2.0 therefore you will have to upgrade Jersey using these tips (it's not proven to work well). Or the better way is to download the nightly build of Glassfish 4.0.1. but it's not completely stable at the moment. I hope the new version will be released soon.

UPDATE: At the moment (2014-02-14) Glassfish 4.0.1 nightly build uses Jersey 2.5.1 and context injection works great.

Solution 4:

Jack's response about sessions is correct. They are specific to the container that you execute in, although the Servlet spec at least gives you portability between JavaEE containers.

As for security, you at least have the opportunity to separate it from your JAX-RS specific code by employing JaaS (Java Authentication and Authorization Service) and a servlet filter. The filter can be used to enforce HTTP authentication and, on successful auth, setup the JaaS Subject with the appropriate Principals. Your JAX-RS resources can check for the appropriate Principals on the Subject. Since you control the whole stack, you should be able to rely on an authenticated user in your resources (but do test this!), and you can enforce authorization based on the current operation in the resource code.

Solution 5:

I solved this problem by having the clients add the Authorization header and testing it in the REST methode like this:

@GET
@PRODUCES(MediaType.APPLICATION_JSON)
public String returnClients(@Context HTTPServletRequest request(
    String auth = request.getHeader("Authorization");
    Account acc = null;
    if (auth!=null) {
       Account acc = Utils.LoginAccount(auth);
    }
    if (acc == null)
     // not logged in, handle it gracefully

This way there is authentication without starting a session.