简体   繁体   中英

jax-rs 1.1 with security filter and dependency injection, how to achieve this?

For example I have the following JAX-RS 1.1 method which receives JWT token , check it and then process or deny request like following:

@GET
public Response getBook(@HeaderParam("Authorization") String authorizationToken, @QueryParam("id") String bookId) {

    //if(authorizationToken is a valid JWT token)
    //      get User object which represented by this token
    //      get required book from database
    //      return 200 code with a book within response
    //else if(authorizationToken is invalid by whatever reason it's)
    //      return 401 code within a response
    //else if(database error)
    //      return 500 code within a response

}

As you can see in every jax-rs method I need to use same lines of code: check token , transform it to User object , return 401 error if it isn't valid .

Actually I can optimise it by extracting it in a static method which will perform this check and return User object upon success or throw an Exception if something went wrong. Also I can create webfilter which will check header and validate token before it reached jax-rs method and will abort it with 401 exception if it isn't valid.

But I'd like to achieve all together. Webfilter which will validate JWT token and if it's valid convert it to User object and inject it in jax-rs method as following:

@GET
public Response getBook(@RequestByUser User user, @QueryParam("id") String bookId) {

    //if(get required book from database was successfull)
    //      return 200 code with a book within response
    //else(database error)
    //      return 500 code within a response
}

So if I reached that point I can be sure that User is valid and I don't need to care about it. Is it possible to achieve this with JAX-RS 1.1 ?

I'm not aware of a way to do exactly what you want here but it wouldn't be too hard to implement something with some thread local data . Basically, the filter would store the ThreadLocal data with the User information and the service methods would be able to get it. It's a bit of a hack but it would let you do what you want.

Be careful though - thread local data lives along with the thread - try not to store a giant object in there or you may run into memory issues if you have a ton of clients.

EDIT

After thinking about this a bit, why not just use the session? ThreadLocal assumes that the filter and the service run on the same thread. This is likely to be true but not guaranteed.

So in your filter:

JWTFilter.java

@WebFilter(urlPatterns={"/*"}
public class JWTFilter implements Filter {
        public void doFilter(ServletRequest request,
                             ServletResponse response,
                             FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;

        String jwt = req.getHeader("Authorization");
        User user = getUserFromJWT(jwt);  // you'll have to code this
        if(user != null) {
            req.getSession().setAttribute("user", user);
            chain.doFilter(request, response);
        }
        else {
            HttpServletResponse resp = (HttpServletResponse)response;
            resp.sendError(HttpServletResponse.SC_FORBIDDEN);
        }
    }
}

YourService.java

@GET
public Response getBook(@QueryParam("id") String bookId,
                        @Context HttpServletRequest request) {
    User user = (User)request.getSession().getAttribute("user");    
}

Notice the "cheat" here - we're injecting the HttpServletRequest so that we can get the session and, with it, the user. But because of the filter this method will not be called if the JWT validation failed.

This code does not take advantage of anything outside of the JAX-RS and/or Servlet spec and should be app server agnostic.

you can use filter and/or interceptor for this purpose. Here is the Jersey example.

public class AuthorizationRequestFilter implements ContainerRequestFilter {

    @Override
    public void filter(ContainerRequestContext requestContext)
                    throws IOException {

        final SecurityContext securityContext =
                    requestContext.getSecurityContext();
        if (securityContext == null ||
                    !securityContext.isUserInRole("privileged")) {

                requestContext.abortWith(Response
                    .status(Response.Status.UNAUTHORIZED)
                    .entity("User cannot access the resource.")
                    .build());
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM