我想验证发出请求的用户。为此,我有两个过滤器: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)。
答案 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();