2015-04-21 17 views
9

Tôi có dịch vụ JAX-RS nơi tôi muốn tất cả người dùng truy cập dịch vụ của mình, nhưng chỉ những người có quyền xem kết quả. Vai trò dựa trên bảo mật và hiện tại REALMS và phương pháp atuhentication không phù hợp với yêu cầu của tôi.Ủy quyền JAX-RS tùy chỉnh - sử dụng JWT trong mỗi yêu cầu

Ví dụ:

  1. người dùng xác thực đối với một dịch vụ REST và tôi gửi cho anh ta JWT mã thông báo với ID của mình
  2. người dùng yêu cầu tài nguyên khác và gửi JWT của mình với ID của mình trong mỗi yêu cầu
  3. tôi kiểm tra id người dùng của anh ấy (từ JWT) và nếu logic nghiệp vụ trả về kết quả tôi gửi lại, thì tôi gửi tập kết quả trống hoặc trạng thái HTTP cụ thể

Câu hỏi là: Tôi nên kiểm tra ở đâu ID người dùng, trong một số bộ lọc riêng biệt, ngữ cảnh bảo mật hoặc trong mọi phương thức REST thực hiện? Làm thế nào để cung cấp các phương thức REST với ID này, SecurityContext có thể được tiêm vào mọi phương thức sau khi yêu cầu lọc theo ID không?

Tôi đang sử dụng GlassFish 4.1 và triển khai Jersey JAX-RS.

Trả lời

23

Bạn có thể thực hiện logic này trong một ContainerRequestFilter. Nó khá phổ biến để xử lý các tính năng bảo mật tùy chỉnh ở đây.

Một số điều cần xem xét

  1. Lớp nên được chú thích với @Priority(Priorities.AUTHENTICATION) vì vậy nó được thực hiện trước khi các bộ lọc khác, nếu có.

  2. Bạn nên sử dụng SecurityContext, bên trong bộ lọc. Những gì tôi làm là thực hiện một SecurityContext. Bạn có thể thực sự thực hiện nó dù sao bạn muốn.

Dưới đây là một ví dụ đơn giản mà không cần bất kỳ logic an ninh

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

Một số điều cần chú ý

  • Tôi đã tạo một SecurityContext
  • tôi đã thêm một số vai trò và sử dụng chúng cho phương thức isUserInRole. Điều này sẽ được sử dụng để ủy quyền.
  • Tôi đã tạo lớp tùy chỉnh User, triển khai java.security.Principal. Tôi trở về tùy chỉnh này phản đối
  • Cuối cùng tôi đặt mới SecurityContext trong ContainerRequestContext

Bây giờ những gì? Chúng ta hãy nhìn vào một lớp tài nguyên đơn giản

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

Một số điều cần chú ý:

  • SecurityContext được tiêm vào phương pháp này.
  • Chúng tôi nhận được Principal và truyền tới User.Vì vậy, thực sự bạn có thể tạo bất kỳ lớp nào thực hiện Principal và sử dụng đối tượng này theo bất kỳ cách nào bạn muốn.
  • Việc sử dụng chú thích @RolesAllowed. Với Jersey, có một bộ lọc kiểm tra SecurityContext.isUserInRole bằng cách chuyển vào mỗi giá trị trong chú thích @RolesAllowed để xem Người dùng có được phép truy cập tài nguyên hay không.

    Để kích hoạt tính năng này với Jersey, chúng ta cần phải đăng ký RolesAllowedDynamicFeature

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

nhờ trả lời của bạn. Tôi nghĩ rằng tôi có thể sử dụng ContainerRequestFilter, nhưng tôi không cần cho Vai trò và SecurityContext cho yêu cầu của tôi. Có quá đơn giản để sử dụng bộ lọc servlet, và cho mỗi yêu cầu vào tài nguyên REST của tôi, tôi sẽ thực hiện unmarshaling của tiêu đề JWT đã gửi trước đó, trích xuất thông tin người dùng từ nó và chuyển nó tới các phương thức dịch vụ để xác định cấp độ của người dùng bên trong mỗi mehod? Điều này có vẻ giống như một giải pháp thực dụng đơn giản mà chỉ cần làm việc .. – D00de

+0

Khá nhiều những gì bạn có thể làm trong một bộ lọc servlet, bạn có thể làm trong ContainerRequestFilter. Nhưng với thứ hai, bạn có quyền truy cập vào ứng dụng Jersey –

+0

cảm ơn, tôi đã thực hiện giải pháp của bạn bằng cách tiêm dữ liệu JWT cho người dùng chính và sau đó lấy nó từ SecurityContext được tiêm vào mọi phương thức REST mà tôi có. Tôi đã bỏ phiếu và chấp nhận sự giúp đỡ của bạn. – D00de

1

Tôi đang tìm kiếm một giải pháp đó là Jersey độc lập và làm việc cho Wildfly -> tìm thấy ví dụ thực hiện github này:

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

Cần cung cấp cho bạn gợi ý cách giải quyết nó sạch sẽ.

Thực hiện một JWTRequestFilter mà thực hiện ContainerRequestFilter https://github.com/sixturtle/examples/blob/master/jaxrs-jwt-filter/src/main/java/com/sixturtle/jwt/JWTRequestFilter.java

như đã nêu ở trên và đăng ký bộ lọc như nhà cung cấp resteasy trong web.xml:

<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> 
Các vấn đề liên quan