TREK 登录与注册完全指南:会话 Cookie、密码策略、邀请链接、限流与 SSO 实战解析
2026/9/15 11:32:17 网站建设 项目流程

TREK 登录与注册完全指南:会话 Cookie、密码策略、邀请链接、限流与 SSO 实战解析

【免费下载链接】TREKA self-hosted travel/trip planner with real-time collaboration, interactive maps, PWA support, SSO, budgets, packing lists, and more.项目地址: https://gitcode.com/GitHub_Trending/nomad22/TREK

TREK 是一个可自托管的旅行行程规划器,其登录/注册体系覆盖从密码认证、双因素认证(MFA)、邀请链接注册、强制改密、按 IP 限流到 OIDC SSO 的完整身份链路。本文以 wiki/Login-and-Registration.md 为骨架,结合前端登录页实现与后端认证服务源码,逐层剖析会话 Cookie 的属性与有效期、密码校验规则、注册的三种触发条件、四类限流阈值以及 Demo 模式与 SSO 的交互细节,帮助你理解并正确配置 TREK 实例的身份入口,也能据此排查“登录后反复 401”“明文 HTTP 下 Cookie 丢失”等自托管常见问题。

登录流程与会话 Cookie

访问/login并提交凭据

导航到/login后,TREK 前端(client/src/pages/LoginPage.tsx)会先调用GET /api/auth/app-config获取实例级配置(是否已有用户、是否开放注册、是否配置了 OIDC、是否启用 Demo 等,见 server/src/services/authService.ts 的getAppConfig),再决定渲染登录表单、注册表单,或直接跳转 SSO。

输入邮箱与密码提交后,请求命中POST /api/auth/login(server/src/nest/auth/auth-public.controller.ts)。服务端通过 bcrypt(成本因子 12,见 server/src/services/authService.ts)比对密码哈希,成功后签发 JWT 会话令牌,并通过Set-Cookie写入trek_session

trek_sessionCookie 的关键属性

会话 Cookie 的构造集中在 server/src/services/cookie.ts,核心属性如下:

属性含义
httpOnlytrue脚本无法读取,防御 XSS 窃取会话
sameSitelax允许顶级导航携带 Cookie,同时缓解 CSRF
secure生产环境为true仅通过 HTTPS 传输(详见下文)
maxAge默认SESSION_DURATION_MS会话有效期,与 JWT 的exp声明保持一致
path/全站生效

24 小时会话与 Remember me

默认情况下,一次登录产生的会话有效期为24 小时,刷新页面、关闭浏览器重启都不会失效,直到会话过期或显式登出。这个默认值来自 server/src/config.ts 中的DEFAULT_SESSION_DURATION = '24h'

  • SESSION_DURATION环境变量:可覆盖默认会话时长,支持ms/s/m/h/d/w/y单位的 ms 风格字符串,例如SESSION_DURATION=7d。配置解析在启动时进行,非法值会告警并回退到24h
  • Remember me(记住我):登录表单上的开关对应SESSION_DURATION_REMEMBER,默认30 天(server/src/config.ts)。实现上rememberMe同时影响两处:JWT 的expiresIn与 Cookie 的maxAge(server/src/services/authService.ts 的generateToken、server/src/services/cookie.ts 的resolveMaxAge),两者永远同步、不会漂移。
  • 未勾选 Remember me 时,Cookie 不携带maxAge,属于浏览器会话级 Cookie,关闭浏览器即清除(server/src/services/cookie.ts)。

注意secure标志可用COOKIE_SECURE=false显式关闭(适合纯 HTTP 的开发环境),也可用FORCE_HTTPS=true强制开启。此外,server/src/services/cookie.ts 的实现会额外检查req.secure(Express 在trust proxy开启时由X-Forwarded-Proto推导)——也就是说,即使本地NODE_ENV=development且未设FORCE_HTTPS,只要请求经由 Traefik/Caddy/Cloudflare Tunnel 等 HTTPS 反代进入,Cookie 同样会带上Secure标志,这正是不少自托管用户忽略的细节。

明文 HTTP 下的 Cookie 丢失陷阱

若服务端即将下发带Secure标志的 Cookie,而请求本身并非 HTTPS,浏览器会静默丢弃 Cookie,导致下一个请求直接返回 “Access token required”。为此 TREK 在登录响应中附带insecureCookie: true标志(server/src/nest/auth/auth-public.controller.ts),前端检测到后会在登录页展示黄色提示条,指导你改用 HTTPS 或设置COOKIE_SECURE=false(见 client/src/pages/LoginPage.tsx),而不是让你面对一个莫名其妙的 401。

双因素认证(MFA)登录

如果账号已启用 2FA,密码校验通过后不会立即下发会话 Cookie,而是返回{ mfa_required: true, mfa_token }(server/src/services/authService.ts)。这个mfa_token是有效期仅5 分钟的短期 JWT(purpose: 'mfa_login'),专门用于承载后续的 MFA 验证步骤,与会话令牌相互隔离。

