Use of JWT in SDU-PTA projects

2021SC@SDUSC

One of the issues to consider in a web project is how to authenticate users.

User Authentication Method

Traditional session authentication

Internet services cannot be separated from user authentication. The general process is as follows.

1. Users send user names and passwords to the server.

2. After the server is authenticated, save relevant data in the current session, such as user role, login time and so on.

3. The server returns a session_to the user Id, which writes the user's Cookie.

4. Each subsequent request of the user passes through a Cookie and session_ The ID is returned to the server.

5. Server receives session_id, find previously saved data, and know the user's identity.

We know that the http protocol itself is a stateless protocol, which means that if a user provides a username and password to our application for user authentication, the user will have to authenticate the user again at the next request.

Because we don't know which user is making the request according to the http protocol, in order for our application to recognize which user is making the request, we can only store a user's login information on the server, which is passed to the browser in response, telling the client to save the SessionID in the cookie. So that the next request is sent to our app, so that our app can identify which user the request comes from, which is traditional session-based authentication.

However, session-based authentication makes it difficult to extend the application itself. With the increase of different client users, independent servers can no longer host more users. At this time, the problem of session-based authentication application will be exposed.

Problems exposed by session-based authentication

  • session: After each user has been authenticated by our application, our application will record once on the server side to facilitate the user's next request for authentication. Generally, sessions are stored in memory. As more authenticated users increase, the overhead on the server side will increase significantly.
  • Horizontal expansion difficulty: After user authentication, the server records the authentication. If the authentication record is saved in memory, this means that the next request of the user must also be made on this server in order to get authorized resources. This limits the load balancer's ability in distributed applications. This also means limiting the scalability of the application.
  • CSRF: Because cookies are used for user identification, if a cookie is intercepted, users will be vulnerable to cross-site request forgery.

Front End Security Series (2): How to prevent CSRF attacks?

token-based authentication mechanism

The token-based authentication mechanism is stateless, similar to the http protocol. It does not need to preserve user authentication information or session information on the server side. This means that applications based on token authentication do not need to consider which server the user is logged on to, which facilitates the expansion of applications.

Technological process:

  1. Users use accounts, passwords to request servers
  2. Server authenticates user information
  3. The server sends a token to the user through authentication
  4. The client stores the token and attaches the token value with each request
  5. The server validates the token value and returns the data

JWT

What is JWT and its principles

JWT(JSON Web Token) is an open standard (RFC 7519) that defines a compact, self-contained (containing some session information internally).

Used to securely transfer information between parties as a JSON object**. This information can be verified and trusted** because it is digitally signed.

The principle of JWT is that after server authentication, a JSON object is generated and sent back to the user as follows.

 {
   "Full name": "Zhang San",
   "role": "Administrators",
   "Expiration Time": "2018 0:0 on July 1, 2001"
 }

The JSON object will be sent back later when the user communicates with the server. The server identifies users solely on this object. To prevent users from tampering with data, the server will sign this object when it is generated (see later).

The server does not hold any session data, that is, the server becomes stateless, which makes it easier to extend.

Composition of JWT

JWT consists of three parts, with dots (.) between them. Connect. These three parts are:

  • Header
  • Payload
  • Signature

The three are Base64 URL encoded strings.

Write one line, just like the following.

Header.Payload.Signature

The specific form is as follows:

base64(header).base64(payload).base64( HS256(base64(header) + "." + base64(payload), secret) )

Header

The header of jwt consists of two parts of information:

  • Type: declare type, here is jwt
  • alg: algorithms that declare encryption usually use HMAC SHA256 directly
{
  "typ":"jwt",
  "alg":"HS256"
}

Base64 encoding the header information to get the first part of the information.

Payload

Load is the place where valid information is stored and contains declarations (requirements). There are three types of declarations:

  • registered claims: a declaration registered in a standard. Here is a set of predefined declarations that are not mandatory, but recommended
  • public claims: a public statement
  • Private claims: private claims

