【JWT】JWT integration

Article directory

1. Introduction to JWT

JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact, self-contained way to securely transmit information between parties as JSON objects. This information can be verified and trusted because it is [digitally signed]** is an open standard (RFC 7519) that defines a compact, self-contained way to securely transmit information between parties as JSON objects. This information can be verified and trusted because it is [digitally signed] .

2. Application scenarios of JWT

  • Authorization

    This is the most common scenario for using JWT. Once the user is logged in, each subsequent request will include the JWT, allowing the user to access the routes, services, resources allowed by that token. Single sign-on is a feature that is widely used today with JWT because of its low overhead and ease of use across different domains.

  • Information Exchange

    JWTs are a great way to securely transfer information between parties. Because the JWT can be signed (eg using a public/private key pair), you can be sure who the sender is. Additionally, since the signature is calculated using the header and payload, you can also verify that the content has not been tampered with.

3. JWT and traditional session authentication comparison

3.1 Based on traditional session authentication

verification method:

Since the http protocol itself is a stateless protocol, it means that if the user provides the user name and password to the backend for user authentication, the user needs to authenticate the user again in the next request. Because according to the http protocol, the backend does not know which user initiated the request, so in order for the server to identify which user initiated the request, the server will store a copy of the user’s login information, save it in the Session, and return the sessionId to browser and save to cookie. In this way, when a user initiates an authentication request, the sessionId will be included so that the server can identify which user sent the request.

Expose the problem:

  • The session is stored in the server’s memory. When the number of authenticated users increases, the server’s overhead will increase significantly.

  • Since the authentication record is stored in the server’s memory, the user stored in the current server can only initiate a request on the server, so as to obtain authorized resources. In distributed applications, this will limit the ability of load balancing. Reduce scalability.

  • Because user identification is based on cookies, if cookies are intercepted, users will be vulnerable to cross-site request forgery attacks.

3.2 JWT-based authentication

  1. First the front-end sends the username and password to the back-end interface. This process is generally an HTTP POST request, and it is recommended to use SSL encrypted transmission (https protocol) to avoid sensitive information from being sniffed.
  2. After the backend successfully checks the username and password, the user’s id and other information are used as the JWT Payload (payload), and it is Base64 encoded and spliced ​​with the header and signed to form a JWT (Token). The resulting JWT is a string in the form of header.payload.signature.
  3. The back-end returns the JWT string to the front-end as the result of successful login. The front-end can save the returned result in localStorage or sessionStorage. When logging out, the front-end deletes the saved JWT.
  4. The front end puts the JWT in the Authorization in the HTTP Header on every request.
  5. After the backend receives the frontend request, it will check whether it exists, and if it exists, it will verify the validity of the JWT. (Check whether the signature is correct, whether the Token has expired, whether the recipient of the Token is yourself, etc.)
  6. After the verification is passed, the backend uses the user information contained in the JWT to perform other logical operations and returns the corresponding results.

JWT advantages:

  • Concise: It can be sent via URL, POST parameters or in HTTP headers, because the amount of data is small and the transfer speed is fast.
  • Self-contained: The payload contains the information required by all users, avoiding multiple queries to the database.
  • Because the Token is stored on the client in the form of JSON encryption, JWT is cross-language, and in principle, any web form is supported.
  • There is no need to save session information on the server side, which is especially suitable for distributed microservices.

4. Structure of JWT

JWT is a token string, the structure is xxx.yyy.zzz, it consists of the following three parts, the middle is connected with the .number

  • header header
  • payload payload
  • signature

4.1 Header

4.2 Payload

