简体   繁体   English

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

[英]Custom JAX-RS authorization - using JWT in each request

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. 我有一个JAX-RS服务,我希望所有用户都能访问我的服务,但只有那些有权查看结果的用户。 Roles based security and existing REALMS and atuhentication methods doesn't fit my requirement. 基于角色的安全性和现有的REALMS和身份验证方法不符合我的要求。

For example: 例如:

  1. user authenticates against one REST service and I send him JWT token with his ID 用户对一个REST服务进行身份验证,并向他发送带有其ID的JWT令牌
  2. user asks for other resource and sends his JWT with his ID in each request 用户请求其他资源,并在每个请求中向他的JWT发送他的ID
  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 我检查他的用户ID(来自JWT),如果业务逻辑返回结果我发回去,否则我发送空结果集或特定HTTP状态

Question is: Where should I check for users ID, in some separate filter, security context or in every REST method implementation? 问题是:我应该在哪里检查用户ID,在一些单独的过滤器,安全上下文或每个REST方法实现中? How to provide REST methods with this ID, can securityContext be injected in every method after filtering request by ID? 如何使用此ID提供REST方法,在按ID过滤请求后,是否可以在每个方法中注入securityContext?

I'm using GlassFish 4.1 and Jersey JAX-RS implementation. 我正在使用GlassFish 4.1和Jersey JAX-RS实现。

You can perform this logic in a ContainerRequestFilter . 您可以在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. 该类应使用@Priority(Priorities.AUTHENTICATION)进行注释,以便在其他过滤器(如果有)之前执行。

  2. You should make use of the SecurityContext , inside the filter. 您应该在过滤器内使用SecurityContext What I do is implement a SecurityContext . 我所做的是实现一个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 我创建了一个SecurityContext
  • I've added some roles, and used them for the isUserInRole method. 我添加了一些角色,并将它们用于isUserInRole方法。 This will be used for authorization. 这将用于授权。
  • I've created a custom User class, that implements java.security.Principal . 我创建了一个自定义User类,它实现了java.security.Principal I returned this custom object 我退回了这个自定义对象
  • Finally I set the new SecurityContext in the ContainerRequestContext 最后,我在ContainerRequestContext设置了新的SecurityContext

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. SecurityContext被注入到方法中。
  • We get the Principal and cast it to User . 我们得到Principal并将其投射给User So really you can create any class that implements Principal , and use this object however you want. 所以你真的可以创建任何实现Principal类,并根据需要使用这个对象。
  • The use of the @RolesAllowed annotation. 使用@RolesAllowed注释。 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. 对于Jersey,有一个过滤器通过传入@RolesAllowed注释中的每个值来检查SecurityContext.isUserInRole ,以查看是否允许用户访问该资源。

    To enable this feature with Jersey, we need to register the RolesAllowedDynamicFeature 要使用Jersey启用此功能,我们需要注册RolesAllowedDynamicFeature

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

I was searching for an solution which is Jersey independent and works for Wildfly -> found this github example implementation: 我正在寻找一个独立于泽西岛的解决方案,适用于Wildfly - >发现这个github示例实现:

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

It should give you a hint how to solve it clean. 它应该给你一个提示如何解决它干净。

Implement a JWTRequestFilter which implements ContainerRequestFilter https://github.com/sixturtle/examples/blob/master/jaxrs-jwt-filter/src/main/java/com/sixturtle/jwt/JWTRequestFilter.java 实现一个实现ContainerRequestFilter的JWTRequestFilter https://github.com/sixturtle/examples/blob/master/jaxrs-jwt-filter/src/main/java/com/sixturtle/jwt/JWTRequestFilter.java

as stated above and register the filter as resteasy provider in web.xml: 如上所述,并在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>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM