jersey security and session management

Viewed 88804

Is there a way to get session management or security programatically in Jersey, e.g. web-application session management? Or are transactions, sessions, and security all handled by the container in which the Jersey application is deployed?

7 Answers

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();


    }
}

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.

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.

Related