做统一认证登录

This commit is contained in:
2026-09-19 21:44:04 +08:00
parent 1a4471e34d
commit fa281363fb
78 changed files with 8127 additions and 1442 deletions
+69 -2
View File
@@ -2,7 +2,7 @@
* 统一请求封装:baseURL、Bearer Token、业务 code 处理。
* 接口地址统一从 .env 的 VITE_API_BASE_URL 读取(见 api/config.js)。
*/
import { getToken, logout } from '@/utils/auth.js'
import { getToken, logout, isSSOEnabled, getRefreshToken, setAuthTokens } from '@/utils/auth.js'
import { resolveBaseURL, getBaseURL } from '@/api/config.js'
export { getBaseURL }
@@ -11,6 +11,63 @@ function buildFullUrl(url) {
return resolveBaseURL().replace(/\/$/, '') + url
}
/**
* 用 refresh_token 换取新的访问令牌(独立封装,避免与 request 递归调用)。
* 仅在统一认证模式(VITE_AUTH_MODE=sso)下使用。
*/
function refreshAccessToken() {
return new Promise((resolve, reject) => {
const refresh = getRefreshToken()
if (!refresh) {
reject(new Error('无刷新令牌'))
return
}
uni.request({
url: buildFullUrl('/auth/token'),
method: 'POST',
header: { 'Content-Type': 'application/x-www-form-urlencoded' },
data: `grant_type=refresh_token&refresh_token=${encodeURIComponent(refresh)}&client_id=yz-uniapp`,
timeout: 10000,
success(res) {
const body = res.data || {}
if (res.statusCode === 200 && body.access_token) {
setAuthTokens(body)
resolve(body.access_token)
} else {
reject(new Error((body && (body.error || body.msg)) || '刷新失败'))
}
},
fail: reject
})
})
}
/** 续期后重放原请求,返回与 request 一致的 data 解包结果 */
async function replayRequest(options) {
const token = await refreshAccessToken()
const res = await new Promise((resolve, reject) => {
uni.request({
url: buildFullUrl(options.url),
method: (options.method || 'GET').toUpperCase(),
data: options.data || {},
header: Object.assign(
{ 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
options.header || {}
),
timeout: options.timeout || 30000,
success: (r) => resolve(r.data || {}),
fail: reject
})
})
if (typeof res.code !== 'undefined' && res.code !== 200) {
const err = new Error(res.msg || '操作失败')
err.code = res.code
err.response = res
throw err
}
return typeof res.data !== 'undefined' ? res.data : res
}
/**
* @param {{ url: string, method?: string, data?: object, header?: object, silent?: boolean }} options
* @returns {Promise<any>} 业务 data 字段(若无 data 则返回整包)
@@ -35,11 +92,21 @@ export function request(options = {}) {
data: data || {},
header: headers,
timeout,
success(res) {
async success(res) {
const status = res.statusCode
const body = res.data || {}
if (status === 401 || body.code === 401) {
// 统一认证模式:先用 refresh_token 静默续期并重放请求,
// 续期失败(令牌过期/被吊销/被踢下线)才真正登出。
if (isSSOEnabled()) {
try {
resolve(await replayRequest(options))
return
} catch (e) {
// 续期失败,继续走下面的登出流程
}
}
logout()
if (!silent) {
uni.showToast({ title: body.msg || '请重新登录', icon: 'none' })