是的,我不确定它在EE环境中如何工作,甚至将资源类变成无状态bean。这个
@RolesAllowed
注释用于ejb。在这种情况下,将从servlet请求中检索主体(我相信)。我要做的只是实现您自己的授权过滤器,该过滤器查找注释并在安全上下文中检查主体。
你可以看到
how Jersey implements it
。除了
AnnotatedMethod
班为此,你可以用
java.lang.reflect.Method
(resourceInfo.getResourceMethod())。除此之外,您几乎可以原样复制代码。完成后,只需注册
RolesAllowedDynamicFeature
使用应用程序。或者用
@Provider
以进行扫描。
您还需要确保您的身份验证过滤器带有注释
@Priority(Priorities.AUTHENTICATION)
以便在授权过滤器之前调用它
@Priority(Priorities.AUTHORIZATION)
.
更新
这里是我链接到的代码的重构,所以它不使用Jersey特定的类。这个
注释的方法
刚更改为
Method
.
@Provider
public class RolesAllowedFeature implements DynamicFeature {
@Override
public void configure(ResourceInfo resourceInfo, FeatureContext configuration) {
Method resourceMethod = resourceInfo.getResourceMethod();
if (resourceMethod.isAnnotationPresent(DenyAll.class)) {
configuration.register(new RolesAllowedRequestFilter());
return;
}
RolesAllowed ra = resourceMethod.getAnnotation(RolesAllowed.class);
if (ra != null) {
configuration.register(new RolesAllowedRequestFilter(ra.value()));
return;
}
if (resourceMethod.isAnnotationPresent(PermitAll.class)) {
return;
}
ra = resourceInfo.getResourceClass().getAnnotation(RolesAllowed.class);
if (ra != null) {
configuration.register(new RolesAllowedRequestFilter(ra.value()));
}
}
@Priority(Priorities.AUTHORIZATION) // authorization filter - should go after any authentication filters
private static class RolesAllowedRequestFilter implements ContainerRequestFilter {
private final boolean denyAll;
private final String[] rolesAllowed;
RolesAllowedRequestFilter() {
this.denyAll = true;
this.rolesAllowed = null;
}
RolesAllowedRequestFilter(final String[] rolesAllowed) {
this.denyAll = false;
this.rolesAllowed = (rolesAllowed != null) ? rolesAllowed : new String[]{};
}
@Override
public void filter(final ContainerRequestContext requestContext) throws IOException {
if (!denyAll) {
if (rolesAllowed.length > 0 && !isAuthenticated(requestContext)) {
throw new ForbiddenException("Not Authorized");
}
for (final String role : rolesAllowed) {
if (requestContext.getSecurityContext().isUserInRole(role)) {
return;
}
}
}
throw new ForbiddenException("Not Authorized");
}
private static boolean isAuthenticated(final ContainerRequestContext requestContext) {
return requestContext.getSecurityContext().getUserPrincipal() != null;
}
}
}
首先让我解释一下
DynamicFeature
作品为此,我们首先将讨论的上下文更改为您当前的
AuthenticationFilter
.
现在,它是为每个请求处理的过滤器。但假设我们引入了一种习俗
@Authenticated
注释
@Target({METHOD, TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Authenticated{}
我们可以使用这个注释来注释不同的方法和类。为了使过滤器仅过滤带注释的方法和类,我们可以引入
动态功能
它检查注释,然后仅在找到注释时注册过滤器。例如
@Provider
public class AuthenticationDynamicFeature implements DynamicFeature {
@Override
public void configure(ResourceInfo resourceInfo, FeatureContext configuration) {
if (resourceInfo.getResourceMethod().isAnnotationPresent(Authenticated.class)) {
configuration.register(new AuthenticationFilter());
return;
}
if (resourceInfo.getResourceClass().isAnnotationPresent(Authenticated.class)) {
configuration.register(new AuthenticationFilter());
}
}
}
一旦我们注册了这个
AuthenticationDynamicFeature
类,它将使其仅使用
@已验证
将被过滤。
或者,甚至可以这样做
在内部
过滤器。我们可以参考
ResourceInfo
来自
身份验证筛选器
。例如,检查注释,如果不存在,则继续。
@Provider
@Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {
@Context
private ResourceInfo resourceInfo;
@Override
public void filter(ContainerRequestContext context) throws IOException {
boolean hasAnnotation = false;
if (resourceInfo.getResourceMethod().isAnnotationPresent(Authenticated.class)
|| resourceInfo.getResourceClass().isAnnotationPresent(Authenticated.class)) {
hasAnnotation = true;
}
if (!hasAnnotation) return;
// process authentication is annotation is present
这样我们就可以完全忘记
动态功能
。最好只使用
动态功能
,我只是举一个例子来演示。
尽管如此,如果我们用
角色允许的动态功能
,您可以更好地了解发生了什么。它只为带注释的方法和类注册筛选器
@允许的角色
和
@DenyAll
。您甚至可以对其进行重构,使其在过滤器中包含所有注释逻辑,而不是功能。你只有过滤器。就像我对
身份验证筛选器
上面的示例。同样,这只是出于示例目的。
现在,就
动态功能
,它的工作方式与注册任何其他资源类或提供程序类(例如身份验证筛选器)相同。因此,无论您如何注册这些,只需注册
角色允许的动态功能
同样的方式。有扫描,其中
@Path
和
@提供商
扫描批注。如果这是您当前使用的,则只需使用
@提供商
应将其注册。例如,只有一个空
Application
子类将导致扫描发生
@ApplicationPath("/api")
public class RestApplication extends Application {}
然后在您的
应用
子类。例如
@ApplicationPath("/api")
public class RestApplication extends Application {
@Override
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<>();
classes.add(AuthenticationFilter.class);
classes.add(RolesAllowedFeature.class);
classes.add(SomeResource.class);
return classes;
}
}
请注意,在执行此操作时,您将禁用任何正在进行的扫描。
因此,在上述情况清楚之后,需要做一些其他事情来确保它仍然不起作用。
-
确保您的当前
身份验证筛选器
注释为
@优先级(优先级.认证)
。这是为了确保在授权筛选器之前调用身份验证筛选器。这需要发生,因为身份验证过滤器设置安全上下文,而授权过滤器检查它。
-
确保正确创建了安全上下文。授权筛选器将调用
SecurityContext.isUserInRole(role)
从
@允许的角色
注释。因此,您需要确保实现
isUserInRole
正确地