我已经为此工作了一段时间,最终得到了我自己的解决方案。它是在JAX-RS
接口级别实现的(如Pradeep Pati所建议的)——因此,如果您需要通过 访问您的 bean EJB
,它将无法工作。
所以,正如我在这里找到的,ContainerRequestFilter
可以访问资源方法(或类)注释,所以我需要做的就是:
1.实现我自己的@RolesAllowed
注解:
@Inherited
@Target( {ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RolesAllowed {
String[] value();
}
2.ContainerRequestFilter
使用自定义身份验证和授权实现:
@Provider
@Priority(Priorities.AUTHENTICATION)
public class SecurityFilter implements ContainerRequestFilter {
@Context
private ResourceInfo resourceInfo;
@Override
public void filter(ContainerRequestContext context) throws IOException {
// here we have access to headers:
String authorizationHeader = context.getHeaderString("Authorization");
// and, thanks to injected resourceInfo, to annotations:
RolesAllowed annotation = resourceInfo
.getResourceClass() // or getResourceMethod(), I've used both
.getAnnotation(RolesAllowed.class);
// and, finally, to the roles (after a null-check)
String[] roles = annotation.value();
// then you can authenticate and authorize everything on your own using any method (I’ve used Basic Auth and JWT)
// and, if something fails, you can abort the request:
if (!isAuthenticated) {
context.abortWith(Response.status(Response.Status.UNAUTHORIZED).build());
} else if (!isAuthorized) {
context.abortWith(Response.status(Response.Status.FORBIDDEN).build());
}
}
...
}
但是,我也评估了Avishai的解决方案(使用PicketLink)。虽然这有点难以实现,有时可能很复杂(例如,基本的 JPA 场景需要大约 7-8 个 JPA 实体),但如果您需要具有很多选项的可靠、可扩展的安全系统(如 LDAP 或JPA,甚至两者同时使用)或使用各种身份验证选项(例如同时使用 Basic 和 JWT 身份验证,但具有不同的标头)。这个话题可能有数百个优点和/或缺点,所以这不是一个容易的选择。
有趣的是,PicketLink 使用它自己的@org.picketlink.authorization.annotations.RolesAllowed
注释而不是javax.annotation
一个。尽管如此,它应该可以很好地处理EJB
调用,因为它使用EJB
拦截器而不是JAX-RS
过滤器来检查角色。
但对我来说,这似乎有点过头了,所以我想出了自己的、不那么复杂(但有效)的解决方案。