2

I want to perform authentication in a filter before my resource method is called. Within this filter I would also like to retrieve the permissions of a user and pass it on through a RequestScoped @Inject annotation.

@Authenticated
public class AuthenticationFilter implements ContainerRequestFilter {

    @NameBinding
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Authenticated {};

    @Inject
    private ISecurityHandler handler;

    public AuthenticationFilter() {}

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

        // Filter out unauthorized

        // Retrieve user permissions
        this.handler.setUserPermissions(...);
    }
}

Resource:

@Path("my-path")
public class GetVisitorsDataResource {

    @Inject private ISecurityHandler handler;

    @GET
    @Path("resource-method")
    @Authenticated
    @Produces(MediaType.APPLICATION_JSON)
    public Response resource() {

        System.out.println(handler.getUserPermissions());

        return Response.ok().build();
    }
}

I have registered the filter and a Factory for the injection.

public static class SecurityHandlerProvider implements Factory<ISecurityHandler> {

    @Override
    public ISecurityHandler provide() {
        System.out.println("PROVIDING SECURITY CONTEXT!");
        return new SecurityHandlerImpl();
    }

    @Override
    public void dispose(ISecurityHandler instance) {
        System.out.println("DISPOSING SECURITY CONTEXT!");
    }
}

I have also bound it.

bindFactory(SecurityHandlerProvider.class).to(ISecurityHandler.class).in(RequestScoped.class);

It is important that the object is created when a request is received and only accessible within that request. When the request is finished, the dispose method should be called. The only way I can achieve something similar is through the @Singleton annotation. However, the object is not destroyed after the request is completed and is shared across all requests.

I have been investing too much time into this issue already, is there perhaps anybody that knows how to achieve the preferred result?

Felix Novovic
  • 575
  • 11
  • 21

1 Answers1

3

Your code doesn't really make much sense. One place you are injecting ISecurityHandler, and another place SecurityHandler, but the factory is for ISecurityContext. I will just assume those are typos or copy and paste errors.

Aside from that I'll assume that really all is ok, since you you said it works as a singleton. So I'm guessing you are facing the "Not inside a request scope" error. The easiest fix for that is to just inject using javax.inject.Provider, which allows us to lazily retrieve the object. When the object is retrieve, it will be withing a request scope.

@Inject
private javax.inject.Provider<ISecurityContext> securityContextProvider;

@Override
public void filter(ContainerRequestContext context) throws IOException {
    ISecurityContext sc = securityContextProvider.get();
}

...
bindFactory(SecurityHandlerProvider.class)
          .to(ISecurityContext.class)
          .in(RequestScoped.class);

NB, you should also make sure to annotate you AuthenticationFilter with @Priority(Priorities.AUTHENTICATION) so that it occurs before any other filter even you my prefer it to be a @PreMatching filter. The earlier into the system the authentication happens, the better, I'd say.

As an aside, you may want to look into Jersey's RolesAllowedDynamicFeature. It allows you to use the jsr250 annotations @RolesAllowed, @DenyAll, and @PermitAll for your resource classes and methods.

It is basically a filter that occurs after your Priorites.AUTHENTICATION filter, and it looks up the javax.ws.rs.core.SecurityContext from the ContainerRequestContext to look up roles. You just need to create the SecurityContext inside your authentication filter, so the next filter can look it up.

You can see an example here. You can check the user permission in the isUserInRole. When the set the SecurityContext, Jersey's filter will be called afterwards, and it calls your isUserInRole. Doing it this way, you get access control for free.

Community
  • 1
  • 1
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720