Jersey中RolesAllowedDynamicFeature未触发的配置问题咨询
项目相关代码
Jakarta Application类
@ApplicationPath("/app") @RolesAllowed("admin") public class Application extends ResourceConfig { public Application() { System.out.println("!!!!!! Insights starts !!!!!...."); packages("com.company.application.ws"); register(RolesAllowedDynamicFeature.class); register(SecurityFilter.class); register(AuthenticationExceptionMapper.class); } }
自定义授权过滤器SecurityFilter
@Provider @PreMatching public class SecurityFilter implements ContainerRequestFilter { @Inject jakarta.inject.Provider<UriInfo> uriInfo; // @Inject // private UserService userService; private static final String REALM = "My App"; @Override public void filter(ContainerRequestContext filterContext) throws IOException { User user = authenticate(filterContext); filterContext.setSecurityContext(new Authorizer(user)); } private User authenticate(ContainerRequestContext filterContext) { // 提取认证凭证 String authentication = filterContext.getHeaderString(HttpHeaders.AUTHORIZATION); if (authentication == null) { throw new AuthenticationException("Authentication credentials are required", REALM); } if (!authentication.startsWith("Basic ")) { return null; // 此处应添加额外校验逻辑 // "仅支持HTTP Basic认证" } authentication = authentication.substring("Basic ".length()); String[] values = new String(DatatypeConverter.parseBase64Binary(authentication), StandardCharsets.US_ASCII).split(":"); if (values.length < 2) { throw new WebApplicationException(400); // "用户名密码格式无效" } String username = values[0]; String password = values[1]; if ((username == null) || (password == null)) { throw new WebApplicationException(400); // "缺少用户名或密码" } // 校验提取的凭证 User user; if (username.equals("admin") && password.equals("1234")) { user = new User("admin", "admin"); System.out.println("admin AUTHENTICATED"); } else if (username.equals("user") && password.equals("123")) { user = new User("user", "user"); System.out.println("user AUTHENTICATED"); } else { System.out.println("USER NOT AUTHENTICATED"); throw new AuthenticationException("Invalid username or password", REALM); } return user; } public class Authorizer implements SecurityContext { private final User user; private final Principal principal; public Authorizer(final User user) { this.user = user; this.principal = () -> user.username; } @Override public Principal getUserPrincipal() { return this.principal; } @Override public boolean isUserInRole(String role) { return (role.equals(user.role)); } @Override public boolean isSecure() { return "https".equals(uriInfo.get().getRequestUri().getScheme()); } @Override public String getAuthenticationScheme() { return SecurityContext.BASIC_AUTH; } } public static class User { public String username; public String role; public User(String username, String role) { this.username = username; this.role = role; } } }
问题描述
RolesAllowedDynamicFeature从未被触发,已在Application类、资源类及方法级别添加@RolesAllowed注解,但均无效,isUserInRole方法从未被调用。
更新情况
复制本地版本的RolesAllowedDynamicFeature并注册后,RolesAllowedDynamicFeature.configure和RolesAllowedRequestFilter(String[] rolesAllowed)构造函数已触发,但RolesAllowedRequestFilter.filter和RolesAllowedRequestFilter.isAuthenticated仍未被调用。
解决方案建议
移除
@PreMatching注解:SecurityFilter使用的@PreMatching会让过滤器在请求匹配资源之前执行,而RolesAllowedRequestFilter是在资源匹配后才触发的,这会导致认证完成后授权逻辑无法正常触发。去掉该注解,让SecurityFilter在默认请求阶段执行。指定过滤器执行优先级:给
SecurityFilter添加@Priority(Priorities.AUTHENTICATION)注解,确保认证逻辑先于授权执行(RolesAllowedRequestFilter默认使用Priorities.AUTHORIZATION优先级)。校验资源类注解配置:确认资源类/方法上的
@RolesAllowed注解无冲突(比如没有@PermitAll注解覆盖),同时检查packages("com.company.application.ws")是否正确包含了目标资源类。检查SecurityContext设置:确保
SecurityFilter中设置SecurityContext的操作未被后续过滤器覆盖,且设置时机正确。
内容的提问来源于stack exchange,提问作者khateeb

