浏览器免重登:HttpOnly cookie + 服务端 session 表 + 设备名持久化
- 新增 web_sessions 表(sqlc):refresh_token 以 AES-256-GCM 落盘,密钥取自 CDROP_SESSION_SECRET(env、不在库内);cookie 仅存不透明随机串,表 id 存其 SHA-256,故 DB 单独泄露既换不出可用 cookie、也解不出 token - /auth/exchange 建 session 并下发 HttpOnly; Secure; SameSite=Lax; Path=/api/auth cookie,响应体不再回传 refresh_token - /auth/refresh 改为 cookie 驱动(7 天滑动失活),同时即开机静默免重登; 新增 /auth/logout(删 session + 清 cookie)、/auth/device(写设备名入 session) - device_name 随 session 存,开机 refresh 带回前端,抗 iOS PWA 存储清除; setup / 改名时 syncWebDeviceName 推送服务端 - striped per-session 锁串行化同会话并发 refresh,防一次性 refresh_token 被 花两次而把用户从所有 tab 登出 - 前端 store 移除 refreshToken 字段(长寿命凭据彻底不进 JS);main.tsx 首屏前 bootstrapAuth 用 cookie 静默续期,命中直接进已登录态、避免登录页闪现 - config:prod 强制 CDROP_SESSION_SECRET,缺失拒启动 - 桌面端不受影响(自有 loopback flow + Go keyring,不碰这两个端点)
This commit is contained in:
+22
-9
@@ -13,6 +13,7 @@ import { RouterProvider, createRouter } from "@tanstack/react-router";
|
||||
import { ToastViewport } from "./ui/feedback";
|
||||
import { routeTree } from "./routeTree.gen";
|
||||
import { useAppStore, type ThemeMode } from "./store";
|
||||
import { bootstrapAuth } from "./features/auth/auth";
|
||||
import { startCjkAutospace } from "./utils/cjkAutospace";
|
||||
import { initDesktopMenuBridge, isDesktop, loadDesktopSettings } from "./net/desktop";
|
||||
|
||||
@@ -115,16 +116,28 @@ function App()
|
||||
// 时从注入的 window.__CDROP_BOOT__ 同步水合(store/helpers.ts),无需异步等待。
|
||||
applyTheme(useAppStore.getState().theme);
|
||||
|
||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
);
|
||||
function mount(): void
|
||||
{
|
||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
);
|
||||
// CJK 自动间距 shim:在 React 完成首次挂载后启动。MutationObserver 后续
|
||||
// 增量处理 React 重渲染产生的新文本节点。放在 createRoot 之后,首帧绘制
|
||||
// 完会触发一次 runShim() 处理已挂载的子树。
|
||||
requestAnimationFrame(() => { startCjkAutospace(); });
|
||||
}
|
||||
|
||||
// CJK 自动间距 shim:在 React 完成首次挂载后启动。MutationObserver 后续
|
||||
// 增量处理 React 重渲染产生的新文本节点。放在 createRoot 之后,首帧绘制
|
||||
// 完会触发一次 runShim() 处理已挂载的子树。
|
||||
requestAnimationFrame(() => { startCjkAutospace(); });
|
||||
// 浏览器端「免重登」:首屏渲染前用 HttpOnly cookie 尝试静默续期。命中则直接以已
|
||||
// 登录态挂载,避免登录页 / 设置页闪现;未命中(401)即照常落到登录页。桌面 / dev /
|
||||
// sessionStorage 已有令牌时 bootstrapAuth 立即返回,几乎无延迟。4s 超时兜底:万一
|
||||
// refresh 卡死,也不至于把整个应用永久挡在首屏外。
|
||||
const BOOT_AUTH_TIMEOUT_MS = 4000;
|
||||
void Promise.race([
|
||||
bootstrapAuth().catch(() => { /* ignore:失败即落登录页 */ }),
|
||||
new Promise<void>((resolve) => { window.setTimeout(resolve, BOOT_AUTH_TIMEOUT_MS); }),
|
||||
]).finally(mount);
|
||||
|
||||
// PWA service worker:仅在浏览器 + prod 注册。dev 下会干扰 Vite HMR;桌面壳
|
||||
// (Wails WebView,wails:// / loopback origin)已是原生应用且 SW 行为不可靠,
|
||||
|
||||
Reference in New Issue
Block a user