JWT (JSON Web Token) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. Due to its compactness, self-containment, and cross-domain friendliness, JWT has become the mainstream solution for authentication and information exchange in modern web applications. This article starts from the basic concepts of JWT, deeply analyzes its structure, working mechanism, application scenarios, security analysis, and best practices to help developers fully master JWT technology.
I. Overview of JWT
JWT is an open standard based on JSON for transmitting claims between network application environments. It consists of three parts: Header, Payload, and Signature. The main features of JWT include:
- Compact: JWT is small in size and can be sent via URL, POST parameter, or HTTP Header
- Self-contained: JWT contains all the information needed about the user, avoiding multiple database queries
- Cross-domain friendly: JWT natively supports cross-domain authentication, suitable for distributed systems and microservice architectures
- Stateless: The server doesn't need to store session information, making horizontal scaling easy
II. JWT Structure Explained
JWT consists of three parts separated by dots (.): Header.Payload.Signature. Below is a detailed introduction to each part.
2.1 Header
The Header typically consists of two parts: the type of token (which is JWT) and the signing algorithm being used, such as HMAC SHA256 or RSA. Then, this JSON object is Base64Url encoded to form the first part of the JWT.
// JWT Header Example
{
"alg": "HS256",
"typ": "JWT"
}
// After Base64Url encoding:
// eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
2.2 Payload
The Payload part contains claims, which are statements about an entity (typically, the user) and additional data. Claims are of three types: registered claims, public claims, and private claims. Similarly, the Payload is Base64Url encoded to form the second part of the JWT.
Claim Types
Registered Claims:These are a set of predefined claims which are not mandatory but recommended, to provide a set of useful, interoperable claims.
Public Claims:These can be defined at will by those using JWTs. But to avoid collisions they should be defined in the IANA JSON Web Token Registry or be defined as a URI that contains a collision resistant namespace.
Private Claims:These are custom claims created to share information between parties that agree on using them and are neither registered nor public claims.
| Claim Type | Name | Abbreviation | Description |
|---|---|---|---|
| Registered Claims | Issuer | iss | The issuer of the JWT |
| Subject | sub | The subject of the JWT (usually user ID) | |
| Audience | aud | The recipient of the JWT | |
| Expiration Time | exp | The expiration time of the JWT (timestamp) | |
| Not Before | nbf | The JWT is not valid before this time | |
| Issued At | iat | The time the JWT was issued (timestamp) | |
| JWT ID | jti | Unique identifier for the JWT, used to prevent replay attacks |
// JWT Payload Example
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022,
"exp": 1516242622,
"role": "admin"
}
// After Base64Url encoding:
// eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE1MTYyNDI2MjIsInJvbGUiOiJhZG1pbiJ9
2.3 Signature
The Signature part is used to verify that the message wasn't changed along the way. For tokens signed with a private key, it also verifies the identity of the sender of the JWT. The signature is generated by: taking the encoded Header, the encoded Payload, a secret, and signing it with the algorithm specified in the Header.
Signature Formula: HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
| Algorithm | Type | Key | Security |
|---|---|---|---|
| HS256 (HMAC-SHA256) | Symmetric | Shared secret | High (secret must be protected) |
| RS256 (RSA-SHA256) | Asymmetric | Public/private key pair | High |
| ES256 (ECDSA-SHA256) | Asymmetric (Elliptic Curve) | Public/private key pair | High (shorter keys) |
III. How JWT Works
In authentication scenarios, the JWT workflow is as follows:
- User Login: User logs in with username and password (or other authentication method)
- Server Verification: Server verifies user credentials and generates JWT upon successful verification
- Return Token: Server returns the JWT to the client
- Store Token: Client stores the JWT locally (e.g., localStorage, sessionStorage, or Cookie)
- Send Request: Client includes JWT in the Authorization Header of subsequent requests
- Verify Token: Server verifies the JWT signature and validity to confirm user identity
- Return Data: After verification passes, server returns protected resources
💡 提示:Flow Diagram: User → Login Request → Server Verification → Generate JWT → Return JWT → Client Storage → Request with JWT → Server Verification → Return Resources
前端生成 JWT 示例
// Generate JWT using JavaScript (HMAC-SHA256)
const base64UrlEncode = (obj) => {
return btoa(JSON.stringify(obj))
.replace(/=/g, '')
.replace(/\+/g, '-')
.replace(/\//g, '_');
};
const generateJWT = async (payload, secret) => {
const header = { alg: 'HS256', typ: 'JWT' };
const encodedHeader = base64UrlEncode(header);
const encodedPayload = base64UrlEncode(payload);
const data = encodedHeader + '.' + encodedPayload;
const encoder = new TextEncoder();
const keyData = encoder.encode(secret);
const messageData = encoder.encode(data);
const cryptoKey = await crypto.subtle.importKey(
'raw',
keyData,
{ name: 'HMAC', hash: 'SHA-256' },
false,
['sign']
);
const signature = await crypto.subtle.sign('HMAC', cryptoKey, messageData);
const signatureArray = Array.from(new Uint8Array(signature));
const encodedSignature = btoa(String.fromCharCode(...signatureArray))
.replace(/=/g, '')
.replace(/\+/g, '-')
.replace(/\//g, '_');
return data + '.' + encodedSignature;
};
// Usage Example
const payload = {
sub: 'user123',
name: 'John Doe',
role: 'admin',
exp: Math.floor(Date.now() / 1000) + 3600
};
const token = await generateJWT(payload, 'your-secret-key');
console.log('JWT:', token);
IV. Common Application Scenarios
With its unique advantages, JWT is widely used in various scenarios:
1. Authentication
This is the most common scenario for using JWT. Once the user is logged in, each subsequent request will include the JWT, allowing the user to access routes, services, and resources that are permitted with that token. Single Sign On is a feature that widely uses JWT nowadays, because of its small overhead and its ability to be easily used across different domains.
2. Information Exchange
JWTs are a good way of securely transmitting information between parties. Because JWTs can be signed—for example, using public/private key pairs—you can be sure the senders are who they say they are. Additionally, as the signature is calculated using the header and the payload, you can also verify that the content hasn't been tampered with.
3. Single Sign-On (SSO)
Due to its cross-domain nature, JWT is ideal for implementing single sign-on systems. Users can seamlessly access other associated systems after logging in to one system without re-authentication.
4. Microservices Architecture
In a microservices architecture, each service can independently verify JWT without sharing session state, reducing system coupling and improving scalability.
5. Mobile Application Authentication
JWT is suitable for mobile application authentication because it doesn't rely on cookies and can be easily stored in the mobile device's local storage.
V. Security Analysis
Although JWT provides many conveniences, the following security issues need to be noted during use:
1. Signing Algorithm Security
Choosing a secure signing algorithm is crucial. HMAC algorithms require protecting the secret key, while RSA/ECDSA algorithms require properly safeguarding the private key. Never use the 'none' algorithm, as this allows JWTs to be arbitrarily forged.
2. Token Storage Security
The way JWT is stored directly affects security. Storing in localStorage makes it vulnerable to XSS attacks, while storing in cookies makes it vulnerable to CSRF attacks if not properly configured. It is recommended to use HttpOnly and Secure cookies to store JWTs.
3. Token Expiration Mechanism
Setting a reasonable expiration time (exp) is very important. A shorter expiration time reduces the risk of token theft. At the same time, a refresh token mechanism can be used to extend user session time while maintaining security.
4. Sensitive Information Protection
The JWT Payload is only Base64 encoded, not encrypted. Therefore, never store sensitive information such as passwords or credit card numbers in the Payload.
VI. Common Attacks and Protection
Understanding the common attacks JWT faces and their protection measures is crucial for ensuring system security:
1. XSS Attacks
Cross-Site Scripting (XSS) attacks can steal JWTs stored in localStorage. Protection measures include: escaping all user input, using Content Security Policy (CSP), and using HttpOnly cookies to store JWTs.
2. CSRF Attacks
If JWT is stored in cookies, it may be vulnerable to Cross-Site Request Forgery (CSRF) attacks. Protection measures include: using the SameSite cookie attribute, verifying the Referer/Origin Header, and using CSRF tokens.
3. Algorithm Confusion Attacks
An attacker might change the signing algorithm from RS256 to HS256 and then use the public key as the secret to forge a signature. The protection measure is to explicitly specify the expected algorithm during verification and not rely on the alg field in the JWT Header.
4. Replay Attacks
After intercepting a JWT, an attacker can replay the token to access protected resources. Protection measures include: setting a short expiration time, using one-time tokens, and recording used tokens (jti).
5. Token Theft
If a JWT is stolen during transmission, the attacker can impersonate the user. Protection measures include: always using HTTPS for transmission, setting a reasonable expiration time, and implementing a token revocation mechanism.
VII. Best Practices
Here are some best practices for using JWT:
1. Choose Secure Algorithms
Use HS256 or higher strength algorithms. For scenarios requiring asymmetric encryption, use RS256 or ES256. Never use the 'none' algorithm.
2. Set Reasonable Expiration Time
Access Token expiration time should be short (e.g., 15-30 minutes), while Refresh Token expiration time can be longer (e.g., 7 days).
3. Store Tokens Securely
Prioritize using HttpOnly + Secure + SameSite cookies to store JWTs. If you must store in localStorage, ensure your website has no XSS vulnerabilities.
4. Minimize Payload
Only store necessary information in the Payload, don't store sensitive data. The larger the JWT, the greater the overhead for each request.
5. Strictly Verify Tokens
Verify all necessary claims including signature, expiration time (exp), not before (nbf), issuer (iss), audience (aud), etc.
6. Use Refresh Token Mechanism
Use a combination of short-lived access tokens and long-lived refresh tokens to ensure both security and good user experience.
7. Implement Token Revocation Mechanism
Although JWT is stateless, in some scenarios (such as user logout, password change) tokens need to be immediately revoked. This can be implemented using a token blacklist or shortening the expiration time.
Node.js 验证 JWT 示例
// Verify JWT in Node.js
const jwt = require('jsonwebtoken');
// Auth middleware
const authMiddleware = (req, res, next) => {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({ error: 'No authentication token provided' });
}
const token = authHeader.substring(7);
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET, {
algorithms: ['HS256'],
issuer: 'your-app',
audience: 'your-app-audience'
});
req.user = decoded;
next();
} catch (error) {
if (error.name === 'TokenExpiredError') {
return res.status(401).json({ error: 'Token has expired' });
}
return res.status(401).json({ error: 'Invalid token' });
}
};
// Generate access token and refresh token
const generateTokens = (user) => {
const accessToken = jwt.sign(
{ userId: user.id, role: user.role },
process.env.JWT_ACCESS_SECRET,
{ expiresIn: '15m', issuer: 'your-app' }
);
const refreshToken = jwt.sign(
{ userId: user.id },
process.env.JWT_REFRESH_SECRET,
{ expiresIn: '7d', issuer: 'your-app' }
);
return { accessToken, refreshToken };
};
刷新令牌示例
// Refresh token example
const refreshTokenHandler = async (req, res) => {
const { refreshToken } = req.body;
if (!refreshToken) {
return res.status(401).json({ error: 'No refresh token provided' });
}
try {
const decoded = jwt.verify(
refreshToken,
process.env.JWT_REFRESH_SECRET,
{ algorithms: ['HS256'] }
);
const user = await User.findById(decoded.userId);
if (!user) {
return res.status(401).json({ error: 'User not found' });
}
const newAccessToken = jwt.sign(
{ userId: user.id, role: user.role },
process.env.JWT_ACCESS_SECRET,
{ expiresIn: '15m', issuer: 'your-app' }
);
const newRefreshToken = jwt.sign(
{ userId: user.id },
process.env.JWT_REFRESH_SECRET,
{ expiresIn: '7d', issuer: 'your-app' }
);
res.json({
accessToken: newAccessToken,
refreshToken: newRefreshToken
});
} catch (error) {
res.status(401).json({ error: 'Invalid refresh token' });
}
};
VIII. JWT vs Session Comparison
JWT and Session are two common authentication schemes, each with advantages and disadvantages:
| Feature | JWT | Session |
|---|---|---|
| State | Stateless | Stateful |
| Storage Location | Client (localStorage/Cookie) | Server-side |
| Scalability | Excellent, no session sharing needed | Moderate, requires session sharing |
| Cross-domain Support | Native support | Requires additional configuration |
| Performance | Needs signature verification each time | Needs to query session storage |
| Mobile Apps | Friendly, no cookie dependency | Unfriendly, cookie-dependent |
| Immediate Revocation | Difficult (needs blacklist) | Easy, just delete session |
| Security | Beware of XSS/CSRF attacks | Beware of CSRF attacks |
IX. JWT Types
According to purpose and security level, JWT can be divided into the following types:
1. JWS (JSON Web Signature)
This is the most common JWT type, using signatures to ensure message integrity and sender authentication. JWS is the preferred choice for most authentication scenarios.
2. JWE (JSON Web Encryption)
JWE provides not only signature but also encryption, protecting the confidentiality of the Payload. Suitable for scenarios where sensitive information needs to be transmitted in JWT. JWE has a more complex structure and larger size than JWS.
JWS vs JWE Comparison
| Feature | JWS | JWE |
|---|---|---|
| Main Purpose | Authentication, integrity verification | Encrypted sensitive information transmission |
| Encryption | No (Base64 encoding only) | Yes (Content Encryption) |
| Size | Smaller | Larger |
| Complexity | Simple | Complex |
| Use Case | Most authentication scenarios | Scenarios requiring Payload confidentiality |
X. TudoSi JWT Tool
To help developers use and debug JWT more conveniently, TudoSi Tools provides a professional JWT online tool that supports the following features:
- JWT encoding and decoding
- Support for multiple signing algorithms (HS256, HS384, HS512, RS256, RS384, RS512, ES256, ES384, ES512)
- Real-time signature validation
- Visual display of Header and Payload content
- Support for custom keys and public/private keys
XI. Summary
As a modern authentication and information exchange solution, JWT has been widely used in web applications, mobile applications, and microservice architectures due to its compactness, self-containment, and cross-domain friendliness. Understanding the structure, working mechanism, and security features of JWT is the foundation for using JWT correctly.
In practical applications, developers need to choose appropriate signing algorithms, storage methods, and expiration strategies based on specific scenarios, and strictly follow security best practices to ensure system security. At the same time, using professional JWT tools can greatly improve development efficiency and debugging convenience.