自定义 JAX-RS 授权 - 在每个请求中使用 JWT
Custom JAX-RS authorization - using JWT in each request
我有一个 JAX-RS 服务,我希望我的所有用户都可以访问我的服务,但只有那些有权查看结果的用户。基于角色的安全性以及现有的 REALMS 和验证方法不符合我的要求。
例如:
- 用户针对一项 REST 服务进行身份验证,我将 JWT 令牌和他的 ID 一起发送给他
- 用户请求其他资源并在每个请求中发送他的 JWT 和他的 ID
- 我检查他的用户 ID(来自 JWT),如果业务逻辑 returns 结果我发回,否则我发送空结果集或特定的 HTTP 状态
问题是:我应该在哪里检查用户 ID,是在某个单独的过滤器、安全上下文中还是在每个 REST 方法实现中?如何给REST方法提供这个ID,securityContext是否可以通过ID过滤请求后注入到每个方法中?
我正在使用 GlassFish 4.1 和 Jersey JAX-RS 实现。
您可以在 ContainerRequestFilter
中执行此逻辑。在这里处理自定义安全功能很常见。
一些需要考虑的事情
class 应使用 @Priority(Priorities.AUTHENTICATION)
注释,以便在其他过滤器(如果有)之前执行。
您应该使用过滤器内的 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
方法。这将用于授权。
- 我创建了一个实现
java.security.Principal
的自定义 User
class。我返回了这个自定义对象
- 最后我在
ContainerRequestContext
中设置了新的 SecurityContext
现在呢?让我们看一个简单的资源 class
@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
的 class,并根据需要使用此对象。
@RolesAllowed
注解的使用。对于 Jersey,有一个过滤器通过传入 @RolesAllowed
注释中的每个值来检查 SecurityContext.isUserInRole
以查看是否允许用户访问资源。
要使用 Jersey 启用此功能,我们需要注册 RolesAllowedDynamicFeature
@ApplicationPath("/api")
public class AppConfig extends ResourceConfig {
public AppConfig() {
packages("packages.to.scan");
register(RolesAllowedDynamicFeature.class);
}
}
我正在寻找独立于 Jersey 并适用于 Wildfly 的解决方案 -> 找到了这个 github 示例实现:
https://github.com/sixturtle/examples/tree/master/jaxrs-jwt-filter
它应该给你一个提示如何解决它。
实施 JWTRequestFilter 实施 ContainerRequestFilter
https://github.com/sixturtle/examples/blob/master/jaxrs-jwt-filter/src/main/java/com/sixturtle/jwt/JWTRequestFilter.java
如上所述,并在 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>
我有一个 JAX-RS 服务,我希望我的所有用户都可以访问我的服务,但只有那些有权查看结果的用户。基于角色的安全性以及现有的 REALMS 和验证方法不符合我的要求。
例如:
- 用户针对一项 REST 服务进行身份验证,我将 JWT 令牌和他的 ID 一起发送给他
- 用户请求其他资源并在每个请求中发送他的 JWT 和他的 ID
- 我检查他的用户 ID(来自 JWT),如果业务逻辑 returns 结果我发回,否则我发送空结果集或特定的 HTTP 状态
问题是:我应该在哪里检查用户 ID,是在某个单独的过滤器、安全上下文中还是在每个 REST 方法实现中?如何给REST方法提供这个ID,securityContext是否可以通过ID过滤请求后注入到每个方法中?
我正在使用 GlassFish 4.1 和 Jersey JAX-RS 实现。
您可以在 ContainerRequestFilter
中执行此逻辑。在这里处理自定义安全功能很常见。
一些需要考虑的事情
class 应使用
@Priority(Priorities.AUTHENTICATION)
注释,以便在其他过滤器(如果有)之前执行。您应该使用过滤器内的
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
方法。这将用于授权。 - 我创建了一个实现
java.security.Principal
的自定义User
class。我返回了这个自定义对象 - 最后我在
ContainerRequestContext
中设置了新的
SecurityContext
现在呢?让我们看一个简单的资源 class
@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
的 class,并根据需要使用此对象。 @RolesAllowed
注解的使用。对于 Jersey,有一个过滤器通过传入@RolesAllowed
注释中的每个值来检查SecurityContext.isUserInRole
以查看是否允许用户访问资源。要使用 Jersey 启用此功能,我们需要注册
RolesAllowedDynamicFeature
@ApplicationPath("/api") public class AppConfig extends ResourceConfig { public AppConfig() { packages("packages.to.scan"); register(RolesAllowedDynamicFeature.class); } }
我正在寻找独立于 Jersey 并适用于 Wildfly 的解决方案 -> 找到了这个 github 示例实现:
https://github.com/sixturtle/examples/tree/master/jaxrs-jwt-filter
它应该给你一个提示如何解决它。
实施 JWTRequestFilter 实施 ContainerRequestFilter https://github.com/sixturtle/examples/blob/master/jaxrs-jwt-filter/src/main/java/com/sixturtle/jwt/JWTRequestFilter.java
如上所述,并在 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>