在JAX-RS请求过滤器之间共享数据的最佳方法

时间:2018-06-28 14:23:32

标签: java-ee jax-rs cdi

我想验证发出请求的用户。为此,我有两个过滤器:AuthenticationFilter和AuthorizationFilter。 AuthenticationFilter从请求中提取令牌,并从数据库中查找用户。 AuthorizationFilter检查该用户(由上一个过滤器检索)是否具有必要的权限。我有两种可能的解决方案,并且想知道每种解决方案的优缺点,我应该使用哪种解决方案。我还需要在实际的业务逻辑中访问用户。我的代码如下:

AuthenticationFilter:

@Secured
@Provider
@Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {

    // option 1.1
    @Inject
    @AuthenticatedUser
    private Event<User> authenticatedUserEvent;

    // option 1.2
    @Inject
    @AuthenticatedUser
    private User authenticatedUser;

    public void filter(ContainerRequestContext requestContext) throws IOException {
        String token = getToken(requestContext);
        User user = getUser(token)
        if (user == null) {
            requestContext.abortWith(...);
        } else {
            // option 1.1
            authenticatedUserEvent.fire(User);

            // option 1.2
            authenticatedUser.setData(user);

            // option 2
            requestContext.setProperty("authenticatedUser", user);
        }
    }
}

AuthorizationFilter:

@Secured
@Provider
@Priority(Priorities.AUTHORIZATION)
public class AuthorizationFilter implements ContainerRequestFilter {

    @Context
    private ResourceInfo resourceInfo;

    // option 1
    @Inject
    @AuthenticatedUser
    private User authenticatedUser;

    public void filter(ContainerRequestContext requestContext) throws IOException {

        // option 2
        User authenticatedUser = (User) requestContext.getProperty("authenticatedUser")

        boolean allowed = verifyRoles(user, resourceInfo.getResourceClass(), resourceInfo.getResourceMethod());
        if (!allowed) {
            requestContext.abortWith(...);
        }
    }
}

AuthenticatedUserProducer(仅用于选项1):

@RequestScoped
public class AuthenticatedUserProducer {

    @Produces
    @RequestScoped
    @AuthenticatedUser
    private User authenticatedUser;

    public void handleAuthenticationEvent(@Observes @AuthenticatedUser User user) {
        this.authenticatedUser = user
    }
}

对于选项1,是否需要用@RequestScoped注释过滤器?默认情况下,过滤器是应用程序作用域的,那么注入安全吗?也就是说,如果同时处理多个请求,请求链1中的过滤器1触发的事件将不会最终注入链2的过滤器2中?和将用户注入运行实际业务逻辑的资源类中一样吗?

对于选项2,我不再有权访问ContainerRequestContext,但是可以通过注入HttpServletRequest来访问对象。但是在我看来,此选项“不太干净”,因为在使用存储对象之前,我必须将存储的对象强制转换为User,当使用注入方法时,可以直接使用对象。

我已经检查了这些问题,我正在寻找的是确定哪个选项是最好的:

我当前使用的是WildFly 11,它的默认实现是JAX-RS(Resteasy)和CDI(Weld)。

1 个答案:

答案 0 :(得分:2)

执行身份验证/授权后,可以使用SecurityContext,它是JAX-RS API的一部分。查看如何在身份验证过滤器中使用它的示例:

final SecurityContext currentSecurityContext = requestContext.getSecurityContext();
requestContext.setSecurityContext(new SecurityContext() {

        @Override
        public Principal getUserPrincipal() {
            // Return a Principal instance according to your needs
            return () -> username;
        }

    @Override
    public boolean isUserInRole(String role) {
        return true;
    }

    @Override
    public boolean isSecure() {
        return currentSecurityContext.isSecure();
    }

    @Override
    public String getAuthenticationScheme() {
        // Return the authentication scheme used by your application
        return SecurityContext.BASIC_AUTH;
    }
});

然后使用@Context批注将SecurityContext注入您的JAX-RS资源和提供程序中:

@Context
SecurityContext securityContext;

然后,您将获得在Principal中设置的SecurityContext实例:

Principal principal = securityContext.getUserPrincipal();