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

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

例如:

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

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

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

您可以在ContainerRequestFilter执行此逻辑。 在这里处理自定义安全function非常常见。

有些事情需要考虑

  1. 该类应使用@Priority(Priorities.AUTHENTICATION)进行注释,以便在其他filter(如果有)之前执行。

  2. 您应该在filter中使用SecurityContext 。 我所做的是实现一个SecurityContext 。 无论如何你都可以真正实现它。

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

 @Provider @Priority(Priorities.AUTHENTICATION) public class SecurityFilter implements ContainerRequestFilter { @Override public void filter(ContainerRequestContext requestContext) throws IOException { SecurityContext originalContext = requestContext.getSecurityContext(); Set roles = new HashSet<>(); roles.add("ADMIN"); Authorizer authorizer = new Authorizer(roles, "admin", originalContext.isSecure()); requestContext.setSecurityContext(authorizer); } public static class Authorizer implements SecurityContext { Set roles; String username; boolean isSecure; public Authorizer(Set 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 。 我退回了这个自定义对象
  • 最后,我在ContainerRequestContext设置了新的SecurityContext

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

 @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,有一个filter通过传入@RolesAllowed注释中的每个值来检查SecurityContext.isUserInRole ,以查看是否允许用户访问资源。

    要使用Jersey启用此function,我们需要注册RolesAllowedDynamicFeature

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

我正在寻找一个独立于泽西岛的解决方案,适用于Wildfly – >发现这个github示例实现:

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

它应该给你一个提示如何解决它干净。

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

如上所述,并在web.xml中将filter注册为resteasy提供程序:

  Custom JAX-RS Providers resteasy.providers com.sixturtle.jwt.JWTRequestFilter   resteasy.role.based.security true