The second part of the token is the payload, which contains the claims. Claims are claims about entities (usually users) and other data. The declaration consists of three parts:

  • registered claims in the annotation

    statement describe
    iss JWT issuer
    sub Users of the JWT
    aud The party receiving the JWT
    exp The expiration time of the JWT, which must be greater than the issuance time
    nbf Defines the time before which the JWT will not take effect
    iat When the JWT was issued
    jti The unique identifier of the JWT is mainly used as a one-time token to avoid replay attacks

    * public claims

    The public statement can add any information, generally add user-related information or other necessary information for business needs. But adding sensitive information is not recommended as this part can be decoded on the client side.

  • private claims

    Private declarations are declarations jointly defined by providers and consumers, and it is generally not recommended to store sensitive information

After Base64 encoding the payload, it becomes the second part of the JWT.

Information Security Issues:

Since Base64 is an encoding and is reversible, no sensitive data (such as passwords) should be added to the JWT payload. Therefore, JWT is suitable for passing some non-sensitive information to Web applications. It is often used to design user authentication and authorization systems, and to implement single sign-on for Web applications.

4.3 Signature

The last part of the signature of the token is to sign the above two parts of 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. The signature is then generated according to the following formula using the signature algorithm specified in the header (HMAC SHA256 by default).

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

After the signature is calculated, the three parts of header, payload and signature are combined into a string, and each part is .separated by to form a complete JWT.

The role of the signature:

The last step of the signing process is actually to sign the header and payload content to prevent the content from being tampered with. If someone tampered with the content of the header and payload after decoding, and combined the previous signature after encoding to form a new JWT, the server will judge the signature formed by the new header and payload after receiving the JWT. Whether the signature attached to the new JWT is consistent, and the result is definitely not the same.

Base64URL algorithm:

The Base64URL algorithm is similar to the Base64 algorithm. The JWT as the token can be placed in the URL (eg api.example/?token=xxx). The three characters used in Base64 +, /, =have special meaning in URL, so if JWT needs to be used in URL, it is not suitable. And Base64URL replaces them, =removes , -replaces +with , and _replaces /, thus avoiding this problem.

5. How to use JWT

The main thing to use with JWT is to master the information in generating the token, validating the token and getting the token. Next, we will introduce how to use JWT in Java.

  1. import dependencies

    <dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.4.0</version>
    </dependency>

  2. generate token

    HashMap< String , Object > map = new HashMap<>(); // used to set header information</p> <p>Calendar instance = Calendar.getInstance(); // Get the current calendar instance.add(Calendar.DATE, 7 ); // Set the calendar to the current 7 days later Date date = instance.getTime(); // Convert the calendar to Date</p> <p>// create token JWTCreator.Builder builder = JWT.create(); String token = builder.withHeader(map) // header .withClaim("userId", 2001) // payload .withExpiresAt(date) // set expiration time.sign (Algorithm.HMAC256( "!@#SDA$!@#" )); // signature</p> <p>System.out.println(token);

    • JWT.create()used toCreate a JWT constructor to set the data of the JWT before encoding. The return value is JWTCreator.Builder.
    • JWTCreator.BuilderThe withClaim()method is used forSet the payload part of the JWT, has two parameters, the first parameter is the name of the string type, and the second parameter is the value of the Boolean/Integer/Long/Double/String/Date type.
    • JWTCreator.BuilderThe withExpiresAt(Date)method is used forSet the expiration time of the JWT, the parameter is Date.
  3. Parse data based on token and signature

    // token to be verified String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2NTkzNDgwMjAsInVzZXJJZCI6MjAwMSwidXNlcm5hbWUiOiLlsI_mmI4ifQ.5Kmd5QLqgEYkAUh5m2Y22UPjlsH2jrrd to be verified</p> <p>// Create JWT Verifier JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256( "!@#SDA$!@#" )).build(); DecodedJWT decodedJWT = jwtVerifier.verify(token);</p> <p>System.out .println ( decodedJWT.getClaim ( "userId" ).asInt()); // Get the claim's userId System.out .println ( decodedJWT.getClaim ( "username" ).asString()); // Get the claim username System.out .println ( decodedJWT.getExpiresAt ()); // Get expiration time</p> <p>System.out .println ( decodedJWT.getHeaderClaim ( "typ" ).asString()); // get the tpy of the header System.out .println ( decodedJWT.getHeaderClaim ( "alg" ).asString()); // get header alg</p> <p>System.out .println ( decodedJWT.getType ()); // get the tpy of the header System.out .println ( decodedJWT.getAlgorithm ()); // get the alg of the header</p> <p>System.out .println ( decodedJWT.getHeader ()); // Get the encoded header System.out .println ( decodedJWT.getPayload ()); // Get the encoded payload System.out .println ( decodedJWT.getSignature ( )); // Get the encoded signature System.out .println ( decodedJWT.getToken ()); // Get the encoded token

    • JWT.require(Alogrithm).builder()The method is used to create a JWT validation object and the return value is JWTVerifier. The encryption algorithm and key used by Alogrithm should be the same as those used when the token was created, otherwise an exception will be reported.
    • JWTVerifierThe verify(token)method can be used to verify the token. If it does not match, an exception will be reported. If it is correct, the decoded token will be returned.
    • DecodeJWTThe getClaim()method can get the specified declared parameter, but get the reference value, and also convert the reference value to the real value of the corresponding type through the asInt/asLong/asDouble/asString/asBoolean/asDate method.
    • DecodeJWTThe getExpiresAt()method can get the expiration time of the token.
    • DecodeJWTThe getType()method is able to get the typ of the header.
    • DecodeJWTThe getAlgorithm()method is able to get the alg of the header.

