Authentication/authorization in JAX-RS using interceptors and injection

夙愿已清 提交于 2019-11-29 02:24:43
lefloh

In my eyes this approach is valid as long as you don't try to build something like a session with this User Object.

As answered here you could use @Context and @Provider but that's not exactly what you want. Directly injecting a Class per @Context is possible with the Resteasy Dispatcher. But here you must register the Object which should be injected. I don't think that makes sense for request-scoped parameters. What you could do is inject a provider like this:

// Constructor of your JAX-RS Application
public RestApplication(@Context Dispatcher dispatcher) {
    dispatcher.getDefaultContextObjects().put(UserProvider.class, new UserProvider());
}

// a resource
public Response getById(@Context UserProvider userProvider) {
    User user = userProvider.get();
}

Other ways to solve your problem:

  1. Register a WebFilter, authenticate the user, wrap the ServletRequest and override getUserPrincipal. You can then access the UserPrincipal from a injected HttpServletRequest.
  2. Implement a JAX-RS Interceptor which implements ContainerRequestFilter. Use ContainerRequestContext.html#setSecurityContext with a UserPrincipal and inject the SecurityContext as ResourceMethod-Parameter.
  3. Implement a CDI-Interceptor which updates your Method-Parameters.
  4. Implement a class which produces your user and inject it via CDI.

I pushed examples to github.

While lefloh's answer is definitely correct I'd like to elaborate on his 2nd approach which I find most convenient.

You're to create Interceptor(Filter) which implements ContainerRequestFilter.

import org.apache.commons.lang.StringUtils;
import org.jboss.resteasy.spi.ResteasyProviderFactory;

import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;
import java.io.IOException;

@Provider
public class UserProvider implements ContainerRequestFilter {

    @Override
    public void filter(ContainerRequestContext containerRequestContext) throws IOException {
        String userId = containerRequestContext.getHeaders().getFirst("User-Id");
        if (StringUtils.isEmpty(userId)) {
            Response response = Response
                    .status(Response.Status.BAD_REQUEST)
                    .type(MediaType.TEXT_PLAIN_TYPE)
                    .entity("User-Id header is missing.")
                    .build();
            containerRequestContext.abortWith(response);
            return;
        }

        //do your logic to obtain the User object by userId

        ResteasyProviderFactory.pushContext(User.class, user);
    }
}

You'd need to register it either by autodiscovering set up in web.xml

<context-param>
    <param-name>resteasy.scan</param-name>
    <param-value>true</param-value>
</context-param>

or in your app boot

ResteasyProviderFactory.getInstance().registerProvider(UserProvider.class);

Since all the logic behind fetching the user is in the UserProvider you can for example use it like this

@Path("/orders")
@GET
public List<Order> getOrders(@Context User user) {
    return user.getOrders();
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!