小编典典

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

java

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

例如:

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

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

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


阅读 332

收藏
2020-09-18

共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。我返回了这个自定义对象
  • 最后,我SecurityContextContainerRequestContext

怎么办?让我们看一个简单的资源类

@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);
    }
}
2020-09-18