前端收到mfa_required后切换到 TOTP 输入界面(client/src/pages/login/useLogin.ts),支持:

  • TOTP 验证码:来自你绑定的 Authenticator 应用;
  • 备用恢复码(backup code):8 位字符,输入时自动转大写并去除非字母数字字符(server/src/services/authService.ts 的normalizeBackupCode)。

验证请求走POST /api/auth/mfa/verify-login(server/src/nest/auth/auth-public.controller.ts),成功后才会真正写入trek_session会话 Cookie,并记录user.login审计事件。MFA 验证单独限流为每 IP 每 15 分钟 5 次

忘记密码与强制改密

忘记密码自助流程

登录表单下方的“Forgot password?”链接启动自助重置流程,完整细节见 wiki/Password-Reset.md。服务端实现上,POST /api/auth/forgot-password(server/src/nest/auth/auth-public.controller.ts)无论邮箱是否存在都返回统一的{ ok: true }响应,并强制等待至少 350ms(FORGOT_MIN_LATENCY_MS),以消除账号枚举的时间侧信道;POST /api/auth/reset-password则负责校验重置令牌并写入新密码。

强制密码更改(Forced password change)

当管理员将账号标记为“必须修改密码”时(数据库中must_change_password字段为 1),成功登录(或 MFA 步骤完成)后,TREK 会直接展示Set new password表单,且不会签发会话 Cookie,直到新密码保存成功:

  1. 登录接口返回的用户对象带有must_change_password: true(server/src/services/authService.ts);
  2. 前端捕获该标志,进入passwordChangeStep状态(client/src/pages/login/useLogin.ts);
  3. 提交新密码走POST /api/auth/change-password,成功后才会loadUser并跳转(client/src/pages/login/useLogin.ts)。

服务端在执行改密时同样会走validatePassword密码强度校验,并更新password_version、清除must_change_password(server/src/services/authService.ts)。password_version的存在意味着改密后旧令牌即刻失效——这正是会话安全模型的关键一环。

注册流程

注册表单出现的三种条件

TREK 的注册表单只在以下任一条件成立时展示:

  1. 开放注册:管理员为实例启用了密码注册(password_registration设置项)。该开关在 server/src/services/authService.ts 的resolveAuthToggles中解析,可由数据库app_settings表或环境变量控制,且在 Demo 模式下被强制关闭(server/src/services/authService.ts)。
  2. 有效的邀请链接:访问/login?invite=TOKEN且令牌有效(详见下文“邀请链接流程”)。
  3. 首个用户:实例中尚不存在任何账号,注册表单自动展示。

前端逻辑与之一一对应:useLogin启动时解析invite查询参数、拉取app-config,若has_users === false则直接切换到注册模式(client/src/pages/login/useLogin.ts 与 client/src/pages/login/useLogin.ts)。

注册字段与校验

注册表单包含三个字段:username(用户名)、email(邮箱)、password(密码)。服务端registerUser(server/src/services/authService.ts)会依次校验:

  • 三字段非空;
  • 密码满足强度规则(见下节);
  • 邮箱格式合法(EMAIL_REGEX);
  • 用户名/邮箱未与已有账号冲突(返回 409,且客座账号is_guest不会阻塞真实注册)。

密码使用 bcrypt 成本因子 12 哈希后落库,随后签发会话令牌并写入trek_sessionCookie。

密码强度要求

密码必须同时满足以下全部规则,实现在 server/src/services/passwordPolicy.ts 的validatePassword

  • 最短8 个字符
  • 至少包含一个大写字母
  • 至少包含一个小写字母
  • 至少包含一个数字
  • 至少包含一个特殊字符(非字母数字);
  • 不得是常见弱密码(内置 30 余条黑名单,如password12345678qwerty123admin123letmein12welcome1passw0rdchangeme等,server/src/services/passwordPolicy.ts);
  • 不得由单一重复字符构成(正则^(.)\1+$拦截,如aaaaaaaa)。

管理员提示:可以关闭开放注册,仅保留邀请链接方式,见 wiki/Admin-Users-and-Invites.md。

邀请链接流程

管理员分享形如/login?invite=TOKEN的邀请链接后,访问它会依次发生(前端见 client/src/pages/login/useLogin.ts):

  1. GET /api/auth/invite/:token校验令牌(该端点本身也受登录桶限流保护,server/src/nest/auth/auth-public.controller.ts);
  2. 校验通过后,登录页自动切换到注册模式
  3. 注册请求携带该 token,使本次注册计入邀请链接的使用次数上限invite_tokens.used_count递增,server/src/services/authService.ts)。

若令牌无效、过期或用尽,页面会展示错误提示(login.invalidInviteLink)。另外,若邀请链接绑定到具体行程(trip-bound invite),新注册用户会被自动加入该行程(server/src/services/authService.ts)。

首个用户自动成为管理员

