What is JWT

Json Web Token (JWT) is an open jSON-based standard (RFC 7519) implemented for the transfer of declarations between network application environments. The token is designed to be compact and secure, especially suitable for single sign-on (SSO) scenarios in distributed sites. The JWT declaration is generally used to pass authenticated user identity information between the identity provider and the service provider to obtain resources from the resource server, and to add some additional declaration information necessary for other business logic. The token can also be used directly for authentication or can be encrypted.

advantages
  • Support across languages, like JAVA, JavaScript, NodeJS, PHP… .
  • You can store non-sensitive information in payload that is necessary for other business logic.
  • Easy to transfer, the JWT is very simple to build and takes up very few bytes
  • Easy to use extensions that do not require session information to be stored on the server
disadvantages
  • Once a JWT is generated, the data cannot be modified
  • JWT PlayLoad has a lot of data and occupies bandwidth resources on the server
  • JWT is not very secure, playload cannot store sensitive information, it must be encrypted
Note:
  • The payload part should not store sensitive information
  • Protect the Secret private key, which is used for JWT signing and JWT authentication

Ii. Composition of JWT

The first part we call the header, the second part we call the payload, the third part is the visa, the signature.

1, the header

The header of the JWT carries two pieces of information:

Declare type, in this case JWT, and declare encryption algorithms usually directly using HMAC SHA256, as follows:

{
  'typ': 'JWT'.'alg': 'HS256'
}
Copy the code
2, playload

The payload is where the useful information is stored. The name seems to refer specifically to the cargo carried on the plane, and this valid information consists of three parts

(1) Declaration of registration in the standard

Iss: JWT issuer sub: JWT user aud: JWT receiving party exp: JWT expiration time, which must be greater than the issue time NBF: JWT is unavailable before the issue time defined. The unique identifier of the JWT is mainly used as a one-time token to avoid replay attacks

(2) Public statements

Public declarations can add any information, usually about the user or other information necessary for the business. However, it is not recommended to add sensitive information because this part can be decrypted on the client side

(3) Private declaration

Private declarations are defined by both providers and consumers. Sensitive information is generally not recommended because Base64 is symmetrically decrypted, meaning that part of the information can be classified as plaintext information.

3.signature

Signature is used by the base64-encrypted header and the base64-encrypted payload. The string consists of the join and is salted with the encryption method declared in the header for MD5 encryption. Then, the third part of the JWT is formed.

Use JWT in Java

1. Import related packages
		<dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.107.</version>
        </dependency>
Copy the code
2. Log in to the JWT management tool
package com.sxkj.jwt.utils;

import cn.hutool.core.date.DateUtil;
import com.sxkj.jwt.model.User;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.http.parser.Authorization;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
@Component
public class JwtTokenUtil {
    private static final Logger LOGGER = LoggerFactory.getLogger(JwtTokenUtil.class);

    private static final String CLAIM_KEY_USERNAME = "sub";
    private static final String CLAIM_KEY_CREATED = "created";

    /** * The name of the key that holds the token */
    private static String headerKey = "Bearer";
    private static String tokenHeader = "Authorization";
    /** * secret */
    private static String secret = "lss0555";
    /** * Expiration time, in seconds */
    private static long expire = 1800L;

    static {
        // The values of the variables above TODO should be read from the configuration file for testing purposes
        // Override the value initialized by a static variable with the value in the configuration file
    }

    /** * based on the token responsible for generating the JWT */
    public static String generateToken(Map<String, Object> userInfoMap) {
        if (Objects.isNull(userInfoMap)) {
            userInfoMap = new HashMap<>();
        }
        // Expiration time
        Date expireDate = new Date(System.currentTimeMillis() + expire * 1000);
        return Jwts.builder()
                .setHeaderParam("typ"."JWT")   // Set the header information
                .setClaims(userInfoMap)               // Load custom user information
                .setExpiration(expireDate)            // Token expiration time
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }

    /** * Check the token and parse the token obtain the load in JWT from the token ** If expired or tampered, an exception will be thrown. Only in the generated token to set the expiration time (setExpiration (expireDate)) to check whether the overdue, * can reference source IO in jsonwebtoken. Impl. DefaultJwtParser 299 lines. * Extension: We do not set Expiration to verify whether tokens expire without setting Expiration time. * Instead, custom fields are used to store expiration times in Claims (simply known as maps); * After obtaining Claims through token, write code to verify whether the Claims are expired. * With this idea, you can implement manual refreshing of expired tokens *@param token
     * @returnClaims: It inherits a Map and stores the user information */ that was put in when the token was generated
    public static Claims getClaimsFromToken(String token) {
        try {
            return Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception e) {
            LOGGER.info("JWT format validation failed :{}", token);
            return null; }}/** * Obtain the login user name from the token */
    public static String getUserNameFromToken(String token) {
        String username;
        try {
            Claims claims = getClaimsFromToken(token);
            username = claims.getSubject();
        } catch (Exception e) {
            username = null;
        }
        return username;
    }

    /** * Verify that the token is still valid **@paramToken Indicates the token * passed by the client@paramUser User information queried from the database */
    public boolean validateToken(String token, User user) {
        String username = getUserNameFromToken(token);
        returnusername.equals(user.getUsername()) && ! isTokenExpired(token); }/** * Check whether the token is invalid */
    private static boolean isTokenExpired(String token) {
        Date expiredDate = getExpiredDateFromToken(token);
        return expiredDate.before(new Date());
    }

    /** * Get expiration time from token */
    private static Date getExpiredDateFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        return claims.getExpiration();
    }

    /** * Generate token */ based on user information
    public static String generateToken(User user) {
        Map<String, Object> claims = new HashMap<>();
        claims.put(CLAIM_KEY_USERNAME, user.getUsername());
        claims.put(CLAIM_KEY_CREATED, new Date());
        return generateToken(claims);
    }

    /** * The token can be refreshed when the original token has not expired@paramOldToken Token with tokenHead */
    public static String refreshHeadToken(String oldToken) {
        if(StringUtils.isEmpty(oldToken)){
            return null;
        }
        String token = oldToken.substring(headerKey.length());
        if(StringUtils.isEmpty(token)){
            return null;
        }
        // The token verification fails
        Claims claims = getClaimsFromToken(token);
        if(claims==null) {return null;
        }
        // If the token has expired, it cannot be refreshed
        if(isTokenExpired(token)){
            return null;
        }
        // If the token is refreshed within 30 minutes, the original token is returned
        if(tokenRefreshJustBefore(token,30*60)) {return token;
        }else{
            claims.put(CLAIM_KEY_CREATED, new Date());
            returngenerateToken(claims); }}/** * Determines whether the token has just been refreshed within the specified time@paramToken the original token *@paramTime Specifies the time (s) */
    private static boolean tokenRefreshJustBefore(String token, int time) {
        Claims claims = getClaimsFromToken(token);
        Date created = claims.get(CLAIM_KEY_CREATED, Date.class);
        Date refreshDate = new Date();
        // Refresh time within the specified time of creation time
        if(refreshDate.after(created)&&refreshDate.before(DateUtil.offsetSecond(created,time))){
            return true;
        }
        return false;
    }

    /** * Get the token header key *@return* /
    public static String getHeaderKey(a){
        return headerKey;
    }

    /** * Get the token header key *@return* /
    public static String getTokenHeader(a){
        returntokenHeader; }}Copy the code

The controller