feat(auth): 实现禁用用户拦截机制防止禁用用户访问
- 新增工具函数 isUserDisabled 判断用户是否被禁用 - 新增 blockDisabledUser 函数拦截禁用用户,清除登录态弹窗提示并跳转登录页 - UserContext 启动时异步校验用户状态,禁用则拦截并退出 - UserContext 的 refreshUser、syncFromStorage、loginUser 接口均增加禁用检查 - 登录相关 API 登录前校验用户状态,禁用时拒绝登录 - 登录页面保存登录态前校验,禁用用户弹窗提示阻断登录流程 - 用户状态定义:status 0 为正常,1 为禁用 - 新增用户管理页支持查询和管理用户状态 - 文档中补充用户状态与禁用机制说明及拦截层级介绍
This commit is contained in:
@@ -3,6 +3,7 @@ import Taro from '@tarojs/taro'
|
||||
import type { User } from '@/api/system/user/model'
|
||||
import { getUserInfo } from '@/api/layout'
|
||||
import { saveStorageByLoginUser, clearStorageByLoginUser } from '@/utils/server'
|
||||
import { isUserDisabled, blockDisabledUser } from '@/utils/auth'
|
||||
|
||||
interface UserContextType {
|
||||
user: User | null
|
||||
@@ -25,11 +26,14 @@ export const UserProvider: React.FC<{ children: ReactNode }> = ({ children }) =>
|
||||
const token = Taro.getStorageSync('access_token')
|
||||
const userData = Taro.getStorageSync('User')
|
||||
|
||||
// 情况1: 有本地token和用户数据,直接使用
|
||||
// 情况1: 有本地token和用户数据,先用缓存快速显示,再异步校验状态
|
||||
if (token && userData) {
|
||||
const parsed = typeof userData === 'string' ? JSON.parse(userData) : userData
|
||||
setUser(parsed as User)
|
||||
setLoading(false)
|
||||
|
||||
// 异步从服务端校验用户是否被禁用(防止被禁用后仍可使用)
|
||||
verifyUserStatusFromServer()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -37,6 +41,12 @@ export const UserProvider: React.FC<{ children: ReactNode }> = ({ children }) =>
|
||||
if (token) {
|
||||
try {
|
||||
const userInfo = await getUserInfo()
|
||||
if (isUserDisabled(userInfo)) {
|
||||
blockDisabledUser(userInfo)
|
||||
setUser(null)
|
||||
setLoading(false)
|
||||
return
|
||||
}
|
||||
setUser(userInfo)
|
||||
setLoading(false)
|
||||
return
|
||||
@@ -46,13 +56,38 @@ export const UserProvider: React.FC<{ children: ReactNode }> = ({ children }) =>
|
||||
}
|
||||
}
|
||||
|
||||
// 无本地token,保持未登录状态(已禁用静默登录)
|
||||
// 无本地token,保持未登录状态
|
||||
setLoading(false)
|
||||
}
|
||||
init()
|
||||
}, [])
|
||||
|
||||
/** 异步从服务端拉取最新用户信息,校验是否被禁用 */
|
||||
const verifyUserStatusFromServer = async () => {
|
||||
try {
|
||||
const userInfo = await getUserInfo()
|
||||
if (isUserDisabled(userInfo)) {
|
||||
// 用户已被禁用,拦截
|
||||
blockDisabledUser(userInfo)
|
||||
setUser(null)
|
||||
} else {
|
||||
// 状态正常,更新本地缓存和状态
|
||||
const token = Taro.getStorageSync('access_token')
|
||||
if (token) {
|
||||
saveStorageByLoginUser(token, userInfo)
|
||||
}
|
||||
setUser(userInfo)
|
||||
}
|
||||
} catch {
|
||||
// 网络错误或token失效,静默处理(已有缓存的登录态可用)
|
||||
}
|
||||
}
|
||||
|
||||
const loginUser = (token: string, userInfo: User) => {
|
||||
if (isUserDisabled(userInfo)) {
|
||||
blockDisabledUser(userInfo)
|
||||
return
|
||||
}
|
||||
saveStorageByLoginUser(token, userInfo)
|
||||
setUser(userInfo)
|
||||
}
|
||||
@@ -68,6 +103,12 @@ export const UserProvider: React.FC<{ children: ReactNode }> = ({ children }) =>
|
||||
const userData = Taro.getStorageSync('User')
|
||||
if (token && userData) {
|
||||
const parsed = typeof userData === 'string' ? JSON.parse(userData) : userData
|
||||
// 快速检查缓存中的 status,如果已禁用则拦截
|
||||
if (isUserDisabled(parsed)) {
|
||||
blockDisabledUser(parsed)
|
||||
setUser(null)
|
||||
return false
|
||||
}
|
||||
setUser(parsed as User)
|
||||
return true
|
||||
}
|
||||
@@ -81,6 +122,11 @@ export const UserProvider: React.FC<{ children: ReactNode }> = ({ children }) =>
|
||||
if (!Taro.getStorageSync('access_token')) return null
|
||||
try {
|
||||
const userInfo = await getUserInfo()
|
||||
if (isUserDisabled(userInfo)) {
|
||||
blockDisabledUser(userInfo)
|
||||
setUser(null)
|
||||
return null
|
||||
}
|
||||
const token = Taro.getStorageSync('access_token')
|
||||
if (token) {
|
||||
saveStorageByLoginUser(token, userInfo)
|
||||
|
||||
Reference in New Issue
Block a user