JSON Web Token (JWT) is a very lightweight specification. This specification allows us to use JWT to pass 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 saying “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

So the URL above is basically a URL that describes this and of course there’s a downside to that, 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 just that.

The composition of JWT

A JWT is really just a string that consists of three parts, the header, the payload, and the 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"
}

The first five fields are defined by the JWT standard.

  • ISS: 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, here’s 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. This string is what we call the JWT Payload.

eyJpc3MiOiJKb2huIFd1IEpXVCIsImlhdCI6MTQ0MTU5MzUwMiwiZXhwIjoxNDQxNTk0NzIyLCJhdW

If you’re using Node.js, you can use the Node.js package base64url to get the string.

Fact: Base64 is an encoding, that is, 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))) // output: eyJpc3MiOiJKb2huIFd1IEpXVCIsImlhdCI6MTQ0MTU5MzUwMiwiZXhwIjoxNDQxNTk0NzIyLCJhdWQiOiJ3d3cuZXhhbXBsZS5jb20iLCJzdWIiOiJqcm9j a2V0QGV4YW1wbGUuY29tIiwiZnJvbV91c2VyIjoiQiIsInRhcmdldF91c2VyIjoiQSJ9

Header

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"
}

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

You Base64 encode it as well, and then the string becomes a JWT Header.

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

Signature (Signature)

Use a period for both of the above encoded strings. They are joined together (heads in front) and formed

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcm9tX3VzZXIiOiJCIiwidGFyZ2V0X3VzZXIiOiJBIn0

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

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

rSWamyAYwuHCo7IFAgd1oRpSP7nzL7BF5t7ItqpKViM

This part is also called the signature.

Finally, we concatenate this part of the signature after the signed string, and we get the complete JWT

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcm9tX3VzZXIiOiJCIiwidGFyZ2V0X3VzZXIiOiJBIn

So we can change the URL in the email to

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

Then you can add friends safely!

Wait, we’re bound to have some questions:

  1. What is the purpose of the signature?
  2. Base64 is a code, it’s reversible, so wouldn’t my information be exposed?

Let me go through them for you.

Purpose of Signature

The final step of the signing process is actually signing the header and payload contents. In general, encryption algorithms produce different outputs for different inputs. The probability that two different inputs will produce the same output is extremely small (probably smaller than the probability that I’m the richest person in the world). So let’s take it as a matter of necessity that different inputs produce different outputs.

So, if someone decodes and modifies the contents of the header and payload, and then encodes them, the signatures of the new header and payload will be different from the previous signatures. Also, if you don’t know the key the server uses to encrypt, the signature will be different.

When the server application receives the JWT, it first re-signs the header and payload contents using the same algorithm. So how does the server application know which algorithm we’re using? Remember, we already specified our encryption algorithm with the ALG field in the JWT header.

If the server application signs the header or payload in the same way again and finds that its calculated signature isn’t the same as the one it received, then the contents of the Token may have been manipulated by someone else. We should reject the Token and respond with an HTTP 401 Unauthorized response.

Information will be exposed?

Yes.

Therefore, in JWT, you should not add any sensitive data to the payload. In the example above, we are transferring the User ID of the User. This value is not really sensitive and is generally safe to know.

But things like passwords can’t be put in JWT. If you put the user’s password in JWT, a malicious third party can quickly find out your password through Base64 decoding.

Scenarios where JWT works

As you can see, JWT is good for delivering non-sensitive information to a Web application. For example, complete the friend action mentioned above, and place the order action, etc.

User authentication is eight steps

Authentication is a mechanism that allows users to log in and then, over a period of time, allow them to access a website using their account without having to log in again.

Tip: Do not confuse user authentication with Authorization. User authorization refers to prescribing and allowing users to use their rights, such as Posting, managing sites, and so on.

First, the server application (hereinafter referred to as “the application”) lets the user send his or her user name and password through a Web form to the server interface. This process is typically an HTTP POST request. The recommended approach is to use SSL encrypted transport (HTTPS protocol) to avoid sniffing sensitive information.

Next, the application checks the user name and password with the database.

Once the user name and password are verified successfully, the application takes the user’s ID (user_id in the figure) as an attribute of the JWT Payload, splits it with the header and signs it to form a JWT. Here, the JWT is a string that looks 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).

The application will receive a Cookie containing JWT every time the user accesses the application until the Cookie is invalidated or deleted. The application can then extract the JWT from the request.

The application checks the effectiveness of JWT through a series of tasks. For example, check if the signature is correct; Check if the Token has expired; Optionally, check that the receiver of the Token is himself.

After the application verifies that the JWT is valid, the JWT does Base64 decoding (which may have been done in the previous step), and then reads the user’s ID value, the user_id attribute, in the Payload. Here the user ID is 1025.

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

The application responds to the user’s request.

The difference between storing IDs in Session and in Session

The biggest drawback of the Session approach to storing user IDs is that it takes up a lot of server memory and may require a lot of state for larger applications. In general, large applications also need to use some KV databases and a series of caching mechanisms to achieve Session storage.

The JWT approach distributes the user state to the client, which can significantly reduce the memory pressure on the server. In addition to the user ID, you can also store other user-related information, such as whether the user is an administrator, which buckets the user is in (see “What You Should Know About A/B Testing Basics” (/2015/08/27/ Intro-to-ab-testing /), etc.).

While the JWT approach puts some computational pressure on the server (such as encryption, encoding, and decoding), these pressures may be as small as disk I/O. Specific whether to adopt, need to use data to speak in different scenarios.

Single sign-on (sso)

The Session mode is used to store the user ID. Initially, the user’s Session will only be stored on one server. For sites with multiple subdomains, each subdomain corresponds to at least one different server, for example:

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

Therefore, if we want to realize that after logging in at login.taobao.com, we can still fetch sessions under other subdomains, which requires us to synchronize sessions on multiple servers.

The JWT approach does not have this problem because the user’s state is already transmitted to the client. Therefore, we just need to set the domain containing the JWT Cookie as the top-level domain, for example

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

Note that the domain must be set to a dot plus the top-level domain, which is.taobao.com. Now taobao.com and *. Taobao.com can both accept the Cookie and get the JWT.

The original:
https://u.nu/2k4wk


This article first in the public number: Java version of the Web project, welcome to pay attention to get more exciting content