jersey security and session management
Asked Answered
P

7

42

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?

Pricking answered 26/5, 2009 at 6:29 Comment(1)
I've just started investigating this as well. I'll reply back if I find anythingStem
S
68

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


    }
}
Stem answered 28/5, 2009 at 17:6 Comment(4)
Thanks Jack, I needed this because we have to implement some sort of access control on JAX-RS web services.. any help on that will also be higly appreciated.. Thanks in advance, AdhirPricking
@Jack Cox: I have posted a related question here: #9677088 Maybe you would know how to do this on the client side (with jersey-client) as well?Jesher
@Jack Cox, if I have an applicaton with 1000 users logged into it at the same time and each set session.setAttribute("username",username). Does Java understand that there are 1000 different sessions each with a variable named username with a value different from the others?Zebe
Kasavbere, yes, Java, more specifically the Java servlet spec, dictates that attribute (e.g. username) is stored separately for each session.Stem
P
23

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

Passport answered 13/10, 2011 at 9:45 Comment(2)
That's not always true. If session is used as a means of caching responses then one might argue that it's used to improve performance. You can still write a stateless application, just with a session as the caching mechanism.Cottingham
websites such as facebook use sessions on top of webservices to run webapps. It's not inherently wrong. You also might want to have a user authenticated to use a webservice in which case you wouldn't want to pass credentials every time.Hudibrastic
Z
16

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.

Zellner answered 5/11, 2013 at 12:34 Comment(0)
P
6

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.

Pederson answered 13/6, 2009 at 5:31 Comment(1)
+1 Sounds like a good idea. How exactly could you get it to work on Grizzly? I opened a new question. #1682561Banc
P
4

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.

Punnet answered 7/11, 2011 at 16:11 Comment(1)
NB: This is totally insecure unless you are forcing HTTPS connections, in which case you might as well use dropwizard-auth.Avelin
M
3

For Jersey security you should take a look on jersey OAuth support. OAuth perfectly fits when you expose API for your system to external users. For example like the linkedin api

http://wikis.oracle.com/display/Jersey/OAuth

Marylnmarylou answered 7/5, 2010 at 9:22 Comment(1)
Please fix the linkTattered
S
2

You can user @path to group the services under single name space. example .

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

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


        return "";


    }
}
Instead of @Path("/helloworld") use
@Path("admin/helloworld") to expose you class as rest and bind filter on "admin/"
in web.xml as below.

<servlet>
            <servlet-name>jersey-serlvet</servlet-name>
            <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
            <init-param>
                <param-name>com.sun.jersey.config.property.packages</param-name>
                <param-value>/</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet-mapping>
            <servlet-name>jersey-serlvet</servlet-name>
            <url-pattern>/rest/*</url-pattern>
        </servlet-mapping>
         <filter>
            <filter-name>myfilter</filter-name>
            <filter-class>com.Filterclass</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>myfilter</filter-name>
            <url-pattern>/rest/admin/*</url-pattern>
        </filter-mapping> 

    public class Filterclass implements Filter {
       public void doFilter(ServletRequest request, ServletResponse response,
                FilterChain chain)
                throws IOException, ServletException {
                  try{
                       chain.doFilter(request, response);
                    }catch(Exception e){
                   e.printStackTrace();
                       }
          }
    }

You can validate you session in this filter class.

Spinet answered 17/9, 2014 at 8:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.