资讯详情

小程序登录态架构设计:code2session、双令牌刷新与多端互踢实战

📅 2026/10/6 12:43:08 | 华诺云谱 👁 阅读
小程序登录态架构设计:code2session、双令牌刷新与多端互踢实战
做小程序后端登录态看似简单——不就是调个code2session拿 openid 吗真正上线后才会发现一堆问题令牌过期要不要让用户重新登录刷新令牌被人截获怎么办同一账号在手机和平板同时登录要不要互踢存储用 Redis 还是 JWT 无状态本文把我们项目里踩过坑的一整套登录态方案完整梳理出来包含可运行的核心代码和 4 个实战踩坑点。一、整体流程从 wx.login 到拿到业务令牌小程序的登录是微信托管的标准流程如下小程序端调用wx.login()拿到临时登录凭证code有效期仅 5 分钟且只能用一次后端拿codeappidappsecret请求微信code2session接口换取openid用户在当前小程序的唯一标识和session_key后端根据 openid 查库或建用户签发自己的业务令牌access token refresh token之后所有业务请求在 header 中携带 access token后端校验通过即放行关键点openid 只在后端用来建立用户身份绝不能下发给前端当作长期令牌session_key涉及用户敏感数据解密要安全存储且不能暴露给前端。// 后端换码的核心逻辑伪代码实际用 WebClient/RestTemplatepublicLoginVOlogin(Stringcode){// 1. code 换 openidCode2SessionResprespwechatClient.code2session(appid,secret,code);if(resp.getErrcode()!nullresp.getErrcode()!0){thrownewBizException(微信登录失败:resp.getErrmsg());}Stringopenidresp.getOpenid();// 2. 查库或建档UseruseruserMapper.findByOpenid(openid);if(usernull){userUser.builder().openid(openid).createdAt(LocalDateTime.now()).build();userMapper.insert(user);}// 3. 签发双令牌returntokenService.issue(user.getId());}二、为什么要用双令牌而不是单个长效 token最朴素的方案是登录后发一个有效期很长的 token比如 30 天简单但危险token 一旦被截获日志泄露、抓包、恶意转发在整个有效期内都可以冒充用户且无法主动失效。双令牌方案把安全性和体验分开access token有效期短建议 2 小时用于所有业务请求泄露后风险窗口小refresh token有效期长建议 15-30 天只在 access token 过期时调用刷新接口换新且必须可被服务端主动作废用户全程无感知——access token 过期后用 refresh token 静默换新不需要再走一次wx.login也不会被踢到登录页。三、令牌存储有状态 Redis 还是无状态 JWT这是争议最多的点。我们的选型是refresh token 存 Redis有状态access token 用 JWT无状态校验理由access token 用 JWT自包含 uid、过期时间网关层本地验签即可不用每个请求都打一次 Redis性能好refresh token 存 Redis可以做到主动作废、单点登录互踢、登录设备管理——这是纯 JWT 无状态方案的死穴JWT 签发后在过期前无法撤回publicLoginVOissue(Longuid){StringaccessTokenJwtBuilder.builder().claim(uid,uid).expire(Duration.ofHours(2)).signWith(hmacKey).build();// refresh token 用随机串不携带业务信息StringrefreshTokenSecureRandomUtil.uuid();// key 设计refresh:{token} - uid同时记录登录设备redis.setex(refresh:refreshToken,Duration.ofDays(15),String.valueOf(uid));redis.setex(login:uid:uid,Duration.ofDays(15),refreshToken);returnnewLoginVO(accessToken,refreshToken,7200);}四、刷新接口必须做的旋转和复用检测刷新不是拿着 refresh token 换新 access token这么简单直接给出我们踩坑后的实现要点令牌旋转rotation每次刷新都签发新的 refresh token旧的立即删除。这样 refresh token 不是长期固定的截获者拿到一个旧的也用不了复用检测如果一个已经被旋转掉的 refresh token 又被拿来用说明令牌很可能泄露——此时直接作废该用户的全部登录态强制重新登录刷新接口要限流同一设备短时间大量刷新属于异常配合限流防暴力尝试publicLoginVOrefresh(StringoldRefreshToken){Stringkeyrefresh:oldRefreshToken;StringuidStrredis.get(key);if(uidStrnull){// 可能是过期也可能是在使用已旋转的旧令牌LongleakedUidredis.get(refresh_reuse:oldRefreshToken);if(leakedUid!null){// 命中复用检测令牌疑似泄露踢掉该用户全部会话redis.del(login:uid:leakedUid);thrownewTokenReusedException(登录态异常请重新登录);}thrownewUnauthorizedException(refresh token 无效或已过期);}LonguidLong.valueOf(uidStr);// 旧令牌不是直接删而是短时记录用于复用检测如保留 10 分钟redis.setex(refresh_reuse:oldRefreshToken,Duration.ofMinutes(10),uidStr);redis.del(key);returnissue(uid);}五、多端互踢与单点登录有些业务要求同一账号只允许在一台设备登录新设备登录后旧设备被踢下线。有状态存储让这件事变得简单签发时维护login:uid:{uid} - 当前有效refreshToken每次刷新时校验请求带来的 refresh token 是否等于 Redis 里记录的值不一致说明已经有更新的登录拒绝并提示账号在其他设备登录。如果业务允许多端在线但要可管理则改为login:uid:{uid}下存设备 hashdeviceId - token用户可以在我的设备里查看和远程下线后台只需删除对应 token。access token 是 JWT、无法即时失效的问题怎么解决两种工程做法JWT 里带一个loginVersion用户修改密码、被踢时把 Redis 中用户版本号 1网关校验 JWT 版本是否一致每次请求一次 Redis 查询可接受维护一张短 TTL 的作废清单被踢用户的 access token jti 放入黑名单因为 access token 只有 2 小时黑名单体量很小六、前端配合无感刷新的正确姿势小程序端建议把令牌放在wx.setStorageSync并封装统一的请求函数处理 401constrequestasync(options){constrespawaitwxRequest({...options,header:{Authorization:Bearer getAccess()},});if(resp.statusCode401!options._retried){// access 过期用 refresh 静默换新后重放原请求constnewTokensawaitrefreshAccess();setTokens(newTokens);returnrequest({...options,_retried:true});}returnresp;};必须加并发锁多个请求同时 401 时只允许第一个去刷新其余请求等待同一个刷新结果否则会连发多个刷新请求触发旋转机制导致互相作废。七、四个实战踩坑点坑 1code2session 没做失败重试和降级微信接口偶发超时如果后端直接把错误抛给用户用户会莫名其妙登录失败。对网络超时类错误做有限次重试同时要注意区分错误码——40029code 无效不能重试通常是前端重复提交了同一个 code要在小程序端防重复点击。坑 2时钟不同步导致 JWT 大面积失效服务集群如果有机器系统时间不准签发的 JWT 在别的机器校验时会出现还没生效或提前过期。上线前务必确认所有节点开启 NTP 校时JWT 校验时可以给一个小的 leeway如 30 秒容忍微小偏差但不要给太大。坑 3refresh token 明文进了日志和 URL排查问题时把整个请求参数打日志refresh token 就进了日志系统等于白做安全设计。要对令牌做脱敏只留前后几位且令牌只放在请求 body 或 header不要拼在 URL 上——URL 会被网关、代理、浏览器历史记录留存。坑 4换绑手机号后登录态没处理很多小程序支持更换绑定手机号换绑本质是身份凭证变更旧设备上的登录态应根据业务策略强制重新验证或全部作废。漏掉这一步旧手机持有者仍能操作账号属于账号安全事故。八、小结一套可靠的小程序登录态方案可以概括为wx.login 后端code2session建立身份access token 用短效 JWT 保证性能refresh token 用有状态 Redis 存储支持旋转、作废和多端管理刷新接口做令牌旋转与复用检测前端用带并发锁的无感刷新重放请求。安全性、性能和用户体验三者可以同时兼顾关键是想清楚每一种令牌被泄露后怎么办。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