0

I work in REST service application and generate token and test it successfully. it work but when test the same token after rerun the application it give me this error message (JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not be trusted. -- {}). I think the problem in Java Key may be changed every time close and run the application and can not find the solution.

Here I use the Postman and generate access token.

!

Test token successfully.

!

Test same token after rebuild the project.

!

!

TokenUtils class used to generate access token and validation.

package com.keroles.jobify.Sec.Token.Util;

import com.keroles.jobify.Sec.Token.Model.TokenModel;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.security.Key;
import java.util.*;
import java.util.stream.Collectors;

@Component
@Slf4j
@Getter
public class TokenUtils {
    private final long ACCESS_TOKEN_VALIDITY=432000L;//7days
    private final long REFRESH_TOKEN_VALIDITY=604800L;//7days
    @Getter(AccessLevel.NONE)
    private final Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);


    public String generateToken(Authentication authentication ,long validityTime){
 log.error("{}",key.getEncoded());
 log.error(key.getEncoded().toString());
 return Jwts
                .builder()
                .setClaims(prepareClaims(authentication))
                .setSubject(authentication.getName())
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis()
                        + validityTime * 1000))
                .signWith(key)
                .compact();
    }


    public TokenModel getTokenModel(String token){
        Claims claims= getAllClaimsFromToken(token);
        return TokenModel
                .builder()
                .username(claims.getSubject())
                .roles((List<String>)claims.get("roles"))
                .createdAt(new Date( (Long) claims.get("created")))
                .expirationDate(claims.getExpiration())
                .build();
    }

    public boolean validateToken(TokenModel tokenModel, UserDetails userDetails){
        return (userDetails!=null
                && tokenModel.getUsername().equals(userDetails.getUsername())
                && ! isTokenExpired(tokenModel.getExpirationDate()));
    }

    private boolean isTokenExpired(Date expirationDate) {
        return expirationDate.before(new Date());
    }
    private Claims getAllClaimsFromToken(String token) {
        return Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();
    }
    private Map<String,Object> prepareClaims(Authentication authentication){
        List<String>authority=authentication.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList());
        Map<String, Object> claims = new HashMap<>();
        claims.put("created",new Date());
        claims.put("roles",authority);
        return claims;
    }

1 Answers1

0

You are getting a new key every time you are starting your application, relevant part from docs.

public static SecretKey secretKeyFor(SignatureAlgorithm alg) throws IllegalArgumentException

Returns a new SecretKey with a key length suitable for use with the specified SignatureAlgorithm. JWA Specification (RFC 7518), Section 3.2 requires minimum key lengths to be used for each respective Signature Algorithm. This method returns a secure-random generated SecretKey that adheres to the required minimum key length.

For playing around you can place the plain text secret into your properties file, and for production pick one of these options: Spring Boot how to hide passwords in properties file.
Once you load the secret from your properties file change your signing and parsing to use your secret. Example for signing:

return Jwts
                .builder()
                .setClaims(prepareClaims(authentication))
                .setSubject(authentication.getName())
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis()
                        + validityTime * 1000))
                .signWith(SignatureAlgorithm.HS256, injectedSecretFromProperties)
                .compact();
void void
  • 1,080
  • 3
  • 8