修改token刷新

This commit is contained in:
weiwei committed 2025-02-19 16:44:30 +08:00
1 parent 2adaae4151
commit e15e84aecf
4 files changed
+238 -257

No files matched your search

+7 -7
View File
@@ -1,8 +1,8 @@
{
"tabWidth": 2,
"useTabs": true,
"trailingComma": "all",
"semi": false,
"singleQuote": false,
"printWidth": 120
}
"tabWidth": 2,
"useTabs": true,
"trailingComma": "all",
"semi": false,
"singleQuote": false,
"printWidth": 140
}
+148 -172
View File
@@ -1,44 +1,84 @@
<template>
<crud-layout title="我的" bg="/static/page/blue-bg.png">
<view class="flex-row items-center">
<image
v-if="state.userInfo?.sysUser?.avatar"
class="w-100rpx h-100rpx rounded-full"
:src="filePath(state.userInfo.sysUser.avatar)"
mode="aspectFill"
/>
<image v-else class="w-100rpx h-100rpx rounded-full" src="/static/icon/default-avatar.png" mode="aspectFill" />
<view class="m-l-30rpx" v-if="state.userInfo?.sysUser?.userId">
<view class="flex-row items-center">
<text class="sub-text-30">{{ state.userInfo?.sysUser?.name }}</text>
<text class="iconfont icon-youjiantou color-#666666 m-l-20rpx"></text>
</view>
<text class="sub-text-26">ID {{ state.userInfo?.sysUser?.userId }}</text>
</view>
<view v-else class="flex-row items-center m-l-30rpx" @tap="onTabLogin">
<text class="sub-text-32">请先登录</text>
<text class="iconfont icon-youjiantou" />
</view>
</view>
<view class="m-t-40rpx rounded-20rpx shadow-main p-30rpx p-b-40rpx bg-white!">
<view class="service_item flex-row items-center justify-between" v-for="(v, i) in state.menuList" :key="i" @tap="onTabMenu(v, i)">
<view class="flex-row-center">
<image class="w-40rpx h-40rpx" :src="v.icon" mode="aspectFill" />
<text class="sub-text-28 m-l-20rpx">{{ v.text }}</text>
</view>
<view class="iconfont icon-youjiantou" />
</view>
</view>
<view v-if="isDevEnv()" class="flex-col-center m-t-40rpx m-x-30rpx">
<view class="w-full text-center sub-text-28 p-y-20rpx rounded-40rpx border-solid border border-#0094ff" @tap="doLogout">
退出登录
</view>
</view>
</crud-layout>
</template>
<script setup lang="ts">
import { reactive } from "vue"
import { onLoad, onShow } from "@dcloudio/uni-app"
import { isEmpty } from "radash"
import { filePath } from "@@/utils/file"
import { hooks, router, constant } from "@@/index"
import { merchantApi, userApi, walletApi } from "@@/api"
import { userApi } from "@@/api"
import isDevEnv from "@/sheep/utils/isDevEnv"
// 定义类型
interface ServiceItem {
interface MenuItem {
text: string
icon: string
router: string
}
// 服务列表配置
const DEFAULT_SERVICES: ServiceItem[] = [
{ text: "钱包管理", icon: "/static/icon/service_3.png", router: "/pages/wallet/list" },
{ text: "包厢预定", icon: "/static/icon/service_4.png", router: "/pages/boxes/apply-list" },
{ text: "外来人员就餐", icon: "/static/icon/service_1.png", router: "/pages/apply/foreign-list" },
{ text: "跨单位就餐", icon: "/static/icon/service_1.png", router: "/pages/apply/stride-list" },
{ text: "人脸管理", icon: "/static/icon/service_5.png", router: "/pages/human-face/list" },
]
const state = reactive<any>({
const state = reactive<{ wallet: any; userInfo: any; menuList: MenuItem[] }>({
wallet: [],
userInfo: {},
serviceList: [...DEFAULT_SERVICES],
menuList: [
{ text: "钱包管理", icon: "/static/icon/service_3.png", router: "/pages/wallet/list" },
{ text: "包厢预定", icon: "/static/icon/service_4.png", router: "/pages/boxes/apply-list" },
{ text: "外来人员就餐", icon: "/static/icon/service_1.png", router: "/pages/apply/foreign-list" },
{ text: "跨单位就餐", icon: "/static/icon/service_1.png", router: "/pages/apply/stride-list" },
{ text: "人脸管理", icon: "/static/icon/service_5.png", router: "/pages/human-face/list" },
],
})
const onTabLogin = () => (isDevEnv() ? router.jump("/pages/test/login") : checkAppInfo())
// 登录相关方法
const loginMethods = {
// 诗画环境登录
async shihuaLogin() {
const appInfo = await (window as any).CP2.getAppInfo()
if (appInfo.code !== 1) return false
// const appInfo = await (window as any).CP2.getAppInfo()
// if (appInfo.code !== 0) return false
const userInfo = await (window as any).CP2.getUserInfo()
const { accountId, sessionId } = userInfo.data
return doLogin(1, null, accountId, sessionId)
// const userInfo = await (window as any).CP2.getUserInfo()
// const { accountId, sessionId } = userInfo.data
// return doLogin(1, null, accountId, sessionId)
return false
},
// 浙政钉登录
@@ -58,120 +98,60 @@ const checkAppInfo = async () => {
// 先尝试诗画登录
const shihuaResult = await loginMethods.shihuaLogin()
if (shihuaResult) return
// 尝试浙政钉登录
const zhengdingResult = await loginMethods.zhengdingLogin()
const zhengdingResult = loginMethods.zhengdingLogin()
if (zhengdingResult) return
const tokenKey = constant.storageConstant.TOKEN
const userInfoKey = constant.storageConstant.USERINFO
const tokenStorage = uni.getStorageSync(tokenKey)
const userStorage = uni.getStorageSync(userInfoKey)
// 开发环境下跳转到测试登录页
if ((isDevEnv() && isEmpty(tokenStorage)) || isEmpty(userStorage)) {
router.jump("/pages/test/login")
}
uni.showToast({ title: "当前登录环境未知", icon: "none" })
} catch (error) {
console.error("登录失败:", error)
uni.showToast({ title: "登录失败", icon: "none" })
}
}
onLoad(() => {
if (isDevEnv()) {
state.serviceList.push({
text: "测试登录",
icon: "/static/icon/login.png",
router: "/pages/test/login",
})
}
checkAppInfo()
})
onShow(() => {
getUserInfo()
})
const getUserInfo = () => {
const userInfoKey = constant.storageConstant.USERINFO
const userStorage = uni.getStorageSync(userInfoKey)
if (!isEmpty(userStorage)) {
state.userInfo = userStorage
} else {
state.userInfo = {}
}
}
// {
// "accessToken": {
// "tokenValue": "06d43795-a3ef-4019-a70f-25830e7183a4",
// "issuedAt": "2025-01-24T11:10:30.423604599Z",
// "expiresAt": "2025-01-25T11:10:30.423604599Z",
// "tokenType": {
// "value": "Bearer"
// },
// "scopes": []
// },
// "refreshToken": {
// "tokenValue": "FmRVP4l3d7t3263h9LeKnV8hdU0lPasMuI1wank_EbylRQPNLm7XP4_OtUV-zH9AbMZc9f14Qk2IXhw-33fWMGGKipVY8crbvVyWV5DeAcfeU8fYyQuNDh9rw3Dq0w75",
// "issuedAt": "2025-01-24T11:10:30.423699280Z",
// "expiresAt": "2025-01-31T11:10:30.423699280Z"
// }
// }
const doLogin = async (loginType, token, accountId, sessionId) => {
// loginType 1:诗画登录 2:浙政钉登录
const doLogin = async (loginType: number, token: string, accountId: string | null, sessionId: string | null) => {
const tokenKey = constant.storageConstant.TOKEN
const userInfoKey = constant.storageConstant.USERINFO
const tokenStorage = uni.getStorageSync(tokenKey)
const userStorage = uni.getStorageSync(userInfoKey)
if (isEmpty(tokenStorage) || isEmpty(userStorage)) {
uni.showLoading({
title: "登录中...",
mask: true,
})
let authData
if (loginType == 1) {
switch (loginType) {
case 1:
// 诗画
const params = {
accountId: accountId,
sessionId: sessionId,
// const params = {
// accountId: accountId,
// sessionId: sessionId,
// }
// const response = await userApi.authsh(params)
// authData = response.data
break
case 2:
if (isEmpty(tokenStorage) || isEmpty(userStorage)) {
try {
uni.showLoading({
title: "登录中...",
mask: true,
})
const res = await userApi.auth(token)
if (res?.code === 0) {
uni.setStorageSync(tokenKey, res.data)
uni.hideLoading()
uni.showToast({ title: "登录成功", icon: "none" })
const { data: userData } = await userApi.userInfo()
uni.setStorageSync(userInfoKey, userData)
getStorageUserInfo()
} else {
uni.hideLoading()
uni.showToast({ title: "获取浙政钉用户信息失败", icon: "none" })
}
} catch (error) {
uni.hideLoading()
uni.showToast({ title: "错误", icon: "none" })
}
}
const response = await userApi.authsh(params)
authData = response.data
} else if (loginType == 2) {
//浙政钉
const response: any = await userApi.auth(token)
if (!response) return
authData = response.data
} else {
//后期手机号+验证码登录
if (isDevEnv()) router.jump("/pages/test/login")
uni.hideLoading()
return
}
// const {data: authData} = await userApi.auth(token);
console.log("authData", authData)
uni.setStorageSync(tokenKey, authData)
try {
const { data: userData } = await userApi.userInfo()
uni.setStorageSync(userInfoKey, userData)
getUserInfo()
uni.hideLoading()
// uni.showToast({ title: "登录成功", icon: "none" })
} catch (error) {
uni.hideLoading()
uni.showToast({ title: "登录失败", icon: "none" })
}
}
}
const toService = async (v: any, i: number) => {
if (i == state.serviceList.length - 1) {
router.jump(v.router)
return
}
const isLogin = await hooks.useCheckLogin()
if (isLogin) {
router.jump(v.router)
break
default:
uni.showToast({ title: "当前登录环境未知", icon: "none" })
}
}
@@ -191,64 +171,60 @@ const doLogout = async () => {
uni.showToast({ title: "退出登录成功", icon: "none" })
}
const onTabLogin = () => {
if (isDevEnv()) {
router.jump("/pages/test/login")
} else {
checkAppInfo()
const onTabMenu = async (v: MenuItem, i: number) => {
if (i == state.menuList.length - 1) {
router.jump(v.router)
return
}
const isLogin = await hooks.useCheckLogin()
if (isLogin) {
router.jump(v.router)
}
}
const getStorageUserInfo = () => {
const tokenKey = constant.storageConstant.TOKEN
const userInfoKey = constant.storageConstant.USERINFO
const tokenStorage = uni.getStorageSync(tokenKey)
const userStorage = uni.getStorageSync(userInfoKey)
if (!isEmpty(userStorage) && !isEmpty(tokenStorage)) {
state.userInfo = userStorage
} else {
state.userInfo = {}
}
}
onLoad(() => {
isDevEnv()
? state.menuList.push({
text: "测试登录",
icon: "/static/icon/login.png",
router: "/pages/test/login",
})
: checkAppInfo()
})
onShow(() => {
getStorageUserInfo()
})
// {
// "accessToken": {
// "tokenValue": "06d43795-a3ef-4019-a70f-25830e7183a4",
// "issuedAt": "2025-01-24T11:10:30.423604599Z",
// "expiresAt": "2025-01-25T11:10:30.423604599Z",
// "tokenType": {
// "value": "Bearer"
// },
// "scopes": []
// },
// "refreshToken": {
// "tokenValue": "FmRVP4l3d7t3263h9LeKnV8hdU0lPasMuI1wank_EbylRQPNLm7XP4_OtUV-zH9AbMZc9f14Qk2IXhw-33fWMGGKipVY8crbvVyWV5DeAcfeU8fYyQuNDh9rw3Dq0w75",
// "issuedAt": "2025-01-24T11:10:30.423699280Z",
// "expiresAt": "2025-01-31T11:10:30.423699280Z"
// }
// }
</script>
<template>
<crud-layout title="我的" bg="/static/page/blue-bg.png">
<view class="flex-row items-center">
<image
class="w-100rpx h-100rpx rounded-full"
v-if="state.userInfo?.sysUser?.avatar"
:src="filePath(state.userInfo.sysUser.avatar)"
mode="aspectFill"
/>
<image class="w-100rpx h-100rpx rounded-full" v-else src="/static/icon/default-avatar.png" mode="aspectFill" />
<view class="m-l-30rpx" v-if="state.userInfo?.sysUser?.userId">
<view class="flex-row items-center">
<text class="sub-text-30">{{ state.userInfo?.sysUser?.name }}</text>
<text class="iconfont icon-youjiantou color-#666666 m-l-20rpx"></text>
</view>
<text class="sub-text-26">ID {{ state.userInfo?.sysUser?.userId }}</text>
</view>
<view v-else class="flex-row items-center m-l-30rpx" @tap="onTabLogin">
<text class="sub-text-32">请先登录</text>
<text class="iconfont icon-youjiantou"></text>
</view>
</view>
<view class="m-t-40rpx rounded-20rpx shadow-main p-30rpx p-b-40rpx bg-white!">
<view
class="service_item flex-row items-center justify-between"
v-for="(v, i) in state.serviceList"
:key="i"
@tap="toService(v, i)"
>
<view class="flex-row-center">
<image class="w-40rpx h-40rpx" :src="v.icon" mode="aspectFill" />
<text class="sub-text-28 m-l-20rpx">{{ v.text }}</text>
</view>
<view class="iconfont icon-youjiantou"></view>
</view>
</view>
<view v-if="isDevEnv()" class="flex-col-center m-t-40rpx m-x-30rpx">
<view
class="w-full text-center sub-text-28 p-y-20rpx rounded-40rpx border-solid border border-#0094ff"
@tap="doLogout"
>
退出登录
</view>
</view>
</crud-layout>
</template>
<style lang="scss" scoped>
.service_item:not(:last-child) {
padding-bottom: 40rpx;
+2 -2
View File
@@ -16,7 +16,7 @@ interface LoginParams {
export default {
// 获取用户信息
userInfo: () =>
userInfo: (): Promise<any> =>
request({
url: "/admin/user/info",
method: "GET",
@@ -27,7 +27,7 @@ export default {
}),
// 浙政钉授权
auth: (token: string) =>
auth: (token: string): Promise<any> =>
request({
url: `/thirdparty/thirdPartyOAth2/zzdsso/${token}`,
method: "GET",
+81 -76
View File
@@ -1,40 +1,15 @@
import Request from "luch-request"
import { isEmpty } from "radash"
import { userApi } from "@@/api"
import { router, constant } from "@@/index"
import isDevEnv from "./isDevEnv"
import { router, constant } from "@@/index"
import { userApi } from "../api"
// 定义常量
const TOKEN_KEY = constant.storageConstant.TOKEN
const USER_INFO_KEY = constant.storageConstant.USERINFO
const REFRESH_TOKEN_URL = "/auth/oauth2/token?grant_type=refresh_token"
// 定义类型
interface LoadingInstance {
target: any
count: number
}
// 默认请求配置
const defaultOptions = {
showSuccess: false,
// 成功提醒 默认使用后端返回值
successMsg: "",
// 显示失败消息 默认显示
showError: true,
// 失败提醒 默认使用后端返回信息
errorMsg: "",
// 显示请求时loading模态框 默认显示
showLoading: true,
// loading提醒文字
loadingMsg: "加载中",
// 需要授权才能请求 默认放开
auth: false,
}
// Loading 管理
const LoadingInstance: LoadingInstance = {
const LoadingInstance: { target: any; count: number } = {
target: null,
count: 0,
}
@@ -55,7 +30,21 @@ const http = new Request({
"Content-Type": "application/json;charset=UTF-8",
},
withCredentials: true,
custom: defaultOptions,
custom: {
showSuccess: false,
// 成功提醒 默认使用后端返回值
successMsg: "",
// 显示失败消息 默认显示
showError: true,
// 失败提醒 默认使用后端返回信息
errorMsg: "",
// 显示请求时loading模态框 默认显示
showLoading: true,
// loading提醒文字
loadingMsg: "加载中",
// 需要授权才能请求 默认放开
auth: false,
},
})
// 处理请求头 Authorization
@@ -71,14 +60,14 @@ http.interceptors.request.use((config) => {
const tokenStorage = uni.getStorageSync(TOKEN_KEY)
const config_header: any = config?.header || {}
// 设置通用请求头
// 设置租户请求头
config_header["TENANT-ID"] = "1"
if (import.meta.env.VITE_IS_TOC === "true") {
config_header["CLIENT-TOC"] = "Y"
}
// 设置认证头
if (config.url !== "/sys/auth/refresh-token") {
if (config.url !== "/sys/auth/refresh-token" && config.url !== "/auth/oauth2/token") {
const authHeader = getAuthHeader(tokenStorage)
if (authHeader) {
config_header.Authorization = authHeader
@@ -131,7 +120,7 @@ const responseHelpers = {
// 处理成功提示
handleSuccessMessage(response: any) {
const { data, config } = response
if (data.code === 200 && data.message && config?.custom?.showSuccess) {
if (data.code === 0 && data.message && config?.custom?.showSuccess) {
uni.showToast({
title: config.custom.successMsg || data.message,
icon: "none",
@@ -142,21 +131,67 @@ const responseHelpers = {
// 处理登录过期
handleLoginExpired() {
uni.removeStorageSync(TOKEN_KEY)
uni.removeStorageSync(USER_INFO_KEY)
uni.showToast({ title: "登录已过期,请重新登录", icon: "none" })
router.jump("/pages/test/login")
isDevEnv() ? router.jump("/pages/test/login") : router.jump("/pages/tabbar/my")
},
// 检查并刷新token
async checkAndRefreshToken(tokenStorage: any) {
const { refresh_token, expiresTime } = tokenStorage
if (!refresh_token || !expiresTime) return
checkAndRefreshToken(tokenStorage: any) {
const { accessToken } = tokenStorage
if (!accessToken) return
const now = new Date().getTime()
const remain = expiresTime - now
if (remain <= 1000 * 60 * 20) {
const refreshData = await userApi.refreshToken({ refreshToken: refresh_token })
if (!isEmpty(refreshData.data)) {
uni.setStorageSync(TOKEN_KEY, refreshData.data)
const issuedDate = new Date(accessToken.issuedAt).getTime()
console.log(new Date().getTime() - issuedDate)
if (new Date().getTime() - issuedDate >= 1000 * 1) {
// try {
// const refreshData: any = await request({
// url: "/auth/oauth2/token",
// method: "POST",
// params: { grant_type: "refresh_token" },
// data: JSON.stringify({
// scope: "server",
// refresh_token: tokenStorage.refreshToken.tokenValue,
// }),
// header: {
// skipToken: true,
// Authorization: "Basic " + window.btoa(import.meta.env.VITE_OAUTH2_APP_CLIENT),
// "content-type": "application/x-www-form-urlencoded",
// },
// })
// if (refreshData.code === 0 && !isEmpty(refreshData.data)) {
// console.log("refreshData.data", refreshData.data)
// // 刷新成功,更新token并重试请求
// uni.setStorageSync(TOKEN_KEY, refreshData.data)
// }
// } catch (refreshError) {
// console.error("Token refresh failed:", refreshError)
// }
try {
const params = new URL(window.location.href).searchParams
const token = params.get("uabaseToken")
if (token) {
userApi.auth(token).then((res) => {
if (res?.code === 0) {
uni.setStorageSync(TOKEN_KEY, res.data)
} else {
uni.removeStorageSync(TOKEN_KEY)
uni.removeStorageSync(USER_INFO_KEY)
// uni.showToast({ title: "登录已过期", icon: "none" })
return Promise.resolve({
code: 1,
data: "登录已过期",
msg: "登录已过期",
})
}
})
}
} catch (error) {
uni.hideLoading()
uni.showToast({ title: "错误", icon: "none" })
}
}
},
@@ -165,6 +200,8 @@ const responseHelpers = {
// 响应拦截器
http.interceptors.response.use(
async (response) => {
console.log("response", response)
// 关闭loading
if (response.config?.custom?.showLoading) {
closeLoading()
@@ -195,19 +232,15 @@ http.interceptors.response.use(
// Token 刷新检查
const tokenStorage = uni.getStorageSync(TOKEN_KEY)
if (!isEmpty(tokenStorage) && response.config.url !== "/sys/auth/refresh-token") {
if (!isEmpty(tokenStorage) && !response.config.url?.startsWith("/thirdparty/thirdPartyOAth2/zzdsso")) {
await responseHelpers.checkAndRefreshToken(tokenStorage)
}
return Promise.resolve(response.data)
},
// 错误拦截器
async (error) => {
(error) => {
// 处理 424 错误(token过期)
if (error.statusCode === 424) {
const tokenStorage = uni.getStorageSync(TOKEN_KEY)
if (isDevEnv()) {
// 开发环境直接清除token并跳转登录
uni.removeStorageSync(TOKEN_KEY)
@@ -216,34 +249,6 @@ http.interceptors.response.use(
router.jump("/pages/test/login")
return false
}
// 生产环境尝试刷新token
if (tokenStorage?.refreshToken?.tokenValue) {
try {
const refreshData = await request({
url: REFRESH_TOKEN_URL,
method: "POST",
data: JSON.stringify({
scope: "server",
refresh_token: tokenStorage.refreshToken.tokenValue,
}),
header: {
Authorization: "Basic " + window.btoa(import.meta.env.VITE_OAUTH2_APP_CLIENT),
"content-type": "application/x-www-form-urlencoded",
},
})
if (refreshData.code === 0) {
// 刷新成功,更新token并重试请求
uni.setStorageSync(TOKEN_KEY, refreshData.data)
const config = error.config
config.header!.Authorization = `Bearer ${refreshData.data.accessToken.tokenValue}`
return http.request(config)
}
} catch (refreshError) {
console.error("Token refresh failed:", refreshError)
}
}
}
// 显示错误信息