Java Wildfly中的仅承载身份验证,无需使用密钥斗篷
我想在Wildfly中实现我自己的仅承载身份验证。本质上,我将执行以下步骤:Java Wildfly中的仅承载身份验证,无需使用密钥斗篷,java,authentication,jax-rs,wildfly,bearer-token,Java,Authentication,Jax Rs,Wildfly,Bearer Token,我想在Wildfly中实现我自己的仅承载身份验证。本质上,我将执行以下步骤: 当我收到一个请求时,我会检查它是否有授权头 我获取令牌并对照数据库(在本例中,我将使用Redis)检查其有效性 我从数据库中获取该用户的角色 我希望能够在我的rest服务上使用@RolesAllowed注释 我该怎么做呢?我需要如何修改Wildfly配置文件?我需要实现哪些接口?如何将用户角色传递给安全上下文,以便Wildfly为我检查@RolesAllowed 如果回答,我是一个经验丰富的java程序员,但Wildf
@RolesAllowed
注释@RolesAllowed
<>如果回答,我是一个经验丰富的java程序员,但Wildfly新手,所以你可以跳过编程逻辑的细节,而不是跳转配置。同样,在您的回答中,不要担心令牌最初是如何到达Redis的,也不要担心客户是如何获得它的
编辑
这就是我所做的,但还没有运气。我已经实现了一个AuthenticationFilter
,它实现了ContainerRequestFilter
。(以下仅包括我已实现的主筛选函数。请注意,有些助手函数从数据库中获取未包含的角色)。即使在函数结束时,我使用用户配置文件(其中包含角色)设置了请求上下文的安全上下文,我也无法在我的JAX-RS rest服务上使用@RolesAllowed
注释。我该怎么做有什么建议吗
注意:我没有修改任何Wildfly配置文件或web.xml文件。我知道每个请求都会调用筛选器,因为我可以在每个请求中记录来自它的消息
/**
* (non-Javadoc)
* @see javax.ws.rs.container.ContainerRequestFilter#filter(javax.ws.rs.container.ContainerRequestContext)
*/
@Override
public void filter(ContainerRequestContext requestContext) {
//1. Read the JSON web token from the header
String authorizationHeader = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
if (authorizationHeader == null || !authorizationHeader.startsWith("Bearer ")) {
requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build());
return;
}
String token = authorizationHeader.substring("Bearer".length()).trim();
try{
//Note that if the token is not in the database,
//an exception will be thrown and we abort.
UserProfile userProfile = this.getUserProfile(token);
if (null == userProfile){
userProfile = this.decodeToken(token);
}
if (null == userProfile){
throw new Exception();
}
String role = userProfile.getUserRole();
if (null == role){
role = this.getRoleFromMod(userProfile);
if (null == role){
role = RoleType.READ_ONLY;
}
userProfile.setUserRole(role);
this.updateUserProfileForToken(token, userProfile);
}
userProfile.setUserRole(role);
//5. Create a security context class that implements the crazy interface
//and set it here.
requestContext.setSecurityContext(new ModSecurityContext(userProfile));
}
catch(Exception e){
requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build());
}
}
是的,我不确定它在EE环境中如何工作,甚至使资源类成为无状态bean。
@RolesAllowed
注释用于EJB。在这种情况下,主体将从servlet请求中检索(我相信)。我要做的就是实现您自己的授权过滤器,它在安全上下文中查找注释并检查主体
你可以看到。除了AnnotatedMethod
类之外,它没有什么特别之处。为此,您只需使用java.lang.reflect.Method
(resourceInfo.getResourceMethod())进行一些反射即可。除此之外,您几乎可以按原样复制代码。完成后,只需向应用程序注册RolesAllowedDynamicFeature
。或者只是用要扫描的@Provider
对其进行注释
您还需要确保使用
@Priority(Priorities.authentication)
注释身份验证筛选器,以便在使用@Priority(Priorities.authentication)
注释的授权筛选器之前调用它
更新 这里是我链接到的代码的重构,所以它不使用特定于Jersey的类。
AnnotatedMethod
刚刚更改为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
的当前实现
现在,它是为每个请求处理的过滤器。但是假设我们引入了一个定制的@认证的注释
@Target({METHOD, TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Authenticated{}
我们可以使用这个注释来注释不同的方法和类。为了使过滤器只过滤被注释的方法和类,我们可以引入一个检查注释的DynamicFeature
,然后只在找到注释时注册过滤器。比如说
@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());
}
}
}
@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;
}
}
一旦我们注册了这个AuthenticationDynamicFeature
类,它将使它成为只过滤带有@Authenticated
注释的方法和类
或者,这甚至可以在过滤器内完成。我们可以从AuthenticationFilter
中获取对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
这样我们就可以完全忘记DynamicFeature
。最好只使用DynamicFeature
,我只是举了一个示例进行演示
但是也就是说,如果我们使用RolesAllowedDynamicFeature
查看第一个代码块,您可以更好地了解发生了什么。它只为带有@RolesAllowed
和@DenyAll
注释的方法和类注册筛选器。您甚至可以重构它,使所有注释逻辑都位于过滤器中,而不是特性中。你只有过滤器。就像我对上面的AuthenticationFilter
示例所做的那样。同样,这只是为了举例
现在,就注册DynamicFeature
而言,其工作方式与注册任何其他资源类或提供程序类(例如,您的身份验证过滤器)相同。因此,无论您如何注册,只需以相同的方式注册RolesAllowedDynamicFeature
。还有扫描,其中扫描@Path
和@Provider
注释。如果这是您当前正在使用的,则仅使用@Provider
注释要素类就应该注册它。例如,仅仅拥有一个空的应用程序
子类就会导致扫描发生
@ApplicationPath("/api")
public class RestApplication extends Application {}
然后在应用程序
子类中显式注册。比如说
@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());
}
}
}
@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;
}
}
@ApplicationPath(“/api”)
公共类重新启动应用程序扩展了应用程序{
@凌驾
public Set>classes=new HashSet();
类。添加(AuthenticationFilte)