Java WebFlux:JWT令牌身份验证器
我们正在尝试使用JWT保护我们的api。我们正在使用webflux进行应用程序开发。我们的要求是读取来自消费者的JWT令牌,并从JWT提取证书并进行验证 我们正在应用程序中使用TraceId过滤器。我最好的JWT身份验证方法是什么? 使用另一个过滤器?或者我可以与现有的TraceId过滤器链接。spring是否为JWT身份验证提供了任何解决方案 下面是我用于跟踪Id筛选器的代码Java WebFlux:JWT令牌身份验证器,java,spring,spring-boot,kotlin,spring-webflux,Java,Spring,Spring Boot,Kotlin,Spring Webflux,我们正在尝试使用JWT保护我们的api。我们正在使用webflux进行应用程序开发。我们的要求是读取来自消费者的JWT令牌,并从JWT提取证书并进行验证 我们正在应用程序中使用TraceId过滤器。我最好的JWT身份验证方法是什么? 使用另一个过滤器?或者我可以与现有的TraceId过滤器链接。spring是否为JWT身份验证提供了任何解决方案 下面是我用于跟踪Id筛选器的代码 @Component @Slf4j public class TraceIdFilter implements Web
@Component
@Slf4j
public class TraceIdFilter implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
Map<String, String> headers = exchange.getRequest().getHeaders().toSingleValueMap();
return chain.filter(exchange)
.subscriberContext(context -> {
var traceId = "";
if (headers.containsKey("X-B3-TRACEID")) {
traceId = headers.get("X-B3-TRACEID");
MDC.put("X-B3-TraceId", traceId);
} else if (!exchange.getRequest().getURI().getPath().contains("/actuator")) {
log.warn("TRACE_ID not present in header: {}", exchange.getRequest().getURI());
}
// simple hack to provide the context with the exchange, so the whole chain can get the same trace id
Context contextTmp = context.put("X-B3-TraceId", traceId);
exchange.getAttributes().put("X-B3-TraceId", traceId);
return contextTmp;
});
}
}
@组件
@Slf4j
公共类TraceIdFilter实现WebFilter{
@凌驾
公共Mono筛选器(服务器WebExchange exchange、WebFilterChain链){
映射头=exchange.getRequest().getHeaders().toSingleValueMap();
返回链。过滤器(交换)
.subscriberContext(上下文->{
var traceId=“”;
if(标题。containsKey(“X-B3-TRACEID”)){
traceId=headers.get(“X-B3-traceId”);
MDC.put(“X-B3-TraceId”,TraceId);
}如果(!exchange.getRequest().getURI().getPath()包含(“/exactor”)),则为else{
log.warn(“跟踪_ID不存在于头:{}”,exchange.getRequest().getURI());
}
//提供交换上下文的简单hack,这样整个链就可以获得相同的跟踪id
Context-contextTmp=Context.put(“X-B3-TraceId”,TraceId);
exchange.getAttributes().put(“X-B3-TraceId”,TraceId);
返回contextTmp;
});
}
}
在Java项目中使用JWT时,建议使用此依赖项:
// https://mvnrepository.com/artifact/com.auth0/java-jwt
compile group: 'com.auth0', name: 'java-jwt', version: '3.11.0'
我个人使用实现而不是编译,并且倾向于使用“+”作为版本
由于您使用的是RSA,因此需要在您正在使用的代码附近建立一个类似这样的变量,最好是在代码片段的TraceIdFilter类中
RSAPublicKey publicKey;
假设您能够将公钥的内容放入一个名为publicKeyStr的字符串中,您将需要初始化公钥对象,如下所示:
try {
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyStr));
publicKey = (RSAPublicKey)KeyFactory.getInstance("RSA").generatePublic(pubKeySpec);
} catch (InvalidKeySpecException | NoSuchAlgorithmException e)
{
// Handle Exception accordingly
}
在筛选代码中,您可以像这样浏览JWT内容(使用令牌作为保存JWT令牌的字符串:
DecodedJWT decodedJwt = null;
try
{
decodedJwt = JWT.require(Algorithm.RSA512(publicKey,null))
.build()
.verify(token);
}
catch(JWTVerificationException e)
{
// If your key is valid, then the token is invalid, so handle accordingly
}
请注意,JWT令牌有时可能包含诸如iat和exp之类的字段,这两个字段都接受日期,如果当前时间在exp之后或iat之前,则验证将失败
如果成功,并且您有一个有效的DecodedJWT对象,那么您可以查看令牌中可用的各种功能
例如,如果您正在寻找一个“有效”声明,并且希望它是布尔类型,那么您可以像这样访问布尔类型
Claim idClaim = decodedJwt.getClaim("valid");
Boolean idBool = idClaim.asBoolean();
请记住,如果令牌中没有此类声明,idClaim将为null,如果它不是布尔值(或无法转换为布尔值),idBool将为null
这里有一个链接指向更多的Playload接口,DecodedJWT扩展了该接口。
(该链接在Firefox中比在Edge Chromium中更有效)
由于不知道JWT令牌中的证书应该是什么样子,希望您能够使用此依赖项的声明功能找到它
我的大部分回答都集中在JWT方面,但我发现了一个可能有助于激发您对查询的WebFilter方面的兴趣的方法
以下是代码使用的一些导入:
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.RSAKeyProvider;
在Java项目中使用JWT时,建议使用此依赖项:
// https://mvnrepository.com/artifact/com.auth0/java-jwt
compile group: 'com.auth0', name: 'java-jwt', version: '3.11.0'
我个人使用实现而不是编译,并且倾向于使用“+”作为版本
由于您使用的是RSA,因此需要在您正在使用的代码附近建立一个类似这样的变量,最好是在代码片段的TraceIdFilter类中
RSAPublicKey publicKey;
假设您能够将公钥的内容放入一个名为publicKeyStr的字符串中,您将需要初始化公钥对象,如下所示:
try {
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyStr));
publicKey = (RSAPublicKey)KeyFactory.getInstance("RSA").generatePublic(pubKeySpec);
} catch (InvalidKeySpecException | NoSuchAlgorithmException e)
{
// Handle Exception accordingly
}
在筛选代码中,您可以像这样浏览JWT内容(使用令牌作为保存JWT令牌的字符串:
DecodedJWT decodedJwt = null;
try
{
decodedJwt = JWT.require(Algorithm.RSA512(publicKey,null))
.build()
.verify(token);
}
catch(JWTVerificationException e)
{
// If your key is valid, then the token is invalid, so handle accordingly
}
请注意,JWT令牌有时可能包含诸如iat和exp之类的字段,这两个字段都接受日期,如果当前时间在exp之后或iat之前,则验证将失败
如果成功,并且您有一个有效的DecodedJWT对象,那么您可以查看令牌中可用的各种功能
例如,如果您正在寻找一个“有效”声明,并且希望它是布尔类型,那么您可以像这样访问布尔类型
Claim idClaim = decodedJwt.getClaim("valid");
Boolean idBool = idClaim.asBoolean();
请记住,如果令牌中没有此类声明,idClaim将为null,如果它不是布尔值(或无法转换为布尔值),idBool将为null
这里有一个链接指向更多的Playload接口,DecodedJWT扩展了该接口。
(该链接在Firefox中比在Edge Chromium中更有效)
由于不知道JWT令牌中的证书应该是什么样子,希望您能够使用此依赖项的声明功能找到它
我的大部分回答都集中在JWT方面,但我发现了一个可能有助于激发您对查询的WebFilter方面的兴趣的方法
以下是代码使用的一些导入:
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.RSAKeyProvider;
我的例子是
用户a在安全过滤器链中填充SecurityContextHolder。我的示例是
用户a在Secuirty筛选器链中填充
SecurityContextHolder
。您是使用maven或grade进行依赖关系管理?还是使用其他工具?存在一个专门用于JWT管理的依赖关系,如果我知道您使用的工具,我可以提供更多信息。我使用的是gradle。既然您使用WebFlux,为什么不使用尝试集成spring boot starter安全性和jjwt?请参考此,我们的JWT要求不是基于角色的,我们获取令牌,并从令牌中提取证书并进行验证。@Rocky4是否希望此示例有所帮助。(用kotlin编写)您是使用maven或grade进行依赖关系管理?还是使用其他工具?存在一个专门的依赖关系