refactor: 完成前后端认证体系重构与安全增强
本提交重构了全栈的认证与安全体系,包含以下核心变更: 1. **后端安全增强**: - 使用常量时间比较修复CSRF校验时序漏洞 - 重构refresh token存储为哈希+密文,支持轮转宽限期与盗用检测 - 新增cookie前缀自动配置,支持__Host-前缀强化生产环境安全 - 优化登出逻辑为单设备登出,合并用户信息与未读通知接口 - 增加数据库迁移脚本,兼容旧版refresh token数据升级 - 新增定时清理过期refresh token任务 2. **前端安全与体验优化**: - 新增cookie工具库,统一管理认证cookie名与转发逻辑 - 修复开放重定向漏洞,实现安全跳转校验 - 重构SSR请求逻辑,仅转发必要的access token cookie - 优化middleware轮转逻辑,修复并发请求去重与边缘部署兼容性 - 修复登录态静默校正逻辑,兼容旧版cookie路径与名称 - 新增环境变量示例配置文件 3. **工程化改进**: 修复Next.js类型文件导入路径,统一前后端配置与命名规范
This commit is contained in:
@@ -5,7 +5,8 @@ import "./globals.css";
|
||||
import Header from "@/components/Header";
|
||||
import Footer from "@/components/Footer";
|
||||
import Toaster from "@/components/Toaster";
|
||||
import { fetchMe, fetchUnreadCount, type User } from "@/lib/api";
|
||||
import { fetchMe, type User } from "@/lib/api";
|
||||
import { TOKEN_COOKIE, REFRESH_COOKIE, authCookieHeader } from "@/lib/cookies";
|
||||
|
||||
// 构建期下载并自托管字体:同源加载 + 自动 preload + fallback 度量校正,
|
||||
// 消除 F5 时 Google CDN 字体到达后整页"由粗变细"的 FOUT 闪动
|
||||
@@ -52,21 +53,19 @@ export default async function RootLayout({
|
||||
const theme = cookieStore.get("j13-theme")?.value === "dark" ? "dark" : "light";
|
||||
|
||||
// SSR 直出登录态(access 过期时 middleware 已先行轮转,见 middleware.ts),
|
||||
// 保证 F5 初始 HTML 中的右上角就是正确的用户区,而非先闪"登录/注册"
|
||||
const cookieHeader = cookieStore.toString();
|
||||
// 保证 F5 初始 HTML 中的右上角就是正确的用户区,而非先闪"登录/注册"。
|
||||
// 旧版 refresh cookie 可能是 Path=/api/auth(页面请求不带),因此只要
|
||||
// token 或 refresh 任一存在就向后端确认一次,避免半登录态(列表点赞态与
|
||||
// Header 游客态不一致),并兼容 access 15 分钟内的自然窗口。
|
||||
let user: User | null = null;
|
||||
let unread = 0;
|
||||
if (cookieStore.get("j13_refresh")?.value) {
|
||||
try {
|
||||
const [me, count] = await Promise.all([
|
||||
fetchMe(cookieHeader),
|
||||
fetchUnreadCount(cookieHeader),
|
||||
]);
|
||||
user = me.user;
|
||||
unread = count;
|
||||
} catch {
|
||||
// 后端不可用时降级为游客视图,不阻塞页面渲染
|
||||
}
|
||||
const hasAuthCookie =
|
||||
!!cookieStore.get(TOKEN_COOKIE)?.value || !!cookieStore.get(REFRESH_COOKIE)?.value;
|
||||
if (hasAuthCookie) {
|
||||
// 用户信息与未读数合并为一次请求;只转发最小化的 access cookie
|
||||
const me = await fetchMe(authCookieHeader(cookieStore) || undefined);
|
||||
user = me.user;
|
||||
unread = me.unread_count;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user