JWT low-level entry

1. Introduction to JWT

  • JWT is the abbreviation of JSON Web Token, that is, JSON Web Token, which is a self-contained token. It is a JSON-based open standard implemented for passing declarations between web application environments.

  • The statement of JWT is generally used to pass the authenticated user identity information between the identity provider and the service provider, so as to obtain resources from the resource server. For example, it is used for user login.

  • The most important function of JWT is the anti-counterfeiting function of token information.

2. JWT composition introduction

A JWT consists of three parts: JWT header, payload, signature hash,
and finally base64url encoding of the combination of these three parts to obtain JWT

Typically, a JWT looks like the following figure: the object is a very long string, and the characters are divided into three substrings by the "." delimiter.

JWT header

The JWT header is a JSON object describing the JWT metadata, typically as follows.

{
    
    
  "alg": "HS256",
  "typ": "JWT"
}

In the above code, the alg attribute indicates the algorithm used by the signature, and the default is HMAC SHA256 (written as HS256);

The typ attribute indicates the type of the token, and the JWT token is uniformly written as JWT.

Finally, use the Base64 URL algorithm to convert the above JSON object into a string and save it.

Payload

The payload part is the main content part of the JWT, and it is also a JSON object that contains the data that needs to be passed. JWT specifies seven default fields for selection.

iss: jwt签发者
sub: 主题
aud: 接收jwt的一方
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的.
iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。

In addition to the above default fields, we can also customize private fields, as in the following example:

{
    
    
  "name": "Helen",
  "role": "editor",
  "avatar": "helen.jpg"
}

Please note that JWT is unencrypted by default, and anyone can interpret its content, so do not build a private information field to store confidential information to prevent information leakage.

JSON objects are also converted to strings using the Base64 URL algorithm.

signature hash

The signature hash part is to sign the above two parts of the data, and generate a hash through the specified algorithm to ensure that the data will not be tampered with.

First, you need to specify a password (secret). This password is only stored on the server and cannot be disclosed to the user. Then, use the signature algorithm specified in the header (HMAC SHA256 by default) to generate a signature according to the following formula.

HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(claims), secret)    ==>   签名hash

After calculating the signature hash, the three parts of the JWT header, payload and signature hash are combined into a string, and each part is separated by "." to form the entire JWT object.

Base64URL Algorithm

As mentioned earlier, both the JWT header and payload serialization algorithms use Base64URL. This algorithm is similar to the common Base64 algorithm, with slight differences.

A JWT as a token can be placed in the URL (eg api.example/?token=xxx). The three characters used in Base64 are "+", "/" and "=". Since they have special meanings in the URL, they are replaced in Base64URL: "=" is removed, "+" is replaced with "-" ,"/" is replaced with "_", this is the Base64URL algorithm.

3. Introduce dependencies

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.5</version>
</dependency>

If only this jar package is imported, an error will be reported when running the code

Exception in thread "main" java.lang.IllegalStateException: Unable to invoke class method io.jsonwebtoken.impl.crypto.MacProvider#generateKey.  Ensure the necessary implementation is in the runtime classpath.

Caused by: io.jsonwebtoken.lang.UnknownClassException: Unable to load class named [io.jsonwebtoken.impl.crypto.MacProvider] from the thread context, current, or system/application ClassLoaders.  All heuristics have been exhausted.  Class could not be found.  Have you remembered to include the jjwt-impl.jar in your runtime classpath?

Also need to import


<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.5</version>
</dependency>

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.11.5</version>
</dependency>

4. Code introduction

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;

import javax.crypto.SecretKey;
import java.util.Date;

/**
 * @author 尹稳健~
 * @version 1.0
 * @time 2022/12/14
 */
@Slf4j
public class JwtUtil {
    
    

    /** 使用HS512加密算法 签名 */
    static SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS512);

    /** 设置token过期时间 */
    private static final long EXPIRE_TIME = 1000*2*60;

    /**
     * 创建token
     * @param userId
     * @param username
     * @return
     */
    public static String createToken(Long userId,String username){
    
    
        String token = Jwts.builder()
                // 可以将基本不重要的对象信息放到claims
                .claim("userId", userId)
                .claim("username", username)
                .signWith(key)
                .setSubject("AUTH-USER")
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRE_TIME))
                .compact();
        return token;
    }

    /**
     * 解析token
     * @param token
     * @return
     */
    public static Claims  parseToken(String token){
    
    
        //顺带说一句,当Jwt设置了有效期,有效期时间过了之后也会抛出异常,解决办法是try
        //catch一下,将异常抛给统一异常处理类
        try {
    
    
            Claims claims = Jwts.parserBuilder()
                    .setSigningKey(key)
                    .build()
                    .parseClaimsJws(token)
                    .getBody();
            return claims;
        } catch (ExpiredJwtException  eje) {
    
    
            log.error("===== Token过期 =====", eje);
            throw new RuntimeException(eje.getMessage());
        } catch (Exception e){
    
    
            log.error("===== token解析异常 =====", e);
            throw new RuntimeException(e.getMessage());
        }
    }

    /**
     * 获取用户id
     * @param token
     * @return
     */
    public static Long getUserId(String token){
    
    
        return (Long) parseToken(token).get("userId");
    }

    /**
     * 获取用户名
     * @param token
     * @return
     */
    public static String getUsername(String token){
    
    
        return (String)parseToken(token).get("username");
    }
    
	/**
     * 判断是否过期
     * @param token
     * @param base64Security
     * @return
     */
    public static boolean isExpiration(String token, String base64Security) {
    
    
        return parseToken(token).getExpiration().before(new Date());
    }

}


Guess you like

Origin blog.csdn.net/weixin_46073538/article/details/128325116