SpringBoot integrates JWT to realize login verification

1 Definition

The full name of JWT is JSON Web Token, which is the most popular cross domain authentication solution at present. JWT is an open standard based on JSON, which is designed to transfer declarations between network application environments.

This information can be verified and trusted because it is digitally signed. JWT can sign with a key (using HMAC algorithm) or with RSA or ECDSA's public / private key pair.

JWT is especially suitable for single sign on (SSO) scenarios of distributed sites. The declaration of JWT is generally used to transfer the authenticated user identity information between identity providers and service providers, so as to obtain resources from the resource server. It can also be encrypted.

Official introduction

2 data structure of JWT

JWT is actually a very long string, which is [made up of JSON objects by Base64 coding] through "." The separator is divided into three substrings, and there is no newline between each string. Each substring represents a function block. There are three parts in total: JWT header, payload and signature, as shown in the following figure:

2.1 JWT head

The JWT header is a JSON object that describes JWT metadata, which is usually as follows:

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

alg: indicates the algorithm used for signature. The default is HMAC SHA256 (HS256)

typ: indicates the type of token. JWT tokens are uniformly written as JWT

Finally, use the Base64 URL algorithm to convert the above JSON object into a string

2.2 payload

Payload is not only the main content part of JWT, but also a JSON object, which contains the data to be transmitted.

The payload section specifies the following seven default fields to choose from:

iss: publisher
exp: Expiration time
sub: theme
aud: user
nbf: Not available before
iat: Release time
jti: JWT ID Used for this identification JWT

In addition to the above default fields, you can also customize private fields.

2.3 signature

Signature is actually an encryption process, which generates hash for the above two parts of data through the specified algorithm to ensure that the data will not be tampered with.

First, you need to specify a password (secret), which is only saved in the server and cannot be disclosed to users. Then use the signature algorithm specified in the JWT header (HMAC SHA256 by default) to generate the signature hash according to the following formula:

HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload),secret)

After calculating the signature hash, the JWT header, payload and three parts of the signature hash are combined into a string, and each part is marked with "." Separation constitutes the whole JWT object.
Note that for a signed token, all the information contained in the token is exposed to users or other parties, even if they cannot change it. This means that you should not put confidential information in your token.

2.3 JWT signature algorithm

In JWT signature algorithm, there are generally two choices: HS256 and RS256.

HS256 (HMAC with SHA-256) is a symmetric encryption algorithm. Only one key is shared between the two sides. Since the same key is used to generate and verify signatures, care must be taken to ensure that the key is not compromised.

RS256 (RSA signature using SHA-256) is an asymmetric encryption algorithm. It uses public / private key pairs: the provider of JWT uses the private key to generate a signature, and the user of JWT obtains the public key to verify the signature.

Authentication mechanism of 3JWT

In authentication, a JSON Web token is returned when a user successfully logs in using their credentials. In authentication, a JSON Web token is returned when a user successfully logs in using their credentials.
Whenever a user wants to access a protected route or resource, the user agent should send a JWT, usually using the holder schema in the authorization header. The contents of the header should be as follows:

Authorization: Bearer <token>

4. Integrate SpringBoot

4.1 related dependencies
  <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

<!--jwt Create and verify-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
    </dependencies>
4.2 JWT general method
import io.jsonwebtoken.Jwt;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.io.*;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

/**
 * @author lyf
 * @create 2022-03-05 17:43
 * JWT Validation tool class
 **/
public class JWTUtil {


    /**
     * Create JWT validation string based on payload
     * The signature algorithm used is HS256, symmetric algorithm, generating JWT string and parsing JWT
     * @param playload JWT Payload
     * @param key Signed key,
     * @return
     */
    public String createJWTStrHS256(Map<String,Object> playload,String key){

        //Header JSON data, fixed writing method
        Map<String,Object>headerMap=new HashMap<>();
        //Algorithm HS256
        headerMap.put("alg", SignatureAlgorithm.HS256.getValue());
        headerMap.put("typ", "JWT");

        //Use the API provided by JJWT to create JWT
        String s = Jwts.builder()
                //Set header data
                .setHeader(headerMap)
                //Set payload data
                .setClaims(playload)
                //Use HS256 signature algorithm
                .signWith(SignatureAlgorithm.HS256, key)
                .compact();
        return s;
    }

    /**
     * Get payload object from JWT string
     * @param JwtStr
     * @param key Customize the key, which is consistent with the key used to create JWT
     * @return
     */
    public Object decodeJWTrHS256(String JwtStr,String key){

        //Header JSON data, fixed writing method
        Map<String,Object>headerMap=new HashMap<>();
        //Algorithm HS256
        headerMap.put("alg", SignatureAlgorithm.HS256.getValue());
        headerMap.put("typ","JWT");

        Jwt jwt = Jwts.parser()
                .setSigningKey(key)
                .parse(JwtStr);

        return jwt.getBody();
    }

