Java WebFlux:JWT令牌身份验证器

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

我们正在尝试使用JWT保护我们的api。我们正在使用webflux进行应用程序开发。我们的要求是读取来自消费者的JWT令牌,并从JWT提取证书并进行验证

我们正在应用程序中使用TraceId过滤器。我最好的JWT身份验证方法是什么? 使用另一个过滤器?或者我可以与现有的TraceId过滤器链接。spring是否为JWT身份验证提供了任何解决方案

下面是我用于跟踪Id筛选器的代码

@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进行依赖关系管理?还是使用其他工具?存在一个专门的依赖关系