The above is the most basic operation of Java combined with JWT.

Common exception information:

abnormal describe
SignatureVerificationException Signature inconsistency exception
TokenExpiredException Token expired exception
AlgorithmMismatchException Algorithm mismatch exception
InvalidClaimException Invalid payload exception

6. Encapsulate the JWT tool class

public class JWTUtils {

    private static final String SECRET = "#$#fdas!%";

    /**
     * Generate token
     */
    public static String getToken(HashMap<String, String> map){
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.DATE, 7 ); // default 7 days to expire

        // Create JTW builder
        JWTCreator.Builder builder = JWT.create();
        // payload
        map.forEach((k, v)->{
            builder.withClaim(k, v);
        });

        // Specify the token expiration time
        builder.withExpiresAt(instance.getTime());

        // sign
        String token = builder.sign(Algorithm.HMAC256(SECRET));
        return token;
    }

    /**
     * Verify and get token information
     */
    public static DecodedJWT verify(String token){

        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(SECRET)).build();
        DecodedJWT decodedJWT = jwtVerifier.verify(token);
        return decodedJWT;
    }
}

7. The interceptor verifies the token

When the token needs to be verified, each method must receive the token parameter and verify the received token, which will lead to code redundancy and inflexibility.

In order to solve this problem, you need to use interceptors for optimization. Proceed as follows:

  1. Create the JWTInterceptor class under the interceptors package and configure it as follows:

    public class JWTInterceptor implements HandlerInterceptor {</p> <pre><code>@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HashMap<String, Object> map = new HashMap<>(); // Get the token in the request header String token = request.getHeader( "token" ); try { DecodedJWT decodedJWT = JWTUtils.verify(token); // verify token return true ; // release request } 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" , "The token algorithm is inconsistent!" ); } catch (Exception e) { e.printStackTrace(); map .put( "msg" , "token is invalid!" ); } map .put( "state" , false ); // set the status code // convert map to json String json = new ObjectMapper().writeValueAsString( map ); response.setContentType("application/json;charset=utf-8"); response.getWriter().print(json); return false ; } </code></pre> <p>}

  2. Create the InterceptorConfig class under the config package and configure it as follows:

    @Configuration
    public class InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new JWTInterceptor())
    .addPathPatterns( "" ) // intercepted paths
    .excludePathPatterns( "" ); // released paths
    }
    }

Leave a Comment

Your email address will not be published. Required fields are marked *