ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

7 天 Token 有效,怎么让用户不再次输密码:Bangumi 的 OAuth 登录实现

7 天 Token 有效,怎么让用户不再次输密码:Bangumi 的 OAuth 登录实现 7 天 Token 有效怎么让用户不再次输密码Bangumi 的 OAuth 登录实现【免费下载链接】Bangumi:electron: An unofficial https://bgm.tv ui first app client for Android and iOS, built with React Native. 一个无广告、以爱好为驱动、不以盈利为目的、专门做 ACG 的类似豆瓣的追番记录bgm.tv 第三方客户端。为移动端重新设计内置大量加强的网页端难以实现的功能且提供了相当的自定义选项。 目前已适配 iOS / Android。项目地址: https://gitcode.com/GitHub_Trending/ba/BangumiBangumi 是一个用 React Native 开发的 bgm.tv 第三方客户端帮 ACG 追番用户在手机上记录进度、管理收藏无广告、不以盈利为目的。功能之外它更值得看的是登录系统本文以它的 OAuth 登录为样本把从首次登录到 Token 过期静默刷新的完整链路讲一遍。整体链路鸟瞰认证链路穿过四个模块登录页、用户 storesrc/stores/user/、首页进度页、统一请求层。一次完整的 Token 生命周期如下用户在登录页输入账号密码表单在 webview 内提交成功后取回网页端 cookie存为userCookie供后续请求 HTML 页面使用。首页进度页发现有网页 cookie 但 API 令牌缺失或已过期用该 cookie 向/oauth/authorize发起 GET从返回的 HTML 里提取 formhashDiscuz 系网站的一次性表单码防重放提交。把 formhash 以maxRedirects: 0禁止自动跟随重定向POST 回同一地址从 302 响应的 Location 头里解析出 OAuth 授权码 code。拿 code 加client_id、client_secret去/oauth/access_token换令牌返回的expires_in固定 604800 秒7 天随状态一起写入本地存储。之后所有 API 请求在请求头统一带上Authorization: Bearer token服务端若返回invalid_token客户端立刻把登录态标记为过期。令牌确实过期时进度页触发静默重新授权重复第 2~4 步若 cookie 已失效、服务端返回 4xx则提示请重新登录并保留现场。关键实现拆解换令牌这一步非 200 一律抛异常绝不落盘这一步负责完成 OAuth 的最后一次交接核心逻辑在src/stores/user/action.ts的getAccessToken里const { status, data } await axiosWithProxy(axios, { method: post, maxRedirects: 0, validateStatus: null, url: ${HOST}/oauth/access_token, data: urlStringify({ grant_type: authorization_code, client_id: APP_ID, client_secret: APP_SECRET, code, redirect_uri: URL_OAUTH_REDIRECT, state: getTimestamp() }) }, true) if (status ! 200 || !data) throw new TypeError(String(status)) this.updateAccessToken(data)validateStatus: null是关键它让 axios 在 4xx/5xx 时不自行抛错把被服务端拒绝和网络断连两种失败收敛到同一个出口TypeError(String(status))外层reOauth捕获后从 message 里读状态码决定提示重新登录还是检查网络。若依赖 axios 默认行为两类错误会被混在一个 catch 里只能给出含糊的提示。失败时也不写任何状态避免旧令牌被半成品覆盖。静默重授权一个模块级变量拦住并发刷新令牌过期时用户不该有任何动作重授权由进度页的数据加载顺手触发触发点在src/screens/home/v2/store/index.ts// 可能是 access_token 过期了, 需要重新刷新 access_token if (userStore.isWebLogin) { if (!reOauthing) { reOauthing true // oauth 成功后重新刷新数据 if (await userStore.reOauth()) { reOauthing false feedback() info(重新授权成功) // ...重新拉取收藏与进度 return result } reOauthing false } }reOauthing是模块级变量而非组件状态。下拉刷新和定时任务可能同时进入这段逻辑没有这个锁两次授权会各自请求 formhash在服务端互相覆盖。放在模块级而不是实例里还能保证页面组件被卸载重建后锁依然有效。另一个细节重授权失败不清空登录信息——src/stores/user/action.ts的注释写明保留现有登录信息不清除只弹提示。代价是接下来一段时间请求仍会携带失效令牌但这由请求层的兜底下一节接住。令牌挂到请求上一行请求头加一个过期兜底令牌的使用面比获取面更值得看。src/utils/fetch/fetch.ts在统一请求方法里同时做附加和检测过期两件事const { accessToken } syncUserStore() if (accessToken.access_token) { config.headers.Authorization ${accessToken.token_type} ${accessToken.access_token} } // 响应解析时, 服务端声明令牌无效 if (json?.error) { if (json.error invalid_token) syncUserStore().setOutdate() return { code: json.code, error: json.error, request: json.request } }把附加逻辑放在唯一入口业务代码不必传令牌新增接口也不会漏鉴权服务端主动声明invalid_token时立刻置过期标志比等收藏接口返回空再反推要快一轮。同文件里还有一个WEB分支专门处理网页端旧版 API 地址不带令牌避免令牌暴露在浏览器侧——这种跨平台差异如果不集中处理会在每个调用点里重复判断。设计取舍与边界情况 这个项目有三处取舍看得比较清楚。两套登录态分着维护。src/stores/user/index.ts顶部注释说得直白accessToken 和登录时在 webview 里获取 cookie 是两套登录状态暂时只能分开维护。API 令牌活 7 天网页 cookie没多久就过期寿命差了一个量级硬合成一个状态只会两边都别扭。项目当前的写法是用 cookie 当钥匙去开静默重授权用户无感要付出的代价是 cookie 先失效时重授权也跟着失败只能引导重新登录没有第三种长生命周期凭据兜底。我会考虑申请 refresh_token状态结构里refresh_token字段已预留把依赖从网页 cookie 上挪开。空结果不等于登录过期。收藏接口返回空列表有两种成因令牌过期或用户真的没有在看。src/stores/user/fetch.ts的fetchCollection用本地缓存至少 2 条做判断只有本地非空而远端为空才返回null当作过期信号首页的initQueue又用_ok标记确认请求确实成功但列表为空时不视为过期。这条规则的代价只追 0~1 部作品的用户空结果不会触发刷新——不过令牌没坏时本来也不需要刷。若不写这条真没有看用户的每次刷新都会被推进过期重授权循环。secret 放在客户端里。OAuth 的常规设计是client_secret只留在服务端但 App 代码天然可反编译Bangumi 仍把它作为常量打进包内。它赌的是两点令牌有效期短7 天、接口以只读为主。这个赌注的代价是任何拿到代码的人都能以该应用身份调用换令牌接口若照搬到带写操作的 App比如改资料就必须补签名或设备绑定。借鉴到你的项目✅ 三步可以照搬的动作把API 令牌和网页会话拆成两个独立状态项各自配一个登录判断的 getter对应isLogin/isWebLogin别用一个布尔值表达整个 App 的登录态。把令牌过期判断从请求层剥离invalid_token、空列表等信号先汇入一个过期标志由唯一入口如首屏刷新触发重授权入口用模块级布尔量做并发锁。失败分支区分服务端 4xx与网络错误分别提示重新登录和检查网络两条路径都要先关 loading——本项目专门补过loading 永久挂在界面上的 bugcatch 里那句注释值得抄。顺着src/stores/user/action.ts重授权与令牌更新、src/screens/home/v2/store/index.ts重授权触发时机、src/utils/fetch/fetch.ts请求附加与过期兜底三个文件继续读这条链路的重要路径就齐了。【免费下载链接】Bangumi:electron: An unofficial https://bgm.tv ui first app client for Android and iOS, built with React Native. 一个无广告、以爱好为驱动、不以盈利为目的、专门做 ACG 的类似豆瓣的追番记录bgm.tv 第三方客户端。为移动端重新设计内置大量加强的网页端难以实现的功能且提供了相当的自定义选项。 目前已适配 iOS / Android。项目地址: https://gitcode.com/GitHub_Trending/ba/Bangumi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表