自定义JAX-RS授权 - 在每个请求中使用JWT [英] Custom JAX-RS authorization - using JWT in each request

查看:187
本文介绍了自定义JAX-RS授权 - 在每个请求中使用JWT的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个JAX-RS服务,我希望所有用户都能访问我的服务,但只有那些有权查看结果的人。



例如:

    $
  1. 用户针对一个REST服务进行身份验证,并向他发送JWT令牌和他的ID

  2. 用户请求其他资源,并在每个请求中发送他的JWT及其ID。
  3. >
  4. 我检查他的用户标识(来自JWT),如果业务逻辑返回结果,我将它们发送回去,否则我发送空结果集或特定的HTTP状态

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



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

解决方案

您可以在 ContainerRequestFilter 。在这里处理自定义安全功能很常见。



需要考虑的一些事项


  1. @Priority(Priorities.AUTHENTICATION) ,因此它会在其他过滤器之前执行(如果有的话)。

  2. http://docs.oracle.com/javaee/7/api/javax/ws/rs/core/SecurityContext.htmlrel =nofollow noreferrer> SecurityContext ,在过滤器内部。我所做的是实现 SecurityContext 。无论如何,你可以真正实现它。


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



    pre> @Provider
    @Priority(Priorities.AUTHENTICATION)
    public class SecurityFilter implements ContainerRequestFilter {

    @Override
    public void filter(ContainerRequestContext requestContext)抛出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实现SecurityContext {

    Set< String>角色;
    字符串用户名;
    boolean isSecure;
    public Authorizer(设置< String>角色,最终字符串用户名,
    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(){
    returnYour 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 >



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

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


    有几点需要注意:




    • SecurityContext 被注入到方法中。

    • 我们得到 Principal 并将其转换为 User 。所以你真的可以创建任何实现 Principal 的类,然后使用这个对象。

    • 使用 @RolesAllowed 注释。使用Jersey,有一个过滤器通过将 @RolesAllowed 注释中的每个值传递给 SecurityContext.isUserInRole 查看用户是否被允许访问资源。



      要为Jersey启用此功能,我们需要注册 RolesAllowedDynamicFeature
      pre $ code $ @ $ $
      $ public $ AppConfig extends ResourceConfig {

      public AppConfig(){
      packages(packages.to.scan);
      寄存器(RolesAllowedDynamicFeature.class);
      }
      }



    I have a JAX-RS service where I want all my users to access my services, but just those who have rights to see the result. Roles based security and existing REALMS and atuhentication methods doesn't fit my requirement.

    For example:

    1. user authenticates against one REST service and I send him JWT token with his ID
    2. user asks for other resource and sends his JWT with his ID in each request
    3. I check his user id (from JWT) and if the business logic returns result I send them back, else I send empty result set or specific HTTP status

    Question is: Where should I check for users ID, in some separate filter, security context or in every REST method implementation? How to provide REST methods with this ID, can securityContext be injected in every method after filtering request by ID?

    I'm using GlassFish 4.1 and Jersey JAX-RS implementation.

    解决方案

    You can perform this logic in a ContainerRequestFilter. It pretty common to handle custom security features in here.

    Some things to consider

    1. The class should be annotated with @Priority(Priorities.AUTHENTICATION) so it is performed before other filters, if any.

    2. You should make use of the SecurityContext, inside the filter. What I do is implement a SecurityContext. You can really implement it anyway you want.

    Here's a simple example without any of the security logic

    @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; }   
        }
    }
    

    A few things to notice

    • I've created a SecurityContext
    • I've added some roles, and used them for the isUserInRole method. This will be used for authorization.
    • I've created a custom User class, that implements java.security.Principal. I returned this custom object
    • Finally I set the new SecurityContext in the ContainerRequestContext

    Now what? Let's look at a simple resource class

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

    A few things to notice:

    • SecurityContext is injected into the method.
    • We get the Principal and cast it to User. So really you can create any class that implements Principal, and use this object however you want.
    • The use of the @RolesAllowed annotation. With Jersey, there is a filter that checks the SecurityContext.isUserInRole by passing in each value in the @RolesAllowed annotation to see if the User is allowed to access the resource.

      To enable this feature with Jersey, we need to register the RolesAllowedDynamicFeature

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

    这篇关于自定义JAX-RS授权 - 在每个请求中使用JWT的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