在 uni-app 面试中,登录鉴权与 token 无感刷新是一道高频且能区分候选人工程化能力的题目。它考察的不仅是对 API 的熟悉程度,更是对前端安全、请求拦截、并发处理以及用户体验的综合把控。本文将从面试回答的角度,系统拆解 uni-app 中实现登录鉴权与无感刷新的完整方案。
一、面试官想听到什么?
当面试官抛出这个问题时,他通常期待你从以下几个层面展开:
- 登录鉴权的整体流程:从用户输入账号密码到获取 token,再到后续请求携带 token。
- token 的存储与使用:存在哪里?如何附加到请求上?
- 无感刷新的触发时机与实现:什么时候刷新?如何保证用户无感知?
- 并发请求的处理:多个请求同时发现 token 过期怎么办?
- 边界情况与安全性:刷新失败怎么办?如何防止死循环?
下面我们逐一拆解。
二、登录鉴权的基本流程
1. 登录与 token 获取
用户提交账号密码后,前端调用登录接口,服务端返回 access_token 和 refresh_token。通常 access_token 有效期较短(如 2 小时),refresh_token 有效期较长(如 7 天)。
// 登录请求示例
const res = await uni.request({
url: '/api/login',
method: 'POST',
data: { username, password }
})
const { accessToken, refreshToken } = res.data
uni.setStorageSync('access_token', accessToken)
uni.setStorageSync('refresh_token', refreshToken)
2. 请求拦截:自动携带 token
在 uni-app 中,推荐对 uni.request 进行二次封装,利用拦截器统一附加 token。
const request = (options) => {
return new Promise((resolve, reject) => {
const token = uni.getStorageSync('access_token')
uni.request({
...options,
header: {
...options.header,
Authorization: token ? `Bearer ${token}` : ''
},
success: (res) => {
// 处理响应
if (res.statusCode === 401) {
// token 过期,触发刷新逻辑
handleTokenExpired(options, resolve, reject)
} else {
resolve(res.data)
}
},
fail: reject
})
})
}
3. 路由拦截:未登录跳转登录页
除了请求拦截,还需要在页面跳转时校验登录态。可以在 App.vue 的 onLaunch 或使用 uni.addInterceptor 对 navigateTo、switchTab 等路由方法进行拦截。
// 路由拦截示例
const whiteList = ['/pages/login/index', '/pages/register/index']
uni.addInterceptor('navigateTo', {
invoke(args) {
const token = uni.getStorageSync('access_token')
const path = args.url.split('?')[0]
if (!token && !whiteList.includes(path)) {
uni.redirectTo({ url: '/pages/login/index' })
return false
}
return true
}
})
三、Token 无感刷新的核心实现
无感刷新的关键在于:当 access_token 过期时,前端自动使用 refresh_token 换取新的 access_token,并重新发起之前失败的请求,用户全程无感知。
1. 基本思路
- 响应拦截中判断状态码为 401(或业务码表示 token 过期)。
- 调用刷新接口,用
refresh_token换取新的access_token。 - 刷新成功后,更新本地存储,并重试之前失败的请求。
- 刷新失败(如
refresh_token也过期),则清空登录态,跳转登录页。
2. 并发请求的难题
如果同时有 5 个请求都返回 401,不能发起 5 次刷新请求。需要引入一个“刷新中”的标志位,让后续请求等待刷新完成后再重试。
let isRefreshing = false
let requestQueue = []
const handleTokenExpired = (options, resolve, reject) => {
if (isRefreshing) {
// 正在刷新,将请求加入队列
requestQueue.push({ options, resolve, reject })
return
}
isRefreshing = true
const refreshToken = uni.getStorageSync('refresh_token')
uni.request({
url: '/api/refresh',
method: 'POST',
data: { refreshToken },
success: (res) => {
if (res.statusCode === 200 && res.data.accessToken) {
const newToken = res.data.accessToken
uni.setStorageSync('access_token', newToken)
// 重试当前请求
retryRequest(options).then(resolve).catch(reject)
// 重试队列中的请求
requestQueue.forEach(({ options, resolve, reject }) => {
retryRequest(options).then(resolve).catch(reject)
})
} else {
// 刷新失败,清空登录态
clearAuth()
uni.redirectTo({ url: '/pages/login/index' })
requestQueue.forEach(({ reject }) => reject(new Error('登录已过期')))
}
},
fail: () => {
clearAuth()
uni.redirectTo({ url: '/pages/login/index' })
},
complete: () => {
isRefreshing = false
requestQueue = []
}
})
}
3. 重试请求的封装
retryRequest 需要重新执行原始请求,并携带新的 token。
const retryRequest = (options) => {
return new Promise((resolve, reject) => {
const token = uni.getStorageSync('access_token')
uni.request({
...options,
header: {
...options.header,
Authorization: `Bearer ${token}`
},
success: (res) => resolve(res.data),
fail: reject
})
})
}
四、面试加分项:边界情况与优化
1. 防止刷新死循环
如果刷新接口本身也返回 401,不能再触发刷新逻辑。可以在 handleTokenExpired 中判断当前请求的 URL 是否为刷新接口,或者设置一个最大重试次数。
2. 刷新接口的并发控制
除了用 isRefreshing 标志位,还可以使用 Promise 缓存:当刷新请求进行中时,返回同一个 Promise 给所有等待者。
let refreshPromise = null
const refreshToken = () => {
if (!refreshPromise) {
refreshPromise = doRefresh().finally(() => {
refreshPromise = null
})
}
return refreshPromise
}
3. 主动刷新与被动刷新结合
除了被动等待 401,还可以在 access_token 即将过期前主动刷新。例如在 App.vue 的 onShow 中检查 token 剩余有效期,若小于 5 分钟则提前刷新,进一步降低用户感知。
4. 安全性考虑
refresh_token应存储在更安全的位置,避免 XSS 攻击。在 uni-app 中,uni.setStorageSync是明文存储,敏感场景可考虑加密后再存储。- 刷新接口应使用 HTTPS,并设置合理的过期时间。
- 退出登录时务必清空本地 token,并通知服务端使
refresh_token失效。
五、总结
在 uni-app 中实现登录鉴权与 token 无感刷新,核心是三点:
- 封装请求:统一附加 token,统一处理 401。
- 队列与标志位:解决并发刷新问题,避免重复刷新。
- 重试机制:刷新成功后自动重试失败请求,实现用户无感知。
面试回答时,建议先讲整体流程,再深入并发处理和边界情况,最后补充主动刷新、安全性等加分点。这样既能体现你的工程化思维,也能展示对细节的把控,给面试官留下深刻印象。
未经允许不得转载:任鹏个人博客 » uni-app 中如何实现登录鉴权与 token 无感刷新?

