Skip to content

认证与登录 ​

概述 ​

认证与登录是系统管理(module_system)的入口模块,负责用户身份的认证、会话令牌的签发与刷新、第三方账号的接入,以及登录后权限信息的聚合下发。它向下依赖基础设施层的 JWT、Redis 会话、在线用户等能力,向上为前端登录页、单点授权页、社交绑定等场景提供统一接口。

image-20260930111451900

模块覆盖的能力:

  • 账号密码登录、注册、退出与令牌刷新
  • 图形验证码与短信验证码
  • 第三方社交登录(企业微信、钉钉、飞书)
  • OAuth2 授权框架(客户端、令牌、授权页、用户信息)
  • 登录日志与在线会话的打通(详见“日志与监控”“监控与连接”章节)

涉及的核心接口前缀:

text
/system/auth           账号密码登录、注册、刷新、退出、权限信息、社交登录、短信
/system/captcha        图形验证码
/system/oauth2          OAuth2 授权(令牌下发、校验、吊销、授权页)
/system/oauth2-client  OAuth2 客户端管理
/system/oauth2-token   OAuth2 令牌管理
/system/oauth2/user    OAuth2 当前用户信息
/system/social-client  社交客户端(第三方应用凭据)管理
/system/social-user    社交用户与绑定关系管理

认证模型与令牌体系 ​

登录成功后,后端并不使用「用户 ID」作为令牌主体,而是生成一个随机的 session_id,把它同时作为 JWT 的 sub(主体)和 Redis 中会话信息的键。这样令牌本身不直接携带用户敏感信息,吊销/踢人只需删除 Redis 中的会话即可。

JWT 载荷(JWTPayloadSchema)仅包含三个字段:

  • sub:会话编号 session_id
  • is_refresh:是否为刷新令牌,用于区分两种令牌的用途
  • exp:过期时间

令牌有效期由配置项控制(默认均为 12 小时):

  • ACCESS_TOKEN_EXPIRE_SECONDS
  • REFRESH_TOKEN_EXPIRE_SECONDS
  • TOKEN_TYPE:令牌类型,默认 Bearer,前端在请求头中以 Authorization: Bearer <token> 携带

Redis 中各类认证相关键:

键内容过期
USER_SESSION:{session_id}在线会话 JSON(用户、IP、终端、登录时间等)刷新令牌有效期
ACCESS_TOKEN:{session_id}访问令牌访问令牌有效期
REFRESH_TOKEN:{session_id}刷新令牌刷新令牌有效期
CAPTCHA_CODES:{key}图形验证码答案CAPTCHA_EXPIRE_SECONDS
sms_code:{mobile}:{scene}短信验证码300 秒
oauth_state:{state}provider|frontend_redirect600 秒

账号密码登录 ​

账号密码登录是主登录链路,整体流程如下:

要点说明:

  • 当 CAPTCHA_ENABLE=true 且请求来源不是接口文档页时,必须携带图形验证码;captchaVerification 的格式为 key___code(___ 三段式分隔)。
  • 登录参数中的 loginType 支持 PC端 / 移动端,用于在在线用户与日志中区分终端类型。
  • 任何失败(验证码错误、账号密码错误、账号停用)都会写入登录日志,便于安全审计。

接口参考:

  • POST /system/auth/login:账号密码登录,返回令牌与过期时间
  • POST /system/auth/login-form:兼容 Swagger UI 的 OAuth2 密码模式表单登录(不对外公开)

图形验证码 ​

验证码用于拦截机器批量撞库。流程为前端先拉取一张验证码图片,提交登录时带回其标识与用户识别值。

  • POST /system/captcha/get:返回一个 Base64 图片 imgBase、验证码标识 key,以及当前是否启用的 enable
  • POST /system/captcha/check:主动校验(可选,登录时后端自动校验)

验证码答案仅存于 Redis,校验成功后立即删除,且一次性使用。开关由 CAPTCHA_ENABLE 控制,有效期为 CAPTCHA_EXPIRE_SECONDS。

注册 ​

POST /system/auth/register 提供用户自助注册能力,与登录复用同一套验证码与密码校验逻辑:

  • 若开启验证码,注册前需校验 captchaVerification
  • 密码需满足长度与强度要求,且两次输入一致
  • 注册成功后会自动签发令牌并写入注册日志,无需再次登录

令牌刷新与退出登录 ​

刷新令牌 ​

POST /system/auth/refresh-token?refreshToken=xxx 用于在无感知情况下续期访问令牌。后端会:

  1. 校验传入的是否为刷新令牌(is_refresh=true)
  2. 校验 Redis 中的会话是否仍然存在(已过期则需重新登录)
  3. 重新签发 access_token 与 refresh_token,并延长会话 TTL

刷新操作同样写入登录日志,便于追踪异常换票行为。

退出登录 ​

POST /system/auth/logout 需在请求头携带 Authorization: Bearer <token>。后端解析出 session_id 后,删除 Redis 中对应的会话、访问令牌、刷新令牌三处键,并写入退出日志。删除会话的同时也就让该用户的在线记录失效。

登录后权限信息下发 ​

登录成功后,前端需要获取当前用户的菜单与按钮权限,以渲染侧边栏与按钮级鉴权。这是通过一次聚合接口完成的:

GET /system/auth/get-permission-info

返回结构(聚合自 user / roles / menus / permissions):

实现要点:

  • menus 是从用户所属角色递归构建的菜单树,已过滤停用状态与按钮类型(type=3)节点
  • permissions 单独从角色的全部菜单中收集按钮类型节点的权限标识(如 system:user:query),用于前端 useAccess / v-access 按钮鉴权(详见“权限集成”章节)
  • 由于认证依赖加载的用户对象与会话绑定,接口内会使用请求级活跃会话重新加载角色与菜单,避免跨会话懒加载失败

