gogoWebsite

JWT detailed tutorial and use

Updated to 6 months ago

JWT

JSON Web Token (JSON Web Token)

is an open standard (rfc7519) that defines a compact, self-contained way of securely transferring information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. jwt can be signed with a secret (using the HNAC algorithm) or with a public/private key pair using RSA or ECDSA.

Used as a token in Web applications through the form of JSON for securely transferring information as JSON objects between parties. Data encryption, signing and other related processing can also be accomplished during data transmission.

  • JWT Role:
    • Authorization: Once the user is logged in, each subsequent request will include the JWT, thus allowing the user to access the routes, services and resources allowed by the token. It has very little overhead and can be used in different domains. E.g. Single Sign-On.
    • Information exchange: secure transmission of information between parties.JWT can be signed (e.g., using public/private key pairs), thus securing the sender. Since signatures are computed using headers and payloads, it is also possible to verify that the content has not been tampered with.

1. Traditional Session

1.1. Authentication methods

The http protocol itself is a stateless protocol, if the user provides the server with a username and password for user authentication, the next request, the user will have to authenticate the user again. Because according to the http protocol, the server does not know which user issued the request, so in order to allow our application to recognize which user issued the request, we can only store a copy of the user's login information on the server, the login information will be passed in the response to the browser, tell it to save as a cookie, in order to send the next request to our application, so that the application can recognize the request from which user. so that the application can recognize which user the request is coming from.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RJA4mCJy-1603957559914)(E:%5CLiuPengLearn%5CLiuP%E7%9A%84%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%B7%AF%5CJava%E6%8A%80%E6%9C%AF%E6%95%B4%E5%90%88%5CJWT%5Cimages%)]

1.2. Exposure

  • After the user has been authenticated by the application, the application has to do a record on the server side to facilitate the identification of the user's next request, usually the session is saved in memory, and with the increase in the number of authenticated users, the server-side overhead will increase significantly;
  • After the user authentication, the server does the authentication record, if the authentication record is kept in the memory, the next request of the user must also be requested on this server so that the authorized resources can be obtained. On distributed applications, it limits the capacity of the load balancer. In this way it limits the scalability of the application;
  • session is based on cookies for user identification, cookies if intercepted, the user is vulnerable to CSRF (Cross-Site Request Forgery Attack) attacks;
  • Application decoupling in front-end and back-end separation systems increases the complexity of deployment. Usually a user request is forwarded multiple times. If you use session to carry the sessionid to the service each time
    machine, the server also has to query the user information. Also if there are many users. This information is stored in the server memory, adding burden to the server. There is also the fact that sessionid is a feature value, which is not rich enough to express the information. It is not easy to extend. And if your back-end application is a multi-node deployment. Then you need to realize the session sharing mechanism. It is not convenient for cluster applications.

accreditation

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4Njwu2Yt-1603957559915)(E:%5CLiuPengLearn%5CLiuP%E7%9A%84%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%B7%AF%5CJava%E6%8A%80%E6%9C%AF%E6%95%B4%E5%90%88%5CJWT%5Cimages%)]

2.1. Certification process

  • The front-end sends its username and password to the back-end interface via a web form. The process is typically an HTTP POST request. The recommended way is through SSL encrypted transmission (https protocol), thus avoiding the sniffing of sensitive information.
  • After the back-end checks the username and password successfully, other information such as the user's id will be used as a JWT Payload(Load), which will be signed with the header respectively after Base64 encoding and splicing to form a JWT(Token).
  • The backend returns the JWT string to the frontend as the result of a successful login. The front-end can save the returned result in localStorage (browser local cache) or sessionStorage (session cache), and the front-end can delete the saved JWT when logging out.
  • The front-end puts the JWT into the Authorization bit in the HTTP's Header on every request. (Addresses XSS and XSRF issues) HEADER
  • The back-end checks whether the JWT exists, and if so, verifies the validity of the JWT. For example, check if the signature is correct; check if the Token is expired; check if the recipient of the Token is itself (optional)
  • -After authentication, the backend uses the user information contained in the JWT to perform other logical operations and return the corresponding results.

2. Advantages

  • Compact: can be sent via URL, POST parameter or in HTTP header, small amount of data, transmission speed is also very fast;
  • Self-contained (Self-contained): the load contains all the information the user needs, avoiding multiple queries to the database;
  • Token is stored in the form of JSON encryption on the client , so JWT is cross-language , in principle , any web form is supported .
  • There is no need to save session information on the server side, which is particularly suitable for distributed microservices.I

framework

It's a token, a String, consisting of 3 parts separated by dots.

The token consists of:

  1. Header
  2. Payload
  3. Signature