Declaration registered in the standard (recommended but not mandatory):

  • iss: jwt issuer
  • Sub: Users targeted by JWT
  • aud: the party receiving jwt
  • exp: jwt expiration time, which must be greater than the issuance time
  • nbf: Define when this jwt will not be available
  • Iat: Issuance time of JWT
  • Jti: The unique identity of jwt, primarily used as a one-time token to avoid replay attacks

Public statement:

A public statement can add any information, typically information about the user or other business needs. However, adding sensitive information is not recommended because it can be decrypted on the client side.

Private Statement:

Private declarations are declarations defined jointly by providers and consumers. It is generally not recommended to store sensitive information because base64 is symmetrically decrypted, meaning that this part of the information can be categorized as plain text.

Base64 encryption of Payload gives you the contents of Part Two of JWT.

Signature

The third part of JWT is a visa information, which consists of three parts:

  • Header (after base64)
  • Payload (after base64)
  • secret

The Signature part is the signature of the first two parts to prevent data tampering.

The third part requires the use of base64 encrypted header and base64 encrypted payload. Connect the strings that make up the JWT, then combine the encryption with salt secret as declared in the header, and make up the third part of the JWT.

Be careful:
secret is stored on the server side, and the signature generation of JWT is also on the server side. Sec is used to issue JWT and verify JWT, so it is the private key of your server side and should not be exposed in any scenario.

Once the client knows this secret, it means the client can self-sign the JWT.

Several features of JWT

(1) JWT is not encrypted by default, but it can also be encrypted. After the original Token is generated, it can be encrypted again with the key. (???)

(2) Secret data cannot be written to JWT without encryption.

(3) JWT can be used not only for authentication but also for exchanging information. Effective use of JWT can reduce the number of times the server queries the database.

(4) The biggest disadvantage of JWT is that because the server does not save the session state, it is not possible to invalidate a token or change the permissions of a token during use, that is, once a JWT is issued, it will always be valid until it expires unless the server deploys additional logic.

(5) JWT itself contains authentication information, and once it is compromised, anyone can get all the rights of the token. To reduce theft, the validity period of JWT should be set fairly short. For some of the more important rights, users should be authenticated again when using it.

(6) In order to reduce theft, JWT should not use HTTP protocol for plain code transmission, but use HTTPS protocol for transmission.

Using JWT based on jjwt

@SpringBootTest
class LearnJwtApplicationTests {

    private static final String secret = "secret";
    private static final String secretBase64;

    static {
        secretBase64 = Base64.getEncoder().encodeToString(secret.getBytes());
    }

    @Test
    void testJwtCreate() {
        JwtBuilder jwtBuilder = Jwts.builder();
        String token = jwtBuilder
                // header
                .setHeaderParam("alg", "HS256")
                .setHeaderParam("typ", "JWT")
                // payload
                .claim("sub", "1234567890") // Standard Statement
                .claim("iat", 1516239022)   // Standard Statement
                .claim("name", "John Doe")  // Custom declaration
                // signature
                .signWith(SignatureAlgorithm.HS256, secretBase64)
                .compact();

        System.out.println();
        System.out.println(token);
    }

    @Test
    void testJWTParse() {
        String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." +
//                "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ."+
                "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9vIiwiaWF0IjoxNTE2MjM5MDIyfQ." +
                "XbPfbIHMI6arZ3Y922BhjWgQzWXcXNrz0ogtVhfEd2o";
        JwtParser jwtParser = Jwts.parser();
        Jws<Claims> claimsJws = jwtParser.setSigningKey(secretBase64).parseClaimsJws(token);
        JwsHeader header = claimsJws.getHeader();
        Claims body = claimsJws.getBody();
        String signature = claimsJws.getSignature();

        System.out.println("----- header -----");
        System.out.println("alg : " + header.getAlgorithm());
        System.out.println("typ : "+header.getType());

        System.out.println("----- body -----");
        System.out.println("sub : "+body.getSubject());
        System.out.println("iat : "+body.getIssuedAt());
        System.out.println("name : "+body.get("name"));
        System.out.println("id : "+body.getId());

        System.out.println("----- signature -----");
        System.out.println("signature : " + signature);
    }

}

