使用 JSON Web Token (JWT) 进行身份验证是一种常见的做法。然而,JWT 通常有一个有效期,当用户的令牌过期时,如果不进行处理,用户将被迫重新登录,这会影响用户体验。为了解决这个问题,可以实现无感刷新(silent refresh)机制,自动刷新令牌而不打扰用户。
本文将介绍如何实现无感刷新 Token 的技术方案,包括以下步骤:
- 生成和使用访问令牌和刷新令牌
- 自动刷新访问令牌
- 处理令牌刷新失败的情况
1. 生成和使用访问令牌和刷新令牌
生成访问令牌和刷新令牌
在用户登录时,服务器生成两个令牌:访问令牌(access token)和刷新令牌(refresh token)。访问令牌用于验证用户身份,有较短的有效期;刷新令牌用于获取新的访问令牌,有较长的有效期。
import jwt import datetime def generate_tokens(user_id): access_token = jwt.encode({ 'user_id': user_id, 'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=15) }, 'access_secret_key', algorithm='HS256') refresh_token = jwt.encode({ 'user_id': user_id, 'exp': datetime.datetime.utcnow() + datetime.timedelta(days=7) }, 'refresh_secret_key', algorithm='HS256') return access_token, refresh_token
返回令牌给客户端
在用户登录成功后,将访问令牌和刷新令牌返回给客户端,并将它们存储在安全的位置。
// 示例:使用 axios 处理登录请求并存储令牌 axios.post('https://yourapi.com/login', { username: 'user', password: 'password' }).then(response => { localStorage.setItem('access_token', response.data.access_token); localStorage.setItem('refresh_token', response.data.refresh_token); });
2. 自动刷新访问令牌
拦截请求并检查令牌
在前端,使用 Axios 拦截器检查每个请求的访问令牌是否即将过期。如果即将过期,使用刷新令牌获取新的访问令牌。
import axios from 'axios'; import jwt_decode from 'jwt-decode'; // 创建 Axios 实例 const api = axios.create({ baseURL: 'https://yourapi.com', }); // 添加请求拦截器 api.interceptors.request.use(async (config) => { let accessToken = localStorage.getItem('access_token'); const refreshToken = localStorage.getItem('refresh_token'); if (accessToken) { const { exp } = jwt_decode(accessToken); const expirationTime = exp * 1000; const currentTime = Date.now(); // 如果访问令牌即将过期,刷新令牌 if (expirationTime - currentTime < 5 * 60 * 1000) { // 5 分钟 try { const response = await axios.post('https://yourapi.com/refresh-token', { token: refreshToken }); accessToken = response.data.access_token; localStorage.setItem('access_token', accessToken); } catch (error) { console.error('Unable to refresh token', error); // 刷新令牌失败,处理失败逻辑 } } config.headers['Authorization'] = `Bearer ${accessToken}`; } return config; }, error => { return Promise.reject(error); });
服务器端刷新令牌
在服务器端,实现刷新令牌的逻辑,验证刷新令牌并生成新的访问令牌。
from flask import Flask, request, jsonify import jwt import datetime app = Flask(__name__) @app.route('/refresh-token', methods=['POST']) def refresh_token(): try: refresh_token = request.json.get('token') decoded_refresh_token = jwt.decode(refresh_token, 'refresh_secret_key', algorithms=['HS256']) user_id = decoded_refresh_token['user_id'] new_access_token = jwt.encode({ 'user_id': user_id, 'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=15) }, 'access_secret_key', algorithm='HS256') return jsonify({'access_token': new_access_token}) except jwt.ExpiredSignatureError: return jsonify({'error': 'Refresh token expired'}), 401 except jwt.InvalidTokenError: return jsonify({'error': 'Invalid refresh token'}), 401 if __name__ == '__main__': app.run()
3. 处理令牌刷新失败的情况
在实际应用中,可能会遇到刷新令牌失败的情况,如刷新令牌已过期或无效。此时需要处理这些情况,通常是引导用户重新登录。
在前端处理刷新失败
在前端拦截响应错误,当刷新令牌失败时,清除存储的令牌并重定向用户到登录页面。
// 添加响应拦截器 api.interceptors.response.use(response => { return response; }, error => { if (error.response.status === 401 && error.config && !error.config.__isRetryRequest) { localStorage.removeItem('access_token'); localStorage.removeItem('refresh_token'); window.location.href = '/login'; } return Promise.reject(error); });
总结
无感刷新(silent refresh)机制通过在后台自动刷新访问令牌,确保用户在使用应用时不会因令牌过期而被迫重新登录,从而提高用户体验。实现无感刷新需要前后端协同工作,前端负责检测和刷新令牌,后端提供刷新令牌的接口。通过合理的设计和实现,可以有效地提高应用的安全性和用户体验。
标签:令牌,error,token,refresh,access,Token,刷新,无感 From: https://www.cnblogs.com/zx618/p/18333176