JWT refreshtoken 实践

本文深入探讨了JSON Web Token (JWT)的特点与应用场景,特别是其在单点登录(SSO)和分布式系统中的优势。同时,文章详细介绍了如何利用Refresh Token解决JWT在安全性与便捷性上的挑战,包括JWT与Refresh Token的生成、验证流程及代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Json web token (JWT), 根据官网的定义,是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。 详细介绍可以查看这篇文章 理解JWT(JSON Web Token)认证及实践

JWT 特点

优点

  • 体积小,因而传输速度快
  • 传输方式多样,可以通过URL/POST参数/HTTP头部等方式传输
  • 严格的结构化。它自身(在 payload 中)就包含了所有与用户相关的验证消息,如用户可访问路由、访问有效期等信息,服务器无需再去连接数据库验证信息的有效性,并且 payload 支持为你的应用而定制化。
  • 支持跨域验证,可以应用于单点登录。

存在的问题

JWT 自身(在 payload 中)就包含了所有与用户相关的验证消息,所以通常情况下不需要保存。这种设计存在几个问题:

  1. Token不能撤销--客户端重置密码后之前的JWT依然可以使用(JWT 并没有过期或者失效
  2. 不支持refresh token,JWT过期后需要执行登录授权的完整流程
  3. 无法知道用户签发了几个JWT

针对第一个问题,可能的解决方法有:

  1. 保存JWT到数据库(或Redis),这样可以针对每个JWT单独校验
  2. 在重置密码等需要作废之前全部JWT时,把操作时间点记录到数据库(或Redis),校验JWT时同时判断此JWT创建之后有没有过重置密码等类似操作,如果有校验不通过

当然,这种解决方法都会多一次数据库请求,JWT自身可校验的优势会有所减少,同时也会影响认证效率。

这篇文章主要介绍解决第二个问题(不支持refresh token)的思路。

refresh token

refresh token是OAuth2 认证中的一个概念,和OAuth2 的access token 一起生成,表示更新令牌,过期所需时间比access toen 要长,可以用来获取下一次的access token。

如果JWT 需要添加 refresh token支持,refresh token需要满足的条件有一下几项:

  1. 和JWT一起生成返回给客户端
  2. 有实效时间,有效时间比JWT要长
  3. 只能用来换取下一次JWT,不能用于访问认证
  4. 不能重复使用(可选)
refresh token 获取流程

refresh token 使用流程

代码示例

import jwt
import time

# 使用 sanic 作为restful api 框架 
def create_token(account_id, username):
    payload = {
        "iss": "gusibi.mobi",
        "iat": int(time.time()),
        "exp": int(time.time()) + 86400 * 7,
        "aud": "www.gusibi.mobi",
        "sub": account_id,
        "username": username,
        "scopes": ['open']
    }
    token = jwt.encode(payload, 'secret', algorithm='HS256')
    payload['grant_type'] = "refresh"
    refresh_token = jwt.encode(payload, 'secret', algorithm='HS256')
    return True, {
        'access_token': token,
        'account_id': account_id,
        "refresh_token": refresh_token
        }

# 验证refresh token 出否有效
def verify_refresh_token(token):
    payload = jwt.decode(token, 'secret', audience='www.gusibi.com', algorithms=['HS256'])
    # 校验token 是否有效,以及是否是refresh token,验证通过后生成新的token 以及 refresh_token
    if payload and payload.get('grant_type') == 'refresh':
        # 如果需要标记此token 已经使用,需要借助redis 或者数据库(推荐redis)
        return True, payload
    return False, None

# 验证token 是否有效
def verify_bearer_token(token):
    #  如果在生成token的时候使用了aud参数,那么校验的时候也需要添加此参数
    payload = jwt.decode(token, 'secret', audience='www.gusibi.com', algorithms=['HS256'])
    # 校验token 是否有效,以及不能是refresh token
    if payload and not payload.get('grant_type') == 'refresh':
        return True, payload
    return False, None
复制代码

参考链接

References [1] 理解OAuth 2.0: www.ruanyifeng.com/blog/2014/0…


最后,感谢女朋友支持和包容,比❤️

也可以在公号输入以下关键字获取历史文章:公号&小程序 | 设计模式 | 并发&协程

<think>好的,用户想了解如何在前端通过JWT实现双Token机制。首先,我需要回忆一下JWT和双Token认证的基本原理。双Token通常指的是使用Access Token和Refresh Token的组合。Access Token用于短期授权,而Refresh Token用于获取新的Access Token,这样可以在不频繁要求用户重新登录的情况下保持安全性。 根据引用[1],双Token认证的工作流程大致是用户登录后,服务器返回两个Token:Access和Refresh。前端用Access Token进行API请求,当Access Token过期时,用Refresh Token获取新的Access Token。如果Refresh Token也过期了,就需要重新登录。这个机制的关键在于减少Access Token的有效期,降低泄露风险,同时通过Refresh Token延长会话。 引用[2]提到JWT的结构,包括头部、载荷和签名。前端需要妥善存储这两个Token,通常Access Token存在内存或会话存储中,而Refresh Token存在更安全的地方,比如HttpOnly的Cookie中,防止XSS攻击。同时,要处理Token过期的情况,比如在请求拦截器中检查响应状态码,如果是401,则尝试用Refresh Token刷新Access Token。 可能的问题点包括如何安全存储Token、防止CSRF和XSS攻击、处理并发请求时的Token刷新等。例如,当多个请求同时发现Access Token过期,需要确保只发送一次Refresh请求,避免重复刷新。此外,需要考虑服务器的Token刷新接口的安全性,比如验证Refresh Token的有效性,并在使用后使其失效,防止重复使用。 用户可能需要具体的实现步骤,比如前端如何发起登录请求,如何处理Token的存储,如何在请求头中添加Authorization,以及如何实现自动刷新。代码示例可以帮助用户理解,比如使用axios的拦截器来处理Token刷新逻辑,同时处理并发请求的情况。 还需要注意一些最佳实践,比如设置合理的Token过期时间,使用HTTPS来加密传输,以及定期轮换Refresh Token等。可能的相关问题会涉及如何保证Token的安全性,双Token机制的优缺点,以及如何处理Token失效后的用户重定向等。 总之,我需要将这些知识点整理成一个结构清晰的回答,涵盖原理、实现步骤、代码示例以及注意事项,并生成相关问题供用户进一步学习。</think>### 前端通过 JWT 实现双 Token 机制的方案及原理 #### 一、双 Token 机制的核心原理 双 Token 机制通过分离短期有效的 **Access Token** 和长期有效的 **Refresh Token** 提升安全性: 1. **Access Token**:用于接口请求鉴权,有效期较短(如 15 分钟),降低泄露风险[^1]。 2. **Refresh Token**:用于刷新 Access Token,有效期较长(如 7 天),存储要求更严格[^1]。 #### 二、前端实现步骤 ##### 1. 登录获取双 Token ```javascript // 用户登录成功后,服务端返回双 Token { "access_token": "xxx", "expires_in": 900, // 15分钟 "refresh_token": "yyy" } ``` - **存储策略**: - Access Token → 内存或 `sessionStorage`(短期) - Refresh Token → `HttpOnly Cookie`(防 XSS)[^2] ##### 2. 请求时携带 Access Token ```javascript // 在请求头中添加 Authorization axios.interceptors.request.use(config => { const accessToken = sessionStorage.getItem('access_token'); if (accessToken) { config.headers.Authorization = `Bearer ${accessToken}`; } return config; }); ``` ##### 3. 自动刷新 Access Token ```javascript let isRefreshing = false; // 防止并发刷新 let failedQueue = []; // 存储等待刷新的请求 axios.interceptors.response.use( response => response, async error => { if (error.response.status === 401 && !error.config._retry) { if (isRefreshing) { return new Promise(resolve => { failedQueue.push(() => resolve(axios(error.config))); }); } isRefreshing = true; error.config._retry = true; try { const newAccessToken = await refreshToken(); sessionStorage.setItem('access_token', newAccessToken); error.config.headers.Authorization = `Bearer ${newAccessToken}`; failedQueue.forEach(cb => cb()); failedQueue = []; return axios(error.config); } catch (e) { // 刷新失败跳转登录页 window.location.href = '/login'; } finally { isRefreshing = false; } } return Promise.reject(error); } ); // 刷新 Token 的函数 async function refreshToken() { const res = await axios.post('/refresh_token', {}, { withCredentials: true // 自动携带 Refresh Token Cookie }); return res.data.access_token; } ``` #### 三、安全增强措施 1. **CSRF 防御**:对敏感操作增加 `CSRF Token` 校验 2. **XSS 防护**: - 避免将 Token 存在 `localStorage` - 设置 Cookie 的 `SameSite=Strict` 3. **服务端控制**: - 每次刷新后使旧 Refresh Token 失效 - 限制 Refresh Token 使用次数 #### 四、双 Token 机制优缺点 | 优势 | 劣势 | |------|------| | 减少 Access Token 泄露窗口期 | 实现复杂度较高 | | 避免频繁重新登录 | 需处理并发请求场景 | | 支持服务端主动吊销会话 | 依赖 HTTPS 保证传输安全 |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值