JSON Web Token (JWT) is a very lightweight specification. This specification allows us to use JWT to deliver secure and reliable information between the user and the server.

Let’s imagine a scenario. When user A follows user B, the system sends an email to user B with A link “click here to follow user A”. The address of the link could look something like this

https://your.awesome-app.com/make-friend/?from_user=B&target_user=A
Copy the code

So the URL above is basically describing this by URL and of course there’s a downside to this, which is that user B has to be logged in first. Is it possible to simplify the process so that user B can complete the operation without logging in? JWT allows us to do this.

The composition of JWT

A JWT is essentially a string made up of three parts, a header, a payload, and a signature.

Payload

Let’s start by describing the add friend operation above as a JSON object. Some additional information is added to help future servers that receive the JWT understand the JWT.

{
    "iss": "John Wu JWT",
    "iat": 1441593502,
    "exp": 1441594722,
    "aud": "www.example.com",
    "sub": "[email protected]",
    "from_user": "B",
    "target_user": "A"
}
Copy the code

The first five fields are defined by JWT standards.

  • Iss: The issuer of the JWT
  • Sub: The user for which the JWT is intended
  • Aud: The party receiving the JWT
  • Exp (Expires): When does it expire, in this case a Unix timestamp
  • Iat (issued at): When was it issued

These definitions can be found in the standard.

Base64 encoding the above JSON object yields the following string. And this string we’re going to call the Payload of JWT.

eyJpc3MiOiJKb2huIFd1IEpXVCIsImlhdCI6MTQ0MTU5MzUwMiwiZXhwIjoxNDQxNTk0NzIyLCJhdW
Copy the code

If you use Node.js, you can use the node.js package base64URL to get this string.

Fact: Base64 is a code, which means it can be translated back to its original form. It is not an encryption process.

var base64url = require('base64url') var header = { "from_user": "B", "target_user": "A"} console.log(base64URL (json.stringify (header))) eyJpc3MiOiJKb2huIFd1IEpXVCIsImlhdCI6MTQ0MTU5MzUwMiwiZXhwIjoxNDQxNTk0NzIyLCJhdWQiOiJ3d3cuZXhhbXBsZS5jb20iLCJzdWIiOiJqcm9j a2V0QGV4YW1wbGUuY29tIiwiZnJvbV91c2VyIjoiQiIsInRhcmdldF91c2VyIjoiQSJ9Copy the code

Header

The JWT also needs a header that describes the most basic information about the JWT, such as its type and the algorithm used to sign it. This can also be represented as a JSON object.

{
  "typ": "JWT",
  "alg": "HS256"
}
Copy the code

Here, we show that this is a JWT, and that the signature algorithm we use (mentioned later) is the HS256 algorithm.

It is Base64 encoded as well, and then the string becomes the JWT Header.

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
Copy the code

Signature (signature)

Use periods for both of the above encoded strings. Join together (head in front) and form

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcm9tX3VzZXIiOiJCIiwidGFyZ2V0X3VzZXIiOiJBIn0
Copy the code

This part of the process is reflected in the node-JWS source code

Finally, we use HS256 algorithm to encrypt the above concatenated string. When encrypting, we also need to provide a secret. If we use MyStar as the key, then we can get our encrypted content

rSWamyAYwuHCo7IFAgd1oRpSP7nzL7BF5t7ItqpKViM
Copy the code

This part is also called the signature.

Finally, this part of the signature is also spliced at the end of the signed string, and we get the complete JWT

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcm9tX3VzZXIiOiJCIiwidGFyZ2V0X3VzZXIiOiJBIn
Copy the code

So, we can change the URL in the email

https://your.awesome-app.com/make-friend/?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJm
Copy the code

Now you can add friends safely!

Wait, we’re bound to have some problems:

  1. What is the purpose of signing?
  2. Base64 is encoding and reversible, so wouldn’t my information be exposed?

Let me explain them to you one by one.

Purpose of signature

The final step in the signing process is actually signing the header and payload contents. In general, encryption algorithms always produce different outputs for different inputs. For two different inputs, the probability of producing the same output is extremely small (probably less than my chances of becoming the richest person in the world). So, let’s take “different inputs produce different outputs” as a necessary event.

Therefore, if the contents of the header and payload are decoded and modified, then the signature of the new header and payload will be different from the previous signature. And if you don’t know the key the server used to encrypt it, the signature is bound to be different.