How JWT is used

Front end

Front End Storage JWT

import axios from 'axios';
axios.post(
	"/user/login",
	{
		username: this.userId,
		password: this.userPsw,
	},
).then((res)=>{
	if (res.status === 200) {
		localStorage.setItem("token", res.data.data);
		this.$message({
			title: "Login Successful",
			message: "Login successful! Jumping pages for you...",
			type: "success",
			duration: 1000,
			showClose: false,
			onClose: () => {
				this.$store.dispatch("setNoToken", false);
				this.$router.go(-1);
			},
		});
	} else {
		this.$alert(
			`error code ${res.data.code}: ${res.data.message}`,
			"Logon Failure",
			{
				type: "error",
			}
		);
	}
});

Request to carry JWT

The client receives the JWT returned by the server, which can be stored in a Cookie or in a local store.

Thereafter, every time a client communicates with the server, it takes this JWT with it. You can put it in a Cookie and send it automatically, but it doesn't cross domain. Cookies cross-domain those things ), so it's better to put it in the Authorization field of the header information of the HTTP request.

Authorization: Bearer <token>

Another option is to place the JWT in the body of the POST request when crossing domains.

import axios from 'axios';
axios.post(
	'/test', // url
	{num: 1}, // data
	{
		headers: {
			Authorization: `Bearer ${token}`
		}
	}, // options
).then((res)=>{
	console.log(res);
})

Front-end extracts information carried by JWT

As mentioned earlier, JWT's payload module can carry some non-sensitive information necessary for business logic. Therefore, the front end needs to be able to parse out the JWT string.
For example, in sduoj, you need to determine whether a user's login information is legal, and one of the criteria is whether the JWT is out of date. This logic exists in the server code at the back end and also in the front end.
In front-end code logic, judgment is made only when the user first establishes a session with the front-end page of sduoj.

// First determine if a token field exists in the localStorage
if(localStorage.getItem("token") !== null) {
	/**
	 * Note that since the localStorage is taken out as a string
	 * When the token field is an empty string, javascript converts it to a Boolean value of false.
	 * So it needs to be increased!== null makes the judgment.	
	 */
	// Using the module loader, jsonwebtoken installed by yarn will be loaded in
	let jwt = require("jsonwebtoken"); 
	// Resolving JWT strings using jsonwebtoken
	const TOKEN = jwt.decode(localStorage.getItem("token"));
	/**
	 * Parsing through jsonwebtoken will result in a JSON object that contains the clear text of the payload insensitive part
	 * Extract the expiration time of jwt under exp field
	 * Note: the time unit of jwt is three orders of magnitude larger than that of javascript, java, and other languages
	 * So you need to multiply the resolved time by 1000
	 */
	let exp = TOKEN.exp * 1000;
	// Determine if the current time has exceeded token's expiration time
	if (exp <= new Date().getTime()) {
		this.$message({
			message: "Your identity has expired, please login again",
			type: "warning",
			duration: 1000,
			onClose: () => {
				/**
				 * Perform two tasks when message prompt is closed
				 * 1.Remove expired token fields from local Storage
				 * 2.Jump to login interface for user
				 */
				localStorage.removeItem("token");
				this.$router.push("/login");
			},
		});
	}
} else {
	// Automatically jump to the login interface when the token field does not exist in the localStorage
	this.$router.push("/login");
}

Backend (SpringBoot)

Reference Links

https://jwt.io/

https://jwt.io/introduction/

http://www.jsons.cn/allencrypt/

https://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html

https://blog.csdn.net/qq_53126706/article/details/120925322?spm=1001.2014.3001.5501

https://www.jianshu.com/p/6623416161ff

https://www.jianshu.com/p/4a124a10fcaf

Keywords: Front-end server http Sandbox

Added by mirana on Mon, 03 Jan 2022 19:41:20 +0200