自定义JAX-RS授权 - 在每个请求中使用JWT

时间:2015-04-21 08:22:17

标签: java security rest glassfish jax-rs

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

例如:

  1. 用户对一个REST服务进行身份验证,然后向他发送带有ID
  2. 的JWT令牌
  3. 用户要求提供其他资源,并在每个请求中向他的JWT发送他的ID
  4. 我检查了他的用户ID(来自JWT),如果业务逻辑返回结果我发回去,否则我发送空结果集或特定HTTP状态
  5. 问题是:我应该在哪里检查用户ID,在某个单独的过滤器,安全上下文或每个REST方法实现中?如何使用此ID提供REST方法,可以在按ID过滤请求后在每个方法中注入securityContext吗?

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

2 个答案:

答案 0 :(得分:24)

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

需要考虑的一些事项

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

  2. 您应该使用过滤器内的SecurityContext。我所做的是实现SecurityContext。无论如何你都可以真正实现它。

  3. 这是一个没有任何安全逻辑

    的简单示例
    @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。我退回了这个自定义对象
    • 最后,我在SecurityContext
    • 中设置了新的ContainerRequestContext

    现在怎样?让我们看一个简单的资源类

    @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,有一个过滤器通过传入SecurityContext.isUserInRole注释中的每个值来检查@RolesAllowed,以查看是否允许用户访问该资源。

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

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

答案 1 :(得分:1)

我正在寻找一种独立于泽西岛的解决方案,适用于Wildfly - &gt;发现了这个github示例实现:

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

它应该给你一个如何解决它的提示。

实现一个实现ContainerRequestFilter的JWTRequestFilter 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>