Upon receiving the JWT, the server application first re-signs the contents of the header and payload using the same algorithm. So how does the server application know which algorithm we’re using? Don’t forget that we already specify our encryption algorithm with the ALG field in the JWT header.

If the server uses the same method to sign the header and payload again and finds that the signature calculated by the server is different from the received signature, then the Token has been modified. We should reject the Token and return an HTTP 401 Unauthorized response.

Information exposed?

Yes.

Therefore, in JWT, no sensitive data should be added to the payload. In the example above, we are transferring the User ID of the User. This value is actually not sensitive and is generally known to be safe.

But things like passwords cannot be placed in JWT. If you put a user’s password in JWT, a malicious third party can quickly learn your password through Base64 decoding.

Application scenarios of JWT

As we can see, JWT is suitable for delivering non-sensitive information to Web applications. Such as completing the friending action mentioned above, or placing an order, etc.

There are eight steps for user authentication

The so-called user Authentication (Authentication), is to allow users to log in, and in the following period of time to allow users to access the website to use their account, without the need to log in again mechanism.

Tip: Don’t confuse user authentication with Authorization. User authorization refers to specifying and allowing users to use their own rights, such as Posting, managing a site, etc.

First, the server application (hereinafter referred to as the “application”) lets users send their user name and password to the server’s interface via a Web form. This process is typically an HTTP POST request. The recommended method is ssl-encrypted transmission (HTTPS protocol) to avoid sensitive information being sniffed.

Next, the application and database check the username and password.

After the user name and password are verified successfully, the application regards the user ID (user_id in the figure) as an attribute of the JWT Payload, concatenates the user ID with the header, and signs the JWT. Here JWT is a string like ll.zzz.xxx.

The application returns the JWT string to the user as part of the request Cookie. Note that the HttpOnly attribute must be used here to prevent cookies from being read by JavaScript, thus avoiding cross-site scripting attacks (XSS attacks).

Each time the user accesses the application, the application will receive a Cookie containing JWT before the Cookie expires or is deleted. The application can then extract the JWT from the request.

The application checks the validity of the JWT through a series of tasks. For example, check whether the signature is correct. Check whether the Token expires. Check whether the recipient of the Token is itself (optional).

After confirming that the JWT is valid, the JWT decodes Base64 (which was probably done in the previous step) and reads the user’S ID value, the user_id attribute, in the Payload. The user ID is 1025.

The application fetches information about the user with ID 1025 from the database, loads it into memory, and performs a series of low-level logic initializations like ORM.

The application responds to user requests.

The storage ID is different from that in Session mode

The biggest drawback to storing user ids in the Session mode is that it takes up a lot of server memory and may store a lot of state for larger applications. Generally speaking, large applications also need to use some KV databases and a series of caching mechanisms to realize Session storage.

JWT disperses the user state to the client, which can significantly reduce the memory pressure on the server. In addition to the user ID, other user-related information can be stored, such as whether the user is an administrator and which bucket the user belongs to (see “All you need to know about A/B testing basics” /2015/08/27/introduction-to-ab-testing/).

While the JWT approach imposes some computational pressures on the server (such as encryption, encoding, and decoding), these pressures may be of equal importance to disk I/O. You need to use data in different scenarios.

Single sign-on (sso)

Session mode stores user ids. At first, user sessions are stored on only one server. If a site has multiple subdomain names, each subdomain name must correspond to at least one server. For example:

  • www.taobao.com
  • nv.taobao.com
  • nz.taobao.com
  • login.taobao.com

Therefore, in order to realize that the Session can still be obtained from other sub-domain names after logging in at login.taobao.com, we need to synchronize the Session on multiple servers.

JWT does not have this problem because the user’s state is already transmitted to the client. Therefore, we only need to set the domain containing JWT cookies to a top-level domain, for example

Set-Cookie: jwt=lll.zzz.xxx; HttpOnly; max-age=980000; domain=.taobao.com
Copy the code

Note that domain must be set to a dot plus top-level domain, i.e..taobao.com. In this way, taobao.com and *. Taobao.com can receive this Cookie and get JWT.


If you are not sure about single sign-on, you can take a look at my study notes on single sign-on. Follow the official number: North Travel learning Java, reply 706 and you can get it.