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

D00*_*0de 10 java security rest glassfish jax-rs

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

例如:

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

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

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

Pau*_*tha 24

您可以在a中执行此逻辑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; }   
    }
}
Run Code Online (Sandbox Code Playgroud)

有几点需要注意

  • 我创造了一个 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();
    }
}
Run Code Online (Sandbox Code Playgroud)

有几点需要注意:

  • 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);
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)