What is JWT?
JWT is an open standard (RFC 7519) used to securely transfer information between parties. It is a compact, URL-secure token commonly used for authentication and information exchange.
In modern web applications, JWT has become the preferred solution for stateless authentication, especially suitable for microservice architectures and API services.
JWT Structure
The JWT consists of three parts, separated by dots (.):
header.payload.signature
1. Header
Contains token type and signature algorithm information:
json
{
"alg": "HS256",
"typ": "JWT"
}
2. Payload
Contains claims, i.e. the data that is actually to be transferred:
json
{
"sub": "1234567890",
"name": "username",
"role": "admin",
"iat": 1516239022,
"exp": 1516242622
}
3. Signature
Used to verify the integrity of the token:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret
)
Common Claim Types
Standard Statement
- 'iss' (issuer): issuer
- 'sub' (subject): Subject
- 'aud' (audience): Audience
- 'exp' (expiration): Expiration time
- 'iat' (issued at): The time of issue
- 'nbf' (not before): effective time
Custom Statements
Any custom fields can be added, such as user roles, permissions, etc.
Advantages of JWT
- Stateless: The server does not need to store session information
- Cross-Domain Support: Can be used between different domains
- Mobile-Friendly: Suitable for mobile apps and APIs
- Good Performance: Avoids database queries
- Standardization: Based on open standards
Usage Scenarios
1. Identity authentication
The user logs in to obtain the JWT and subsequently requests to carry this token for authentication.
2. Information exchange
Securely transfer information between different services.
3. Single Sign-On (SSO)
Log in once, and multiple apps share the authentication status.
Security Considerations
Storage security
- Avoid storing in localStorage (XSS risk)
- httpOnly cookies are recommended
- Consider using sessionStorage
Token Security
- Set a reasonable expiration time
- Sign with a strong key
- Consider the token refresh mechanism
- Implement token blacklists
Transmission Security
- Always use HTTPS
- Transfer in the Authorization header
- Avoid transferring tokens in URLs
Project Architecture Design
Dual token mechanism
This project adopts a dual-token architecture of Access Token + Refresh Token:
- Access Token: Short-term validity (configured expiration time) for API access authentication
- Refresh Token: Long-term validity (configured expiration time) to refresh the Access Token
Core Components
1. Configuration management
yaml
jwt:
access_expire: 24# hours
refresh_expire: 168 # hours (7 days)
access_secret: "your-access-secret"
refresh_secret: "your-refresh-secret"
issuer: "your-app-name"
2. Declare the structural design
go
Access Token statement
type AccessClaims struct {
UserID uint `json:"user_id"`
Username string `json:"username"`
Role string `json:"role"`
jwt. RegisteredClaims
}
Refresh Token statement
type RefreshClaims struct {
UserID uint `json:"user_id"`
jwt. RegisteredClaims
}
Security features
1. Dual-key design
- Access Token and Refresh Token use different signing keys
- Reduce the risk of key compromise
- Support for independent key rotation policies
2. Token blacklist mechanism
go
Add a token to the blacklist
func AddToBlacklist(token string, expiration time. Duration) error {
key := "blacklist:" + token
return redis. Set(key, "1", expiration). Err()
}
Check if the token is on the blacklist
func IsBlacklisted(token string) bool {
key := "blacklist:" + token
_, err := redis. Get(key). Result()
return err == nil
}
3. Geolocation tracking
Record user IP and geographical location information when logging in to enhance security auditing capabilities.
Middleware implementation
1. Basic certification middleware
go
func AuthMiddleware() gin. HandlerFunc {
return func(c *gin. Context) {
token := c.GetHeader("Authorization")
if token == "" {
c.JSON(401, gin. H{"error": "Token not provided"})
c.Abort()
return
}
Validate the token
claims, err := ValidateToken(token)
if err != nil {
c.JSON(401, gin. H{"error": "Invalid token"})
c.Abort()
return
}
Check the blacklist
if IsBlacklisted(token) {
c.JSON(401, gin. H{"error": "Token has expired"})
c.Abort()
return
}
c.Set("user_id", claims. UserID)
c.Next()
}
}
2. Privilege control middleware
go
func RequireRole(requiredRole string) gin. HandlerFunc {
return func(c *gin. Context) {
userRole, exists := c.Get("user_role")
if !exists {
c.JSON(403, gin. H{"error": "Insufficient permissions"})
c.Abort()
return
}
if userRole != requiredRole {
c.JSON(403, gin. H{"error": "Insufficient Role Privileges"})
c.Abort()
return
}
c.Next()
}
}
3. Token refresh middleware
- Verify the validity of the Refresh Token
- Check the token blacklist status
- Extract user identification information
- Prepare for token refreshes
API Design Patterns
1. Login process
go
func Login(username, password string) (*TokenPair, error) {
Verify user credentials
user, err := ValidateCredentials(username, password)
if err != nil {
return nil, err
}
Generate an Access Token
accessToken, err := GenerateAccessToken(user.ID, user. Role)
if err != nil {
return nil, err
}
Generate Refresh Token
refreshToken, err := GenerateRefreshToken(user.ID)
if err != nil {
return nil, err
}
return &TokenPair{
AccessToken: accessToken,
RefreshToken: refreshToken,
}, nil
}
2. Token refresh process
go
func RefreshToken(refreshToken string) (*TokenPair, error) {
Verify the Refresh Token
claims, err := ValidateRefreshToken(refreshToken)
if err != nil {
return nil, err
}
Check the blacklist
if IsBlacklisted(refreshToken) {
return nil, errors. New("Token Expired")
}
Get the latest user information
user, err := GetUserByID(claims. UserID)
if err != nil {
return nil, err
}
Generate a new token pair
newAccessToken, _ := GenerateAccessToken(user.ID, user. Role)
newRefreshToken, _ := GenerateRefreshToken(user.ID)
Add old tokens to the blacklist
AddToBlacklist(refreshToken, time. Hour*24*7)
return &TokenPair{
AccessToken: newAccessToken,
RefreshToken: newRefreshToken,
}, nil
}
3. Cancel the process
go
func Logout(accessToken, refreshToken string) error {
Parse tokens to get expiration time
accessClaims, _ := ParseToken(accessToken)
refreshClaims, _ := ParseToken(refreshToken)
Calculate the remaining valid time
accessTTL := time. Until(accessClaims.ExpiresAt.Time)
refreshTTL := time. Until(refreshClaims.ExpiresAt.Time)
Add to the blacklist
AddToBlacklist(accessToken, accessTTL)
AddToBlacklist(refreshToken, refreshTTL)
return nil
}
Implement best practices
1. Token lifecycle management
go
Token generation example
func GenerateAccessToken(userID uint, role string) (string, error) {
claims := AccessClaims{
UserID: userID,
Role: role,
RegisteredClaims: jwt. RegisteredClaims{
ExpiresAt: jwt. NewNumericDate(time. Now(). Add(time. Hour * 24)),
IssuedAt: jwt. NewNumericDate(time. Now()),
Issuer: "your-app",
},
}
token := jwt. NewWithClaims(jwt. SigningMethodHS256, claims)
return token. SignedString([]byte("your-secret"))
}
2. Error handling strategy
go
type APIError struct {
Code int `json:"code"`
Message string `json:"message"`
Details string `json:"details,omitempty"`
}
func HandleJWTError(err error) APIError {
switch {
case errors. Is(err, jwt. ErrTokenExpired):
return APIError{Code: 401, Message: "Token expired"}
case errors. Is(err, jwt. ErrTokenMalformed):
return APIError{Code: 401, Message: "Token Malform"}
default:
return APIError{Code: 401, Message: "Authentication failed"}
}
}
3. Performance optimization
- Redis caches user information
- Token parsing result caching
- Bulk token validation
- Asynchronous logging
4. Monitoring and auditing
- Log in behavior logs
- Abnormal access detection
- Token usage statistics
- Geolocation anomaly alerts
Common Problems and Solutions
Q: How to deal with token leaks?
Solution:
- Implement Redis blacklist mechanism
- Set a reasonable token expiration time
- Monitor for anomalous IPs and geolocation access
- Allow users to actively log out of all devices
Q: What are the advantages of the dual-token mechanism?
Advantage:
- Access Token is effective in the short term, reducing the risk of leakage
- Refresh Token is valid for a long time, enhancing user experience
- Separate key and expiration policies
- Supports fine-grained permission control
Q: How to implement cross-device login management?
Implementation method:
- The token contains device identification information
- Cache storage of the user's active device list
- Support remote device logout
- Limit on the number of devices that can be configured
Q: What are the performance optimization recommendations?
Optimization Strategy:
- Caching user permission information
- Token pre-validation mechanism
- Bulk token operation processing
- Asynchronous status updates
Q: How do I deal with clock offset issues?
Solution:
- Set a reasonable clock offset tolerance
- Synchronize server time using NTP
- Added time buffer in token validation
- Monitor server time synchronization status
Project Features and Advantages
1. Architectural advantages
- Dual Token Design: Balancing security with user experience
- Configurable Management: Flexible expiration time and key configuration
- Middleware Mode: Unified authentication and authorization processing
- Redis Integration: High-performance token state management
2. Safety features
- Multi-Layer Verification: Token validity + blacklist check + role permissions
- Geolocation Tracking: Enhance security auditing capabilities
- Key Separation: Access and Refresh Tokens use different keys
- Progressive Expiration: Supports elegant token renewals
3. O&M friendly
- Detailed Logs: Complete authentication and authorization logging
- Error Handling: Unified error response and exception handling
- Monitoring Support: Built-in performance and safety monitoring points
- Scalability: Supports multiple authentication methods and permission models