JWT practice of Java security verification

Time:2020-7-24

First, give the official documents of JWT

What is JWT?

JSON web token (JWT) is an open standard (RFC 7519), which defines a compact and independent way to securely transfer information between parties as JSON objects.

When should JWT be used?

  • to grant authorization: This is the most common way to use JWT. Once the user logs in, each subsequent request will include a JWT, allowing the user access to the routes, services, and resources allowed by the token. Single sign on is a feature that is widely used in JWT today because of its low overhead and ease of use in different domains.
  • information switching: JSON web token is a good way to safely transfer information between parties. Because JWT can verify the sender’s identity by signing (using a public / private key pair). In addition, since the signature is calculated using the header and payload, you can verify that the content has not been tampered with.

JWT token structure

JWT token is composed of header, payload and signature. Each part is separated by a dot. The common form isxxxxx.yyyyy.zzzzzEach part is described in detail below.

Header

The header usually consists of two parts: the type of token, namely JWT, and the signature algorithm used, such as HMAC sha256 or RSA.

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

This JSON is encoded asBase64UrlTo form the first part of JWT.

Payload

The second part of JWT is the payload, which contains claims. Declarations contain entities (usually users) and other custom information. There are three types of declarations:registered, publicandprivate claims

  • registered claims: This is a set of predefined declarations, not mandatory, but is recommended to provide a useful, interoperable set of declarations. These include:iss(the issuer),exp(expiration time),sub(Theme),aud(audience).

Please note that: the declaration name is only three characters, because JWT needs to be concise.

  • public claims: these can be defined at will by those who use JWT. However, to avoid conflicts, they should be defined in the IANA JSON web token register or as URIs that contain the anti conflict namespace.
  • private claims: These are custom claims that are used to share information between parties that agree to use them, which is neither registered nor publicly declared.

Payoad passed byBase64UrlCode to form the second part of JWT.

Please note that:For JWT token, it can be tampered with, but anyone can read it. Do not put secret information in JWT payloads or header elements unless encrypted.

Signature

Signature is composed of header encrypted by base64url, payload, encrypted by algorithm specified in header, and secret.

If you want to use the hmacsha256 algorithm, the signature is created as follows:

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

Signature is used to verify that the message has not been changed in the process, and, in the case of a token signed with a private key, it can also verify the identity of the sender of the JWT.

summary

Finally, the three parts of JWT are separated by dots as a string, which can be easily passed in HTML and HTTP environments, and is better than the standard token based on XML(e.g. SAML)More concise.

JWT workflow

In authentication, the JSON web token is returned when the user successfully logs in using his credentials. Since the token is a credential, great care must be taken to prevent security issues. In general, you should not keep the token longer than required.

Whenever a user wants to access a protected route or resource, the user sends JWT to the corresponding address, usually in theAuthorizationHeader. The content of the request header should be as follows:

Authorization: Bearer <token>

In some cases, this can be a stateless authorization mechanism. The server’s protected routes will be checkedAuthorizationA valid JWT in the header, if present, allows the user to access the protected resource. If the JWT contains the necessary data, you can reduce the need to query the database for some operations, although this may not always be the case.

IfAuthorizationIf a token is sent in the header, cross domain resource sharing (CORS) will not be a problem because it does not use cookies.

The following figure shows how to get JWT and use it to access APIs or resources:

JWT practice of Java security verification

  1. An application or client sends an authorization request to the authorization server.
  2. After authorization is granted, the authorization server returns JWT to the application.
  3. Applications use JWT to access protected resources, such as APIs.

Please note that:With JWT, all the information contained in the token is made public to users or other parties, even if they cannot change it. So you should not put secret information in the token.

Java implementation in JWT

There are many Java implementations of JWT. How to check the official documents in detail.

The common ones are com.auth0 . Java JWT and io.jsonwebtoken.jjwt

Here I use jjwt as a demonstration because he has more stars in GitHub.

Maven dependence

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.10.5</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.10.5</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.10.5</version>
    <scope>runtime</scope>
</dependency>
<!-- Uncomment this next dependency if you want to use RSASSA-PSS (PS256, PS384, PS512) algorithms:
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.60</version>
    <scope>runtime</scope>
</dependency>
-->

Create token

    //Sample method to construct a JWT
    public static String createJWT(String id, String issuer, String subject, long ttlMillis) {

        //The JWT signature algorithm we will be using to sign the token
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);

        //We will sign our JWT with our ApiKey secret
        byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary( APP_ID + APP_SECRET);
        Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());

        //Let's set the JWT Claims
        JwtBuilder builder = Jwts.builder().setId(id)
                .setIssuedAt(now)
                .setSubject(subject)
                .setIssuer(issuer)
                .signWith(signatureAlgorithm, signingKey);

        //if it has been specified, let's add the expiration
        if (ttlMillis >= 0) {
            long expMillis = nowMillis + ttlMillis;
            Date exp = new Date(expMillis);
            builder.setExpiration(exp);
        }

        //Builds the JWT and serializes it to a compact, URL-safe string
        return builder.compact();

    }

Parsing token

     //Sample method to validate and read the JWT
    public static Claims parseJWT(String jwt) {

        //This line will throw an exception if it is not a signed JWS (as expected)
        Claims claims = Jwts.parser()
                .setSigningKey(DatatypeConverter.parseBase64Binary(APP_ID + APP_SECRET))
                .parseClaimsJws(jwt).getBody();
//        System.out.println("ID: " + claims.getId());
//        System.out.println("Subject: " + claims.getSubject());
//        System.out.println("Issuer: " + claims.getIssuer());
//        System.out.println("Expiration: " + claims.getExpiration());
        return claims;
    }

amongAPP_IDandAPP_SECRETIt can be customized to any value you want, but it can’t be too simple.

You will find that we do not set the header value when we use it again, because jjwt will set them automatically according to the signature algorithm or compression algorithm used for our convenience.

use

@Test
public void createJWT(){

    String jwt = JWTUtil.createJWT("1", "111", "admin", JWTUtil.DAY_TTL);
    System.out.println(jwt);
}

@Test
public void parseJWT(){
            Claims claims = JWTUtil.parseJWT("eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxIiwiaWF0IjoxNTQ4Mjk1NjQ0LCJzdWIiOiJhZG1pbiIsImlzcyI6IjExMSIsImV4cCI6MTU0ODMzODg0NH0.WRkyeG3MfVor02Ya4732fgGydXhtkkKSDwbxOIZ2i9Y");
    System.out.println(claims);
}

If you want to use the more complex functions of jjwt or other Java implementations, you can go to their corresponding GitHub to learn.