    /**
     * Generate the public and private key files required in the RS256 signature algorithm
     * @param password Random password
     */
    public void generateRS256Key(String password) throws NoSuchAlgorithmException {

        //The key pair generates the Generator using RSA algorithm
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        SecureRandom secureRandom = new SecureRandom(password.getBytes());
        keyPairGenerator.initialize(1024, secureRandom);
        //Get key pair
        KeyPair keyPair = keyPairGenerator.genKeyPair();

        //Public key bytecode
        byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
        //Private key bytecode
        byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();

        //Write the information to the file with the key
        try {
            FileOutputStream fos=new FileOutputStream("..\\pub.key");
            fos.write(publicKeyBytes);
            fos.close();

            FileOutputStream fos_1=new FileOutputStream("..\\pri.key");
            fos_1.write(privateKeyBytes);
            fos_1.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Create a private key object according to the physical file of the private key, which is used by JWT to use the RSA256 signature algorithm
     * @return
     */
    public PrivateKey generatePrivateKey( )  {
        try {
            //According to the pri Get input stream from key private key physical file
            InputStream resourceAsStream =
                    this.getClass().getClassLoader().getResourceAsStream("pri.key");
            DataInputStream dis = new DataInputStream(resourceAsStream);
            byte[] keyBytes = new byte[resourceAsStream.available()];
            dis.readFully(keyBytes);
            PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
            KeyFactory kf = KeyFactory.getInstance("RSA");
            return kf.generatePrivate(spec);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * Create a public key object according to the public key physical file for JWT to use RSA256 signature algorithm
     * @return
     */
    public PublicKey generatePublicKey(){
        try {
            //According to the pri Get input stream from key private key physical file
            InputStream resourceAsStream =
                    this.getClass().getClassLoader().getResourceAsStream("pri.key");
            DataInputStream dis = new DataInputStream(resourceAsStream);
            byte[] keyBytes = new byte[resourceAsStream.available()];
            dis.readFully(keyBytes);
            X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
            KeyFactory kf = KeyFactory.getInstance("RSA");
            return kf.generatePublic(spec);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * Create JWT validation string based on payload
     * The signature algorithm used is RS256 and asymmetric algorithm. The private key is required to create JWT string
     * @param playload JWT Payload
     * @return
     */
    public String createJWTStrRS256(Map<String,Object> playload){

        //Header JSON data, fixed writing method
        Map<String,Object>headerMap=new HashMap<>();
        //Algorithm HS256
        headerMap.put("alg", SignatureAlgorithm.RS256.getValue());
        headerMap.put("typ","JWT");

        PrivateKey key=generatePrivateKey();

        //Use the API provided by JJWT to create JWT
        String s = Jwts.builder()
                //Set header data
                .setHeader(headerMap)
                //Set payload data
                .setClaims(playload)
                //Use HS256 signature algorithm
                .signWith(SignatureAlgorithm.RS256, key)
                .compact();
        return s;
    }

    /**
     * Get payload object from JWT string
     * @param JwtStr JWT needs to be resolved from the public key
     * @return
     */
    public Object decodeJWTrRS256(String JwtStr){
        //Header JSON data, fixed writing method
        Map<String,Object>headerMap=new HashMap<>();
        //Algorithm HS256
        headerMap.put("alg", SignatureAlgorithm.RS256.getValue());
        headerMap.put("typ","JWT");

        PrivateKey key=generatePrivateKey();

        Jwt jwt = Jwts.parser()
                .setSigningKey(key)
                .parse(JwtStr);

        return jwt.getBody();
    }

}

4.3 project structure

4.4 test JWT string generation and parsing
```@Test
    public void test1(){
        Map<String, Object> playload=new HashMap<>();
        playload.put("userId","100");
        playload.put("tes","test");

        //Use HS256 encryption algorithm
        String str = util.createJWTStrHS256(playload, "123456");
        System.out.println(str);

        Object o = util.decodeJWTrHS256(str, "123456");
        System.out.println(o);


        //Use RS256 encryption algorithm
        String str1 = util.createJWTStrRS256(playload);
        System.out.println(str1);

        Object o1 = util.decodeJWTrRS256(str1);
        System.out.println(o1);
    }

Keywords: Java Spring Boot Back-end

Added by SnakeFox on Sat, 05 Mar 2022 13:56:47 +0200