优化登录逻辑的实现,将认证请求和token验证的处理分成两个逻辑文件。新增交叉评查任务列表的页面(尚未对接真实数据)。

This commit is contained in:
2025-07-16 14:32:20 +08:00
parent d876d66dcb
commit 328f326db3
13 changed files with 1729 additions and 131 deletions
+20 -88
View File
@@ -14,7 +14,6 @@ import {
import {
LoaderFunctionArgs,
redirect,
createCookieSessionStorage,
ActionFunctionArgs
} from "@remix-run/node";
import { Layout } from "~/components/layout/Layout";
@@ -30,8 +29,14 @@ import LoadingBarContainer from "~/components/ui/LoadingBar";
import RouteChangeLoader from "~/components/ui/RouteChangeLoader";
// import { useState, useEffect } from "react";
// 定义用户角色类型
export type UserRole = 'common' | 'developer';
// 导入认证相关的服务器端功能(仅在服务器端使用)
import {
getUserSession,
getSession,
sessionStorage,
logout,
type UserRole
} from "~/api/login/auth.server";
// 定义需要高级权限的路径
export const developerOnlyPaths = [
@@ -41,91 +46,10 @@ export const developerOnlyPaths = [
'/prompts',
];
// 创建基于Cookie的会话存储
// 在实际应用中,应该使用环境变量来设置密钥
export const sessionStorage = createCookieSessionStorage({
cookie: {
name: "__lgsession",
httpOnly: true,
path: "/",
sameSite: "lax",
secrets: ["s3cr3t"], // 应该从环境变量读取
// secure: process.env.NODE_ENV === "production",
maxAge: 60 * 60 * 24 * 1, // 1天
secure: false, // 开发环境中禁用secure
},
});
// 导出类型供客户端使用
export type { UserRole };
// 获取会话对象
export async function getSession(request: Request) {
const cookie = request.headers.get("Cookie");
return sessionStorage.getSession(cookie);
}
// 获取用户登录状态
export async function getUserSession(request: Request) {
const session = await getSession(request);
const isAuthenticated = session.get("isAuthenticated") === true;
const userRole = session.get("userRole") || 'common' as UserRole;
const accessToken = session.get("accessToken");
const refreshToken = session.get("refreshToken");
const tokenIssuedAt = session.get("tokenIssuedAt");
const tokenExpiresIn = session.get("tokenExpiresIn");
const userInfo = session.get("userInfo");
// 检查token是否过期
let isTokenExpired = false;
if (accessToken && tokenIssuedAt && tokenExpiresIn) {
const now = Date.now();
const expiresAt = tokenIssuedAt + (tokenExpiresIn * 1000);
isTokenExpired = now >= expiresAt;
}
// console.log("获取会话状态:",
// // "Cookie:", request.headers.get("Cookie"),
// "是否认证:", isAuthenticated,
// "用户角色:", userRole,
// "Token过期:", isTokenExpired
// );
return {
isAuthenticated: isAuthenticated && !isTokenExpired,
userRole,
accessToken,
refreshToken,
userInfo,
isTokenExpired
};
}
// 创建登录会话
export async function createUserSession(isAuthenticated: boolean, userRole: UserRole, redirectTo: string) {
const session = await sessionStorage.getSession();
session.set("isAuthenticated", isAuthenticated);
session.set("userRole", userRole);
const cookie = await sessionStorage.commitSession(session);
console.log("创建会话 - 设置Cookie:", !!cookie);
console.log("创建会话 - 用户角色:", userRole);
console.log("创建会话 - 重定向到:", redirectTo);
return redirect(redirectTo, {
headers: {
"Set-Cookie": cookie,
},
});
}
// 销毁会话(登出)
export async function logout(request: Request) {
const session = await getSession(request);
return redirect("/login", {
headers: {
"Set-Cookie": await sessionStorage.destroySession(session),
},
});
}
// 添加action处理登录/登出请求
export async function action({ request }: ActionFunctionArgs) {
@@ -149,8 +73,8 @@ export async function loader({ request }: LoaderFunctionArgs) {
const publicPaths = ['/login', '/favicon.ico'];
const isPublicPath = publicPaths.some(path => pathname.startsWith(path));
// 获取用户会话
const { isAuthenticated, userRole } = await getUserSession(request);
// 获取用户会话(可能包含刷新后的token
const { isAuthenticated, userRole, refreshedSession } = await getUserSession(request);
// console.log("是否公开路径:", isPublicPath, "是否已认证:", isAuthenticated);
// 如果访问需要认证的路径但未登录,重定向到登录页
@@ -184,6 +108,12 @@ export async function loader({ request }: LoaderFunctionArgs) {
return redirect("/");
}
// 如果token被刷新了,需要在响应中设置更新后的cookie
const responseHeaders: Record<string, string> = {};
if (refreshedSession) {
responseHeaders["Set-Cookie"] = await sessionStorage.commitSession(refreshedSession);
}
// 向组件传递认证状态、当前路径和环境变量
return Response.json({
isAuthenticated,
@@ -194,6 +124,8 @@ export async function loader({ request }: LoaderFunctionArgs) {
NEXT_PUBLIC_APP_ID: process.env.NEXT_PUBLIC_APP_ID,
NEXT_PUBLIC_APP_KEY: process.env.NEXT_PUBLIC_APP_KEY,
},
}, {
headers: responseHeaders
});
}