短信验证码登录与重置密码 ​

针对手机号用户,系统提供短信验证码登录与找回密码能力,场景由 scene 区分:

scene场景短信模板
21登录admin-sms-login
22注册admin-sms-register
23重置密码admin-reset-password

流程:

  1. POST /system/auth/send-sms-code:传入 mobile 与 scene,后端生成 6 位验证码写入 Redis(300 秒有效)并通过短信渠道下发
  2. POST /system/auth/sms-login:传入 mobile 与 code,校验通过后按手机号查找用户并签发令牌(验证码一次性消费)
  3. POST /system/auth/reset-password:传入 mobile、code、password,校验通过后更新该手机号用户的密码

短信渠道未配置(如本地调试)时,发送会失败但验证码仍保留在 Redis,便于本地联调。

第三方社交登录 ​

社交登录支持企业微信、钉钉、飞书等渠道,由 SocialTypeEnum 统一枚举。第三方应用的 client_id / client_secret 等凭据维护在「社交客户端」表中(见下文)。

浏览器跳转模式(推荐) ​

适用于前端页面点击第三方图标后由浏览器发起授权的场景:

关键接口:

  • GET /system/auth/social-auth-redirect:返回 302 跳转,必须让浏览器直接导航(window.location.href),不能通过 axios 跟随跨域 302
  • GET /system/auth/oauth/{provider}/callback:第三方授权后回跳,完成登录后重定向回前端(成功带 access_token,失败带 oauth_error)
  • POST /system/auth/social-login:社交快捷登录,直接传入 type / code / state 完成登录(适用于前端已自行拿到授权码的场景)

安全设计:

  • state 随机串存入 Redis 并校验,防止 CSRF / 重放
  • 前端回跳地址 redirectUri 需命中 OAUTH_REDIRECT_ALLOWED_HOSTS 白名单,否则拒绝跳转,避免开放重定向
  • 未绑定系统用户的社交账号,会在首次登录时自动注册系统用户并写入绑定关系

社交客户端与社交用户管理 ​

  • 「社交客户端」/system/social-client:维护各渠道的 client_id、client_secret、应用名、状态等,登录时按 social_type 读取凭据
  • 「社交用户」/system/social-user:记录第三方 openid、昵称、头像、令牌等,并提供当前用户的绑定/解绑接口(/get-bind-list、/bind、/unbind)

相关表:system_social_client、system_social_user、system_social_user_bind。

OAuth2 授权框架 ​

除社交登录外,系统内置一套完整的 OAuth2 授权服务器,使得第三方应用可以「代表用户」访问本系统开放接口。核心模型见 system_oauth2_client(客户端)、system_oauth2_access_token(令牌)、system_oauth2_code(授权码)、system_oauth2_approve(批准记录)、system_oauth2_refresh_token(刷新令牌)。

支持的授权类型(Oauth2GrantType):

  • authorization_code 授权码模式
  • implicit 简化模式
  • password 密码模式
  • client_credentials 客户端模式
  • refresh_token 刷新令牌

开放接口(/system/oauth2):

  • POST /system/oauth2/token:获取访问令牌,需在 Authorization 头中通过 Basic 传递 client_id:client_secret
  • POST /system/oauth2/check-token:校验令牌
  • DELETE /system/oauth2/token:吊销令牌
  • GET/POST /system/oauth2/authorize:授权页信息获取与授权申请(授权码/简化模式使用)
  • GET/PUT /system/oauth2/user:当前 OAuth2 用户基本信息

管理接口:/system/oauth2-client(客户端 CRUD)、/system/oauth2-token(令牌分页与吊销)。

前端 apps/web-ele/src/views/system/oauth2 提供客户端管理页与令牌管理页;views/_core/authentication/sso-login.vue 是用户侧授权同意页。

前端相关 ​

前端登录相关代码集中在 apps/web-ele/src/api/core/auth.ts 与认证 UI 组件 packages/effects/common-ui/src/ui/authentication:

  • loginApi / refreshTokenApi / logoutApi:分别对应登录、刷新、退出
  • getAuthPermissionInfoApi:获取 user / roles / menus / permissions
  • getCaptcha / sendSmsCode / smsLogin / register / smsResetPassword:验证码与短信链路
  • socialAuthRedirect / socialLogin:社交登录(前者返回完整 URL 由浏览器跳转)
  • 登录页组件包括账号登录、手机号验证码登录(code-login)、二维码登录(qrcode-login)、钉钉登录(dingding-login)、注册(register)、忘记密码(forget-password)

典型前端登录时序:

约定:

  • 请求统一在 Authorization 头携带 Bearer <accessToken>
  • 访问令牌临近过期时,前端拦截器使用 refreshToken 静默刷新,失败则跳转登录页
  • 权限信息中的 permissions 配合 useAccess 与 v-access 实现按钮级鉴权

安全设计要点 ​

  • 会话与令牌分离:JWT 不直接携带用户主体,仅持有 session_id,吊销/踢人通过删除 Redis 会话实现
  • 验证码一次性:图形与短信验证码校验成功后立即删除,防止重放
  • 登录全链路审计:无论成功或失败(验证码错误、账号密码错误、账号停用、刷新、退出),均写入登录日志
  • 社交登录防开放重定向:前端回跳地址必须命中 OAUTH_REDIRECT_ALLOWED_HOSTS 白名单;state 机制防 CSRF
  • OAuth2 客户端隔离:第三方应用通过 client_id/secret 认证,授权范围 scope 由用户在授权页确认