token format: e.g.:

  • Header: has the type of token and the signature algorithm used, e.g. HMAC, SHA256, RSA; consists of using Base64 encoding; (Base64 is an encoding, not an encryption process, and can be translated into its original form)

    {
    	"alg" : "HS256",
    	"type" : "JWT"
    }
    
  • Payload: payload containing declarations; declarations are statements about entities (usually users) and other data, without user-sensitive information such as passwords. Also encoded in Base64

    {
    	"sub" : "123",
    	"name" : "John Do",
    	"admin" : true
    }
    
  • Signature : The first two parts are encoded in Base64, so the front-end can decode the information inside; Signature needs to use the encoded header and payload.
    Together with a key that we provide, it is signed using the signature algorithm (HS256) specified in the header. The purpose of the signature is to ensure that the JWT has not been tampered with

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

    **Purpose of signature: **The process of signing is actually to sign the header and load content to prevent the content from being tampered with. If someone decodes the content of the header and load, modifies it, encodes it again, and then adds the previous signature to form a new JWT, the server will determine that the signature of the new header and load is not the same as the signature attached to the JWT. If you want to sign the new header and load without knowing the key used by the server for encryption, the signature will be different.

    Information security issues: Base64 is an encoding that is reversible and suitable for transmitting some non-sensitive information; JWT should not include sensitive data in the load. If the transmission of the user's ID is known is also safe, such as passwords can not be placed in JWT; JWT is often used to design user authentication, authorization systems, web single sign-on.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nujtvnmx-1603957559917)(E:%5CLiuPengLearn%5CLiuP%E7%9A%84%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%B7%AF%5CJava%E6%8A%80%E6%9C%AF%E6%95%B4%E5%90%88%5CJWT%5Cimages%)]

utilization

4.1. Introducing dependencies

<! --Introducing JWT-->!
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.10.0</version>
</dependency>

4.2. Generate token

HashMap<String,Object> map = new HashMap<>();
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.SECOND,20);
        String token = JWT.create()
                .withHeader(map) //You can not set it, just use the default
                .withClaim("userId",20)//payload //custom username
                .withClaim("username","zhangsan")
                .withExpiresAt(instance.getTime()) //Specify when the token expires
                .sign(Algorithm.HMAC256("fdahuifeuw78921"));// Signature

4.3. Parsing data based on tokens and signatures

JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("fdahuif921")).build();
        DecodedJWT decodedJWT = jwtVerifier.verify(token);
        decodedJWT.getClaim("userId").asInt();// Get the corresponding content inside the load
        decodedJWT.getClaim("username").asString();
        decodedJWT.getExpiresAt();// Get expiration time

4.4. Common Exception Messages

SignatureVerificationException //Signature Inconsistency Exception
TokenExpiredException //Token expiration exception
AlgorithmMismatchException // Algorithm mismatch exception
InvalidClaimException // Failed payload exception (token altered after passing to client, inconsistent validation)

5. Packaging tools

public class JWTUtils {
    private static String SIGNATURE = "token!@#$%^7890";

    /**
     * Generate token
     * @param map // pass in payload
     * @return return token
     */
    public static String getToken(Map<String,String> map){
        JWTCreator.Builder builder = JWT.create();
        map.forEach((k,v)->{
            builder.withClaim(k,v);
        });
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.SECOND,7);
        builder.withExpiresAt(instance.getTime());
        return builder.sign(Algorithm.HMAC256(SIGNATURE)).toString();
    }

    /**
     * Authenticate token
     * @param token
     */
    public static void verify(String token){
        JWT.require(Algorithm.HMAC256(SIGNATURE)).build().verify(token);
    }

    /**
     * Get the payload in token
     * @param token
     * @return
     */
    public static DecodedJWT getToken(String token){
        return JWT.require(Algorithm.HMAC256(SIGNATURE)).build().verify(token);
    }
}

Integration of JWT

6.1. Generate token on login

//controller layer receives data, generates token, and responds
Map<String,Object> map = new HashMap<>();
try{
    User userDB = userService.login(user);
    Map<String,String> payload = new HashMap<>();
    payload.put("id",userDB.getId());
    payload.put("name",userDB.getName());
    // Generate JWT tokens
    String token = JWTUtils.getToken(payload);
    map.put("state",true);
    map.put("msg","Authentication successful.");
    map.put("token",token);//Response token
} catch (Exception e) {
    map.put("state","false");
    map.put("msg",e.getMessage());
}

6.2. Declaring a token interceptor class

package com.liup.interceptor;

import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.office.utils.JWTUtils;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

/**
 * JWT Authentication Interceptor
 */
public class JWTInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Map<String,Object> map = new HashMap<>();
        //Tokens are recommended to be placed in the request header, get the token in the request header
        String token = request.getHeader("token");
        try{
            JWTUtils.verify(token);//Authentication Token
            return true;//Release requests
        } catch (SignatureVerificationException e) {
            e.printStackTrace();
            map.put("msg","Invalid signature");
        } catch (TokenExpiredException e) {
            e.printStackTrace();
            map.put("msg","Token expired.");
        } catch (AlgorithmMismatchException e) {
            e.printStackTrace();
            map.put("msg","Inconsistent token algorithms.");
        } catch (Exception e) {
            e.printStackTrace();
            map.put("msg","Token failure.");
        }
        map.put("state",false);//Setting the status
        //convert map to json, response uses Jackson
        String json = new ObjectMapper().writeValueAsString(map);
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().print(json);
        return false;
    }
}

6.3. Configuring Interceptors

package com.liup.config;

import com.office.interceptor.JWTInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new JWTInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/user/**");
    }
}