Jersey - 从过滤器中注入变量作为 RequestScoped

Jersey - Inject variable from filter as RequestScoped

我想在调用我的资源方法之前在过滤器中执行身份验证。在这个过滤器中,我还想检索用户的权限并通过 RequestScoped @Inject 注释传递它。

@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(...);
    }
}

资源:

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

我已经为注入注册了过滤器和工厂。

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!");
    }
}

我也绑定了

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

重要的是对象在收到请求时创建并且只能在该请求中访问。请求完成后,应调用 dispose 方法。我可以实现类似功能的唯一方法是通过 @Singleton 注释。但是,该对象不会在请求完成后被销毁,并且会在所有请求之间共享。

我已经在这个问题上投入了太多时间,是否有人知道如何获得理想的结果?

你的代码没有多大意义。一个地方你注入ISecurityHandler,另一个地方SecurityHandler,但是工厂是为ISecurityContext注入的。我将假设这些是拼写错误或复制粘贴错误。

除此之外,我会假设真的一切正常,因为你说过它作为一个单例工作。所以我猜你正面临 "Not inside a request scope" 错误。最简单的解决方法是使用 javax.inject.Provider 注入,这允许我们延迟检索对象。检索对象时,它将在请求范围内。

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

注意,你还应该确保用 @Priority(Priorities.AUTHENTICATION) 注释你 AuthenticationFilter 以便它出现在任何其他过滤器之前,即使你更喜欢它是 @PreMatching 过滤器。身份验证越早进入系统越好,我想说。

顺便说一句,您可能想看看泽西岛的 RolesAllowedDynamicFeature。它允许您将 jsr250 注释 @RolesAllowed@DenyAll@PermitAll 用于您的资源 类 和方法。

它基本上是在您的 Priorites.AUTHENTICATION 过滤器之后发生的过滤器,它从 ContainerRequestContext 中查找 javax.ws.rs.core.SecurityContext 以查找角色。您只需要在身份验证过滤器中创建 SecurityContext,以便下一个过滤器可以查找它。

你可以看一个例子。您可以在 isUserInRole 中检查用户权限。当设置 SecurityContext 时,Jersey 的过滤器将在之后调用,它会调用您的 isUserInRole。这样做,您就可以免费获得访问控制。