casualroom/apps/fenghuo/web/components/providers/auth-provider.tsx

331 lines
11 KiB
TypeScript
Raw Normal View History

2025-07-28 07:50:50 +08:00
'use client';
import { createContext, useContext, useState, useEffect, useCallback } from 'react';
import { oidcClient } from '@/lib/auth/oidc-client';
import { TokenManager } from '@/lib/auth/token-manager';
import { useTRPC } from '@fenghuo/client';
import { useQuery } from '@tanstack/react-query';
import {
SystemPermission,
PermissionUtils,
SystemRole,
SystemRoleConfig,
type PermissionMeta,
UserWithRelations,
userWithRelationsSelect
} from '@fenghuo/common';
import { useToken } from '@/components/providers/token-provider';
import { Prisma, Role } from '@fenghuo/db/index';
// 导出权限相关类型和工具
export {
SystemPermission,
PermissionUtils,
SystemRole,
SystemRoleConfig,
type PermissionMeta
} from '@fenghuo/common';
// 扩展用户接口
export interface User extends Omit<UserWithRelations, 'roles'> {
roles: string[]; // 角色名称数组
permissions?: SystemPermission[]; // 使用新的权限系统
}
interface AuthProviderProps {
children: React.ReactNode;
}
interface AuthContextType {
// 用户状态
user: User | null;
isLoading: boolean;
isAuthenticated: boolean;
// 权限检查方法
hasRole: (role: string) => boolean;
hasPermission: (permission: SystemPermission) => boolean;
hasAnyRole: (roles: string[]) => boolean;
hasAnyPermission: (permissions: SystemPermission[]) => boolean;
hasAllPermissions: (permissions: SystemPermission[]) => boolean;
isSuperAdmin: () => boolean;
isAdmin: () => boolean;
// 认证操作
login: (username: string, password: string) => Promise<void>;
register: (userData: Prisma.UserCreateArgs) => Promise<void>;
logout: () => Promise<void>;
getAccessToken: () => Promise<string | null>;
loadUser: () => Promise<void>;
}
const AuthContext = createContext<AuthContextType | null>(null);
export default function AuthProvider({ children }: AuthProviderProps) {
const { refreshToken, clearTokens } = useToken();
const [userId, setUserId] = useState<string | null>(null);
const [user, setUser] = useState<User | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [isAuthenticated, setIsAuthenticated] = useState(false);
const trpc = useTRPC();
// 修改 tRPC 查询,包含角色权限
const { data: backendUser, error: userError, isLoading: isUserLoading } = useQuery({
...trpc.user.findFirst.queryOptions({
where: { id: userId! },
select: userWithRelationsSelect
}),
enabled: !!userId,
retry: false,
}) as { data: UserWithRelations | null, error: Error | null, isLoading: boolean };
// 更新用户状态当后端用户数据加载完成时
useEffect(() => {
if (backendUser) {
const userRoles = backendUser.roles?.map(r => r.slug) || [];
// 传递完整的角色数据给权限计算函数
const userPermissions = getUserPermissions(backendUser.roles || []);
const appUser: User = {
...backendUser,
roles: userRoles,
permissions: userPermissions,
};
setUser(appUser);
} else if (userError) {
console.error('获取用户数据失败:', userError);
setUser(null);
setIsAuthenticated(false);
TokenManager.clearTokens();
}
}, [backendUser, userError]);
// 加载用户信息
const loadUser = useCallback(async () => {
setIsLoading(true);
try {
const accessToken = await TokenManager.getValidAccessToken();
if (accessToken) {
// 只获取 OIDC 用户信息来获得用户 ID
const oidcUser = await oidcClient.getUserInfo(accessToken);
setUserId(oidcUser.sub);
setIsAuthenticated(true);
// 触发 QueryProvider 刷新 token
refreshToken();
} else {
setUserId(null);
setUser(null);
setIsAuthenticated(false);
setIsLoading(false);
}
} catch (error) {
console.error('加载用户信息失败:', error);
setUserId(null);
setUser(null);
setIsAuthenticated(false);
TokenManager.clearTokens();
setIsLoading(false);
}
}, [refreshToken]);
// 当 tRPC 查询完成时更新 loading 状态
useEffect(() => {
if (userId && !isUserLoading) {
setIsLoading(false);
}
}, [userId, isUserLoading]);
// 初始化时加载用户信息
useEffect(() => {
loadUser();
}, [loadUser]);
// 登录功能
const login = useCallback(async (username: string, password: string): Promise<void> => {
setIsLoading(true);
try {
// 调用 OIDC 服务登录
const tokenResponse = await oidcClient.loginWithPassword(username, password);
// 保存令牌
TokenManager.saveTokens(tokenResponse);
// 获取用户信息并设置 userId让 tRPC 查询处理剩余逻辑
const oidcUser = await oidcClient.getUserInfo(tokenResponse.access_token);
setUserId(oidcUser.sub);
setIsAuthenticated(true);
// 登录成功后直接刷新token context
await refreshToken();
} catch (error) {
console.error('登录失败:', error);
throw error;
} finally {
setIsLoading(false);
}
}, [refreshToken]);
// 注册功能
const register = useCallback(async (userData: Prisma.UserCreateArgs): Promise<void> => {
setIsLoading(true);
try {
await oidcClient.register(userData);
} catch (error) {
console.error('注册失败:', error);
throw error;
} finally {
setIsLoading(false);
}
}, []);
// 退出登录功能
const logout = useCallback(async () => {
setIsLoading(true);
try {
const accessToken = TokenManager.getAccessToken();
if (accessToken) {
await oidcClient.logout(accessToken);
}
} catch (error) {
console.error('退出登录失败:', error);
} finally {
TokenManager.clearTokens();
setUserId(null);
setUser(null);
setIsAuthenticated(false);
setIsLoading(false);
// 退出时清除token
clearTokens();
}
}, [clearTokens]);
// 获取访问令牌
const getAccessToken = useCallback(async () => {
return await TokenManager.getValidAccessToken();
}, []);
// 权限检查方法 - 使用新的权限系统
const hasRole = useCallback((role: string): boolean => {
return user?.roles.includes(role) || false;
}, [user]);
const hasPermission = useCallback((permission: SystemPermission): boolean => {
if (!user?.permissions) return false;
return PermissionUtils.hasPermission(user.permissions, permission);
}, [user]);
const hasAnyRole = useCallback((roles: string[]): boolean => {
return roles.some(role => hasRole(role));
}, [hasRole]);
const hasAnyPermission = useCallback((permissions: SystemPermission[]): boolean => {
if (!user?.permissions) return false;
return PermissionUtils.hasAnyPermission(user.permissions, permissions);
}, [user]);
const hasAllPermissions = useCallback((permissions: SystemPermission[]): boolean => {
if (!user?.permissions) return false;
return PermissionUtils.hasAllPermissions(user.permissions, permissions);
}, [user]);
// 检查是否为超级管理员
const isSuperAdmin = useCallback((): boolean => {
return hasPermission(SystemPermission.SUPER_ADMIN);
}, [hasPermission]);
// 检查是否为管理员级别
const isAdmin = useCallback((): boolean => {
return hasRole(SystemRole.SUPER_ADMIN) || hasRole(SystemRole.ADMIN);
}, [hasRole]);
const contextValue: AuthContextType = {
user,
isLoading,
isAuthenticated,
hasRole,
hasPermission,
hasAnyRole,
hasAnyPermission,
hasAllPermissions,
isSuperAdmin,
isAdmin,
login,
register,
logout,
getAccessToken,
loadUser,
};
return (
<AuthContext.Provider value={contextValue}>
{children}
</AuthContext.Provider>
);
}
export function useAuth() {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within an AuthProvider');
}
return context;
}
// 计算用户权限 - 基于数据库角色权限
const getUserPermissions = (roles: Role[]): SystemPermission[] => {
const permissions = new Set<SystemPermission>();
roles.forEach((role) => {
if (!role.permissions || !Array.isArray(role.permissions)) {
console.warn('角色权限数据格式错误:', role);
return;
}
role.permissions.forEach(permission => {
if (PermissionUtils.isValidPermission(permission)) {
permissions.add(permission as SystemPermission);
} else {
console.warn('无效的权限代码:', permission);
}
});
});
return Array.from(permissions);
};
// 权限检查工具函数 - 更新为使用新的权限系统
export const checkPermission = (userPermissions: SystemPermission[], requiredPermissions?: SystemPermission[]): boolean => {
if (!requiredPermissions || requiredPermissions.length === 0) return true;
return PermissionUtils.hasAnyPermission(userPermissions, requiredPermissions);
};
// 检查角色权限的工具函数
export const checkRole = (userRoles: string[], requiredRoles?: string[]): boolean => {
if (!requiredRoles || requiredRoles.length === 0) return true;
return requiredRoles.some(role => userRoles.includes(role));
};
// 过滤菜单项的工具函数 - 支持角色和权限检查
export const filterMenuItems = <T extends { roles?: string[]; permissions?: SystemPermission[] }>(
items: T[],
user: User | null
): T[] => {
2025-07-28 10:32:25 +08:00
return items
2025-07-28 07:50:50 +08:00
if (!user) return [];
return items.filter(item => {
// 检查角色要求
if (item.roles && !checkRole(user.roles, item.roles)) {
return false;
}
// 检查权限要求
if (item.permissions && user.permissions && !checkPermission(user.permissions, item.permissions)) {
return false;
}
return true;
});
};