简体   繁体   中英

WebFlux: JWT Token Authenticator

We are trying to secure our api with JWT.We are using webflux for our app development.Our requirement is to read the JWT token coming from the consumer and extract the certificate from JWT and validate.

We are using filter for TraceId in our application.What is my best approach to JWT Authentication? using another filter? or can i chain with existing TraceId Filter.Does spring provide any solutions for JWT Authentication ?

Here is the code I am using for Trace Id Filter.

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

    }


} 

When working with JWT in a Java project, it is recommended to use this dependency:

// https://mvnrepository.com/artifact/com.auth0/java-jwt
compile group: 'com.auth0', name: 'java-jwt', version: '3.11.0'

I personally use implementation instead of compile and tend to use "+" for the version.

Since you're using RSA, you'll need to establish a variable like this near the code you're workign with, ideally within the TraceIdFilter class from your code snippet.

RSAPublicKey publicKey;

Assuming you're able to get the contents of the public Key into a string called publicKeyStr you'll want to initialize your public Key Object like so:

try {
    X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyStr));
    publicKey = (RSAPublicKey)KeyFactory.getInstance("RSA").generatePublic(pubKeySpec);
} catch (InvalidKeySpecException | NoSuchAlgorithmException e)
{
    // Handle Exception accordingly
}

In your filter code, you can explore the JWT contents like so (using token as the String holding your JWT token:

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
        }

Note that JWT tokens can sometimes contain fields such as iat and exp , both of which take in dates and if the current time is after exp or before iat , then verification will fail.

should it succeed and you have a valid DecodedJWT object, then you can view various features available in the token.

For instance, if you are looking for a "valid" claim and expect it to be of a boolean type, you can access the boolean like-so.

Claim idClaim = decodedJwt.getClaim("valid");
        
Boolean idBool = idClaim.asBoolean();

Bear in mind, if no such claim is in the token, idClaim will be null and if it is not a Boolean value (or can't be converted to one), idBool will be null.

Here is a link to more of the Playload interface, which DecodedJWT extends. JWT Payload (this link works better in Firefox than in Edge Chromium)

Not knowing what the Certificate within the JWT token is supposed to look like, hopefully, you'll be able to find it using the Claims feature of this dependency.

Most of my answer focuses on the JWT aspect, but I found a Stackoverflow Question that might help that might inspire you regarding the WebFilter aspect of your query.

Here are some of the imports used for the code:

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;

My example is hantsy/spring-reactive-jwt-sample .

User a Fitler to populate SecurityContextHolder , register it in the Secuirty Filter Chain.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM