Token-based authentication: JSON Web Token (attached: Node.js project)
Using the Token-based authentication method, there is no need to store user login records on the server side. The approximate process is this:
- The client uses the username and password to request login
- The server receives the request to verify the user name and password
- After the verification is successful, the server will issue a Token, and then send the Token to the client
- After receiving the Token, the client can store it, such as in Cookie or Local Storage
- The client needs to bring the Token issued by the server every time it requests resources from the server
- The server receives the request and then verifies the token contained in the client request. If the verification is successful, it returns the requested data to the client
There are many ways to implement Token verification, and there are some standard methods, such as JWT, pronounced as: jot , which means: JSON Web Tokens. The JWT standard Token has three parts:
- header
- payload (data)
- signature
The middle is separated by a dot, and Base64 encoding is used, so the real Token looks like this:
eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJuaW5naGFvLm5ldCIsImV4cCI6IjE0Mzg5NTU0NDUiLCJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlfQ.SwyHTEx_RQppr97g4J5lKXtabJecpejuef8AqKYMAJc
Header
Each JWT token has a header, which is the header data. It contains the algorithm used, whether this JWT is signed or encrypted. The main thing is to explain how to deal with this JWT token.
The content contained in the header may vary according to the type of JWT. For example, an encrypted JWT must contain the encryption algorithm used. The only thing to include in the header is the attribute alg . If it is an encrypted JWT, the value of this attribute is the algorithm used for signature or decryption. If it is an unencrypted JWT, the value of this attribute should be set to none .
Example:
{ "alg": "HS256" }
This means that the algorithm used by this JWT is HS256. The above content has to be encoded in the form of base64url , so it becomes like this:
eyJhbGciOiJIUzI1NiJ9
Payload
Payload contains the specific content of Token. Some of these content are standard fields. You can also add other content as needed. The following are the standard fields:
- iss: Issuer, issuer
- sub: Subject, subject
- aud: Audience, audience
- exp: Expiration time, expiration time
- nbf:Not before
- iat: Issued at, issue time
- jti : JWT ID
For example, the following Payload uses the iss issuer and two standard fields of exp expiration time. There are also two custom fields, one is name and the other is admin .
{ "iss": "ninghao.net", "exp": "1438955445", "name": "wanghao", "admin": true }
After using base64url encoding, it becomes like this:
eyJpc3MiOiJuaW5naGFvLm5ldCIsImV4cCI6IjE0Mzg5NTU0NDUiLCJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlfQ
Signature
The last part of JWT is Signature. There are three parts in this part. First, use Base64-encoded header.payload, then encrypt it with an encryption algorithm. When encrypting, put in a Secret. This is equivalent to a password, which is stored secretly. On the server side.
- header
- payload
- secret
const encodedString = base64UrlEncode(header) + "." + base64UrlEncode(payload); HMACSHA256(encodedString, 'secret');
After processing, it looks like this:
SwyHTEx_RQppr97g4J5lKXtabJecpejuef8AqKYMAJc
The last token generated on the server and sent to the client looks like this:
eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJuaW5naGFvLm5ldCIsImV4cCI6IjE0Mzg5NTU0NDUiLCJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlfQ.SwyHTEx_RQppr97g4J5lKXtabJecpejuef8AqKYMAJc
After the client receives this Token, it will be stored, and it will be taken with it when it sends a request to the server next time. The server receives this Token, then verifies it, and after passing it, it returns the resource that the client wants.
JWT issuance and verification
To implement a Token-based authentication method based on JWT in your application, you can first find a feature package for issuing and verifying JWT. No matter what programming language, system, or framework your back-end application uses, you should be able to find packages that provide similar functionality.
Below we are in a Node.js project, in the simplest way to demonstrate the method of issuing and verifying JWT.
Project code : https://github.com/ninghao/jwt-demo
Prepare the project
Prepare a simple Node.js project:
cd ~/desktop mkdir jwt-demo cd jwt-demo npm init -y
Install the function package for issuing and verifying JWT. I use jsonwebtoken . Install this package in the project:
npm install jsonwebtoken --save
Issue JWT
Just add a .js file to the project, such as index.js , and add the following code to the file:
const jwt = require('jsonwebtoken') // Token data const payload = { name:'wanghao', admin: true } // key const secret = 'ILOVENINGHAO' // Issue Token const token = jwt.sign(payload, secret, { expiresIn: '1day' }) // Output the issued Token console.log(token)
Very simple, just use the jwt.sign function provided in the jsonwebtoken just installed for the project to issue a token. The sign method requires three parameters:
- Playload : some data to be included in the issued token.
- secret : The key used to sign the token. This key is also needed when verifying the token.
- options : some other options.
Under the command line, use the node command to execute the index.js file ( node index.js ) in the project, and the token issued by the application will be output :
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlLCJpYXQiOjE1MjkwMzM5MDYsInR5cCI6IkpXVCJ9.eyJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlLCJpYXQiOjE1MjkwMzM5MDYsImV4cCI6MTUWyOTEy78PUCIMTUq2NQN0KIO
The Token content above is not encrypted, so if you use some JWT decoding functions, you can see the content contained in the Token. The content consists of three parts, like this:
// header { "alg": "HS256", "typ": "JWT" } // payload { "admin": true, "iat": 1529033906, "name": "wanghao", "exp": 1529120306 } // signature DctA2QlUCrM6wLWkIO78wBVN0NLpjoIq4T5B_2WJ-PU
Assuming that the user passes some kind of authentication, you can use the Token issuance function above to issue a Token for the user. Generally, the client will save it in Cookie or LocalStorage.
The next time the user requests a protected resource from our application, he can bring the Token we issued to it in the request. The back-end application receives the request and checks the signature. If the verification passes, it is confirmed that the Token is issued by us. Then the user can respond to the resources he needs.
Verify JWT
To verify the effectiveness of the JWT, make sure that the user's JWT is issued by us. First, we must obtain the user's JWT Token, and then use the jwt.verify method to verify. This method is provided in the jsonwebtoken package of Node.js. In other application frameworks or systems, you may find a similar method to verify JWT.
Open the index.js file of the project and add a few lines of code:
// Verify Token jwt.verify(token, 'bad secret', (error, decoded) => { if (error) { console.log(error.message) return } console.log(decoded) })
Tell the verify method the Token data to be verified and the key used when issuing the Token . There are two parameters in a callback. Error represents an error, and decoded is the decoded Token data.
carried out:
node ~/desktop/jwt-demo/index.js
Output:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoid2FuZ2hhbyIsmFkbWluIjp0cnVlLCJpYXQiOjE1MjkwMzQ3MzMsImV4cCy6o invalid signature
Note that an invalid signature is output, indicating that the signature in the Token is incorrect. This is because the key provided by our team leader’s verify method is not the one used when issuing the Token. Modify it like this:
jwt.verify(token, secret, (error, decoded) => { ...
Run again, it will output similar data:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlLCJpYXQiOjE1MjkwMzUzODYsImV4cCI6MTUyOTEyMTc4Nn0.mkNrt4TfcfmP22xd3C_GQn8qnUmlB39dKT9SpIBTBGI { name: 'wanghao', admin: true, iat: 1529035386, exp: 1529121786 }
RS256 algorithm
The HS256 algorithm is used by default when issuing and verifying the Token. This algorithm requires a key (password). We can also use the RS256 algorithm to issue and verify JWT. This method allows us to separate issuance and verification. A key is used for issuance and a public key is used for verification. That is, where there is a public key, only verification can be done, but JWT cannot be issued.
Create a new directory under the project, which can store the key and public key files to be generated.
cd ~/desktop/jwt-demo mkdir config cd config
Key
First generate a key file:
ssh-keygen -t rsa -b 2048 -f private.key
Public key
Based on the key generated above, create a corresponding public key:
openssl rsa -in private.key -pubout -outform PEM -out public.key
Issue JWT (RS256 algorithm)
When using the RS256 algorithm to issue JWT, you need to read the content of the created key file from the file system.
const fs = require('fs') // Obtain the key needed to issue JWT const privateKey = fs.readFileSync('./config/private.key')
Signing still uses the jwt.sign method, but the algorithm used is RS256 in the option parameters:
// Issue Token const tokenRS256 = jwt.sign(payload, privateKey, { algorithm: 'RS256' }) // Output the issued Token console.log('RS256 algorithm:', tokenRS256)
Verify JWT (RS256 algorithm)
To verify the JWT issued using the RS256 algorithm, you need to read the contents of the public key file on the file system. Then use jwt 's verify method to verify.
// Obtain the public key needed to verify the JWT const publicKey = fs.readFileSync('./config/public.key') // Verify Token jwt.verify(tokenRS256, publicKey, (error, decoded) => { if (error) { console.log(error.message) return } console.log(decoded) })