【问题标题】:Custom JAX-RS authorization - using JWT in each request自定义 JAX-RS 授权 - 在每个请求中使用 JWT
【发布时间】:2015-06-28 06:54:00
【问题描述】:

我有一个 JAX-RS 服务,我希望我的所有用户都可以访问我的服务,但只有那些有权查看结果的用户。基于角色的安全性以及现有的 REALMS 和身份验证方法不符合我的要求。

例如:

  1. 用户针对一项 REST 服务进行身份验证,我向他发送带有他的 ID 的 JWT 令牌
  2. 用户请求其他资源并在每个请求中发送他的 JWT 和他的 ID
  3. 我检查他的用户 ID(来自 JWT),如果业务逻辑返回结果,我将其发回,否则我发送空结果集或特定 HTTP 状态

问题是:我应该在哪里检查用户 ID,在一些单独的过滤器、安全上下文或每个 REST 方法实现中? REST方法如何提供这个ID,是否可以通过ID过滤请求后在每个方法中注入securityContext?

我正在使用 GlassFish 4.1 和 Jersey JAX-RS 实现。

【问题讨论】:

    标签: java security rest glassfish jax-rs


    【解决方案1】:

    您可以在ContainerRequestFilter 中执行此逻辑。在这里处理自定义安全功能很常见。

    需要考虑的一些事情

    1. 该类应使用@Priority(Priorities.AUTHENTICATION) 进行注释,以便在其他过滤器(如果有)之前执行。

    2. 您应该在过滤器中使用SecurityContext。我所做的是实现SecurityContext。您可以根据需要真正实现它。

    这是一个没有任何安全逻辑的简单示例

    @Provider
    @Priority(Priorities.AUTHENTICATION)
    public class SecurityFilter implements ContainerRequestFilter {
    
        @Override
        public void filter(ContainerRequestContext requestContext) throws IOException {
            SecurityContext originalContext = requestContext.getSecurityContext();
            Set<String> roles = new HashSet<>();
            roles.add("ADMIN");
            Authorizer authorizer = new Authorizer(roles, "admin", 
                                                   originalContext.isSecure());
            requestContext.setSecurityContext(authorizer);
        }
    
        public static class Authorizer implements SecurityContext {
    
            Set<String> roles;
            String username;
            boolean isSecure;
            public Authorizer(Set<String> roles, final String username, 
                                                 boolean isSecure) {
                this.roles = roles;
                this.username = username;
                this.isSecure = isSecure;
            }
    
            @Override
            public Principal getUserPrincipal() {
                return new User(username);
            }
    
            @Override
            public boolean isUserInRole(String role) {
                return roles.contains(role);
            }
    
            @Override
            public boolean isSecure() {
                return isSecure;
            }
    
            @Override
            public String getAuthenticationScheme() {
                return "Your Scheme";
            } 
        } 
    
        public static class User implements Principal {
            String name;
    
            public User(String name) {
                this.name = name;
            }
    
            @Override
            public String getName() { return name; }   
        }
    }
    

    注意事项

    • 我创建了一个SecurityContext
    • 我添加了一些角色,并将它们用于isUserInRole 方法。这将用于授权。
    • 我创建了一个自定义的User 类,它实现了java.security.Principal。我返回了这个自定义对象
    • 最后我在ContainerRequestContext中设置了新的SecurityContext

    现在呢?我们来看一个简单的资源类

    @Path("secure")
    public class SecuredResource {
        @GET
        @RolesAllowed({"ADMIN"})
        public String getUsername(@Context SecurityContext securityContext) {
            User user = (User)securityContext.getUserPrincipal();
            return user.getName();
        }
    }
    

    需要注意的几点:

    • SecurityContext 被注入到方法中。
    • 我们得到Principal 并将其转换为User。所以实际上你可以创建任何实现Principal 的类,然后随意使用这个对象。
    • @RolesAllowed 注解的使用。对于 Jersey,有一个过滤器通过传入 @RolesAllowed 注释中的每个值来检查 SecurityContext.isUserInRole,以查看是否允许用户访问资源。

      要在 Jersey 中启用此功能,我们需要注册 RolesAllowedDynamicFeature

      @ApplicationPath("/api")
      public class AppConfig extends ResourceConfig {
      
          public AppConfig() {
              packages("packages.to.scan");
              register(RolesAllowedDynamicFeature.class);
          }
      }
      

    【讨论】:

    • 感谢您的回复。我以为我可以使用 ContainerRequestFilter,但我不需要 Roles 和 SecurityContext 来满足我的要求。仅使用 servlet 过滤器是否过于简单,并且对于对我的 REST 资源的每个请求,我都会对先前发送的 JWT 标头进行解组,从中提取用户信息并将其传递给服务方法以确定每个方法内的用户授权级别?这似乎是一个简单实用的解决方案,应该可以工作..
    • 几乎可以在 servlet 过滤器中执行的操作,可以在 ContainerRequestFilter 中执行。但是对于后者,您可以访问 Jersey 应用程序
    • 谢谢,我已经通过将 JWT 数据注入到用户主体来实现您的解决方案,然后在我拥有的每个 REST 方法中从注入的 SecurityContext 中获取它。先生,请让我赞成并接受您的帮助。
    • 如何在 Wildfly 10 中实现?
    • 你如何创建“我的方案”,是那组角色名称吗?
    【解决方案2】:

    我正在寻找一个独立于泽西岛并适用于 Wildfly 的解决方案 -> 找到了这个 github 示例实现:

    https://github.com/sixturtle/examples/tree/master/jaxrs-jwt-filter

    它应该会提示您如何干净地解决它。

    实现一个 JWTRequestFilter,它实现了 ContainerRequestFilter https://github.com/sixturtle/examples/blob/master/jaxrs-jwt-filter/src/main/java/com/sixturtle/jwt/JWTRequestFilter.java

    如上所述并在 web.xml 中将过滤器注册为 resteasy 提供程序:

    <context-param>
           <description>Custom JAX-RS Providers</description>
           <param-name>resteasy.providers</param-name>
           <param-value>com.sixturtle.jwt.JWTRequestFilter</param-value>
    </context-param>
    <context-param>
            <param-name>resteasy.role.based.security</param-name>
            <param-value>true</param-value>
    </context-param>
    

    【讨论】:

      猜你喜欢
      • 2011-07-04
      • 1970-01-01
      • 1970-01-01
      • 2018-12-15
      • 1970-01-01
      • 2022-06-14
      • 1970-01-01
      • 2023-03-11
      • 2015-01-22
      相关资源
      最近更新 更多