在没有任何账号的全新 TREK 实例上,注册表单会直接打开。服务端通过统计非客座用户数量判断isFirstUser,首个账号的role被强制设为admin(server/src/services/authService.ts),后续注册的账号默认均为普通user角色。

按 IP 限流机制

四类限流阈值

TREK 对认证接口实施内存级的按 IP 限流,同一 IP 在 15 分钟窗口内的允许次数如下:

操作阈值(每 IP / 15 分钟)对应桶
登录失败/登录尝试(含注册、邀请校验)10 次login
MFA 验证尝试5 次mfa
忘记密码请求3 次forgot
重置密码提交5 次reset

超过阈值后,后续请求返回HTTP 429,直到窗口重置。限流桶定义见 server/src/nest/auth/auth-public.controller.ts 的WINDOW = 15 * 60 * 1000以及各端点调用处。

底层实现

限流核心是 server/src/nest/auth/rate-limit.service.ts 的RateLimitService:一个内存中的双层 Map(buckets → key(IP) → { count, first }),check方法在count >= max且距首次尝试未超过窗口时拒绝请求(server/src/nest/auth/rate-limit.service.ts)。实现要点:

  • 按命名桶隔离:登录、MFA、忘记密码、重置密码互不干扰;
  • 滑动窗口语义:窗口内第 N+1 次尝试立即被拒,窗口过期后自动视为全新尝试;
  • 纯内存实现:服务重启后计数清零(同一实例的 API 网关已通过插件宿主实现自身的限流,见 server/src/nest/plugins/host/rate-limit.ts)。

此外,登录接口还引入了最小响应延迟 350msLOGIN_MIN_LATENCY_MS,server/src/nest/auth/auth-public.controller.ts):即使密码错误也会补足耗时,配合“未知邮箱同样执行 bcrypt 假哈希”的策略(server/src/services/authService.ts),从时间维度进一步防御账号枚举与暴力破解。

Demo 模式

服务器以DEMO_MODE=true启动时(生产环境开启会打印安全警告,见 server/src/index.ts),登录表单下方会出现“Try demo”一键登录按钮。点击后调用POST /api/auth/demo-login(server/src/nest/auth/auth-public.controller.ts),免密直接以演示用户身份登录,前端随后播放一段“飞机起飞”过渡动画并进入仪表盘(client/src/pages/login/useLogin.ts)。

演示凭据demo@trek.app/demo12345会随/api/auth/app-config返回(demo_email/demo_password字段,server/src/services/authService.ts),但一键按钮才是官方推荐的进入方式。需要注意:Demo 模式下注册被强制关闭(password_registration: false),且演示账号在许多业务接口上受到只读/受限处理(server/src/middleware/auth.ts)。

SSO(OpenID Connect)登录

常规 OIDC 登录

管理员配置好 OpenID Connect 后,登录表单下方会出现“Sign in with SSO”按钮,按钮文案取自OIDC_DISPLAY_NAME环境变量(默认回退到SSO,server/src/services/oidcService.ts)。点击后前端跳转/api/auth/oidc/login,服务端将浏览器重定向到身份提供方;回调带回oidc_code后由前端完成 code 交换并恢复会话(client/src/pages/login/useLogin.ts)。

OIDC-only 模式

当实例处于OIDC-only 模式(密码登录被禁用)时:

  • 访问/login自动重定向到身份提供方,不再展示邮箱/密码表单;
  • 唯一的例外是你刚刚显式登出:此时自动跳转被抑制,改为展示 SSO 按钮,让你主动选择重新登录(client/src/pages/login/useLogin.ts 与 client/src/pages/LoginPage.tsx)。

OIDC-only 可由环境变量OIDC_ONLY=true或数据库设置oidc_only触发,app-config中的env_override_oidc_only字段会标明其来源(server/src/services/authService.ts)。完整配置与登录流说明见 wiki/OIDC-SSO.md。

常见问题速查

现象原因与解法
明文 HTTP 下登录后下一请求 401SecureCookie 被浏览器丢弃。改用 HTTPS,或开发环境设COOKIE_SECURE=false
登录被 429 拒绝同一 IP 15 分钟内登录尝试超过 10 次,等待窗口重置
会话提前失效检查SESSION_DURATION/SESSION_DURATION_REMEMBER是否被修改,以及管理员是否执行了 JWT 轮换(见 server/src/config.ts 的updateJwtSecret
注册表单不显示实例关闭了开放注册且无有效邀请链接;若为全新实例,请确认has_users为 false

相关文档:wiki/Password-Reset.md · wiki/OIDC-SSO.md · wiki/Admin-Users-and-Invites.md · wiki/Two-Factor-Authentication.md

【免费下载链接】TREKA self-hosted travel/trip planner with real-time collaboration, interactive maps, PWA support, SSO, budgets, packing lists, and more.项目地址: https://gitcode.com/GitHub_Trending/nomad22/TREK

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询