feat: 1. 完善全局路由的访问权限的验证。 2. 完善接口返回的树形路由结构 3.优化评查点列表的查询,改用表连接的方式,废弃使用数据库的rpc函数,同时进行地区隔离和权限隔离。
4. 删除冗余的评查文件列表。 5.完善上传文档 页面初始化查询数据的时候 查询文件类型(改成动态指定) 6. 添加获取入口模块的查询接口。 7.完善服务端中判断token的有效性,失效则跳转到登录页。 8. 重构layout和sidebar的页面,改成由动态权限路由来渲染对应的菜单栏。 9.重构入口页面,通过动态查询根据不同地区的人返回不同的入口。
This commit is contained in:
@@ -29,7 +29,7 @@ import { OAUTH_CONFIG, API_BASE_URL } from "~/config/api-config";
|
||||
* @property {'common'} common - 普通用户,有基本的系统访问权限
|
||||
* @property {'developer'} developer - 开发者/管理员,有完整的系统管理权限
|
||||
*/
|
||||
export type UserRole = 'common' | 'admin' | 'deptLeader' | 'groupLeader' | string;
|
||||
export type UserRole = string;
|
||||
|
||||
/**
|
||||
* 用户信息接口,对应 sso_users 表结构
|
||||
@@ -144,22 +144,22 @@ export async function getSession(request: Request) {
|
||||
* @param expiresIn OAuth token过期时间(秒)
|
||||
* @returns JWT字符串
|
||||
*/
|
||||
async function generateFrontendJWT(userInfo: UserInfo, savedUserData: SsoUser, userRole: UserRole, expiresIn: number): Promise<string> {
|
||||
const jwtUserInfo: UserInfoForJWT = {
|
||||
sub: userInfo.sub,
|
||||
user_id: savedUserData.id!,
|
||||
username: savedUserData.username,
|
||||
nick_name: savedUserData.nick_name,
|
||||
email: savedUserData.email,
|
||||
phone_number: savedUserData.phone_number,
|
||||
ou_id: savedUserData.ou_id,
|
||||
ou_name: savedUserData.ou_name,
|
||||
is_leader: savedUserData.is_leader,
|
||||
user_role: userRole
|
||||
};
|
||||
// async function generateFrontendJWT(userInfo: UserInfo, savedUserData: SsoUser, userRole: UserRole, expiresIn: number): Promise<string> {
|
||||
// const jwtUserInfo: UserInfoForJWT = {
|
||||
// sub: userInfo.sub,
|
||||
// user_id: savedUserData.id!,
|
||||
// username: savedUserData.username,
|
||||
// nick_name: savedUserData.nick_name,
|
||||
// email: savedUserData.email,
|
||||
// phone_number: savedUserData.phone_number,
|
||||
// ou_id: savedUserData.ou_id,
|
||||
// ou_name: savedUserData.ou_name,
|
||||
// is_leader: savedUserData.is_leader,
|
||||
// user_role: userRole
|
||||
// };
|
||||
|
||||
return JWTUtils.generateJWT(jwtUserInfo, expiresIn);
|
||||
}
|
||||
// return JWTUtils.generateJWT(jwtUserInfo, expiresIn);
|
||||
// }
|
||||
|
||||
/**
|
||||
* 创建包含JWT的用户信息对象
|
||||
@@ -169,38 +169,44 @@ async function generateFrontendJWT(userInfo: UserInfo, savedUserData: SsoUser, u
|
||||
* @param frontendJWT 前端JWT
|
||||
* @returns 完整的用户信息对象
|
||||
*/
|
||||
function createUserInfoWithJWT(userInfo: UserInfo, savedUserData: SsoUser, userRole: UserRole, frontendJWT: string) {
|
||||
return {
|
||||
// 保持与callback.tsx中enhancedUserInfo相同的数据结构
|
||||
sub: userInfo.sub,
|
||||
username: savedUserData.username,
|
||||
nick_name: savedUserData.nick_name,
|
||||
phone_number: savedUserData.phone_number,
|
||||
email: savedUserData.email,
|
||||
ou_id: savedUserData.ou_id,
|
||||
ou_name: savedUserData.ou_name,
|
||||
status: savedUserData.status,
|
||||
is_leader: savedUserData.is_leader,
|
||||
// 增强字段,与OAuth登录保持一致
|
||||
user_id: savedUserData.id,
|
||||
user_role: userRole,
|
||||
frontend_jwt: frontendJWT
|
||||
};
|
||||
}
|
||||
// function createUserInfoWithJWT(userInfo: UserInfo, savedUserData: SsoUser, userRole: UserRole, frontendJWT: string) {
|
||||
// return {
|
||||
// // 保持与callback.tsx中enhancedUserInfo相同的数据结构
|
||||
// sub: userInfo.sub,
|
||||
// username: savedUserData.username,
|
||||
// nick_name: savedUserData.nick_name,
|
||||
// phone_number: savedUserData.phone_number,
|
||||
// email: savedUserData.email,
|
||||
// ou_id: savedUserData.ou_id,
|
||||
// ou_name: savedUserData.ou_name,
|
||||
// status: savedUserData.status,
|
||||
// is_leader: savedUserData.is_leader,
|
||||
// // 增强字段,与OAuth登录保持一致
|
||||
// user_id: savedUserData.id,
|
||||
// user_role: userRole,
|
||||
// frontend_jwt: frontendJWT
|
||||
// };
|
||||
// }
|
||||
|
||||
export async function getUserSession(request: Request) {
|
||||
const session = await getSession(request);
|
||||
const isAuthenticated = session.get("isAuthenticated") === true;
|
||||
const userRole = session.get("userRole") as UserRole;
|
||||
let accessToken = session.get("accessToken");
|
||||
const accessToken = session.get("accessToken");
|
||||
const refreshToken = session.get("refreshToken");
|
||||
let tokenIssuedAt = session.get("tokenIssuedAt");
|
||||
const tokenIssuedAt = session.get("tokenIssuedAt");
|
||||
let tokenExpiresIn = session.get("tokenExpiresIn");
|
||||
const userInfo = session.get("userInfo");
|
||||
let frontendJWT = session.get("frontendJWT");
|
||||
const frontendJWT = session.get("frontendJWT");
|
||||
|
||||
// 🔑 检查是否是公共路径(不需要认证的路径)
|
||||
const url = new URL(request.url);
|
||||
const pathname = url.pathname;
|
||||
const publicPaths = ['/login', '/favicon.ico', '/callback'];
|
||||
const isPublicPath = publicPaths.some(path => pathname.startsWith(path));
|
||||
|
||||
let refreshedSession = null;
|
||||
let shouldRegenerateJWT = false;
|
||||
// let shouldRegenerateJWT = false;
|
||||
|
||||
// 🔑 新的统一过期检查逻辑
|
||||
// 不区分 admin 和 OAuth 用户,所有用户都使用同样的过期检查
|
||||
@@ -251,20 +257,44 @@ export async function getUserSession(request: Request) {
|
||||
}
|
||||
}
|
||||
|
||||
// 🚨 如果 session 无效(包括 token 过期),自动重定向到登录页
|
||||
if (!finalIsAuthenticated && isAuthenticated) {
|
||||
console.error("❌ [getUserSession] Session 已失效,清除 session 并重定向到登录页");
|
||||
// 🚨 统一的认证检查和重定向逻辑
|
||||
if (!finalIsAuthenticated) {
|
||||
// 如果是公共路径,不重定向,直接返回未认证状态
|
||||
if (isPublicPath) {
|
||||
// console.log("ℹ️ [getUserSession] 公共路径,允许未认证访问:", pathname);
|
||||
return {
|
||||
isAuthenticated: false,
|
||||
userRole,
|
||||
accessToken,
|
||||
refreshToken,
|
||||
userInfo,
|
||||
refreshedSession,
|
||||
frontendJWT
|
||||
};
|
||||
}
|
||||
|
||||
// 销毁服务端 session
|
||||
const { redirect } = await import("@remix-run/node");
|
||||
const destroyedSession = await sessionStorage.destroySession(session);
|
||||
// 非公共路径且未认证,重定向到登录页
|
||||
if (isAuthenticated) {
|
||||
// Session 存在但已失效(token 过期或数据不完整)
|
||||
console.error("❌ [getUserSession] Session 已失效,清除 session 并重定向到登录页");
|
||||
const { redirect } = await import("@remix-run/node");
|
||||
const destroyedSession = await sessionStorage.destroySession(session);
|
||||
|
||||
// 重定向到登录页,添加 expired=true 参数标识是因为过期重定向
|
||||
throw redirect("/login?expired=true", {
|
||||
headers: {
|
||||
"Set-Cookie": destroyedSession
|
||||
}
|
||||
});
|
||||
// 重定向到登录页,添加 expired=true 参数
|
||||
throw redirect("/login?expired=true", {
|
||||
headers: {
|
||||
"Set-Cookie": destroyedSession
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Session 不存在(首次访问或已登出)
|
||||
console.warn("⚠️ [getUserSession] 未登录,重定向到登录页");
|
||||
const { redirect } = await import("@remix-run/node");
|
||||
|
||||
// 保存当前路径,登录后可以跳转回来
|
||||
const redirectTo = pathname !== '/login' ? pathname : '/';
|
||||
throw redirect(`/login?redirect=${encodeURIComponent(redirectTo)}`);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user