17 KiB
17 KiB
认证接口
**本文引用的文件** - [AuthController.java](file://fms-api/src/main/java/cn/g3soft/fmsapi/controller/AuthController.java) - [AuthService.java](file://fms-api/src/main/java/cn/g3soft/fmsapi/service/AuthService.java) - [JwtAuthFilter.java](file://fms-api/src/main/java/cn/g3soft/fmsapi/config/JwtAuthFilter.java) - [JwtUtils.java](file://fms-api/src/main/java/cn/g3soft/fmsapi/utils/JwtUtils.java) - [AuthProperties.java](file://fms-api/src/main/java/cn/g3soft/fmsapi/config/AuthProperties.java) - [ApiResponse.java](file://fms-api/src/main/java/cn/g3soft/fmsapi/utils/ApiResponse.java) - [AuthenticationException.java](file://fms-api/src/main/java/cn/g3soft/fmsapi/exception/AuthenticationException.java) - [ActiveSessionRegistry.java](file://fms-api/src/main/java/cn/g3soft/fmsapi/service/ActiveSessionRegistry.java) - [LoginLogService.java](file://fms-api/src/main/java/cn/g3soft/fmsapi/service/LoginLogService.java)目录
简介
本文件为 FMS 系统认证相关 RESTful API 的权威文档,覆盖用户登录、令牌校验与会话管理等能力。文档包含:
- 所有认证端点的 HTTP 方法、URL、请求参数、响应格式与错误码
- JWT 认证流程的完整调用示例(登录、获取令牌、权限验证)
- 认证过滤器的工作原理与拦截机制
- 常见认证错误处理与故障排除建议
项目结构
认证功能主要位于后端 fms-api 模块中,关键目录与职责如下:
- controller:对外暴露认证接口
- service:认证业务逻辑、会话管理、登录日志记录
- config:认证过滤器、配置属性
- utils:JWT 工具、统一响应体
- exception:认证异常类型
graph TB
Client["客户端"] --> Filter["JwtAuthFilter<br/>请求拦截与鉴权"]
Filter --> Controller["AuthController<br/>/auth/login"]
Controller --> Service["AuthService<br/>登录校验、生成Token、记录日志"]
Service --> Db["DbUtils<br/>查询用户信息"]
Service --> Jwt["JwtUtils<br/>签发/解析JWT"]
Service --> Session["ActiveSessionRegistry<br/>活跃会话管理"]
Service --> Log["LoginLogService<br/>登录日志记录"]
Filter --> Session
Filter --> Jwt
图表来源
- JwtAuthFilter.java:19-88
- AuthController.java:19-55
- AuthService.java:19-84
- JwtUtils.java:18-80
- ActiveSessionRegistry.java
- LoginLogService.java
章节来源
核心组件
- 认证控制器:提供登录接口,接收 orgid、userid、password,返回统一响应体
- 认证服务:执行用户校验、密码比对、生成 JWT、激活会话、记录登录日志
- JWT 工具:负责密钥初始化、签发与解析令牌
- 认证过滤器:对所有非登录请求进行 Token 校验与活跃会话检查,设置上下文
- 会话注册表:维护当前活跃会话,支持单点互踢等策略
- 登录日志:记录登录成功/失败及原因
- 统一响应体:定义标准 code/message/data 结构与常用错误码
章节来源
- AuthController.java:19-55
- AuthService.java:19-84
- JwtUtils.java:18-80
- JwtAuthFilter.java:19-88
- ApiResponse.java:1-32
架构总览
下图展示了从客户端发起登录到后续受保护接口调用的完整流程,包括过滤器拦截、JWT 校验与会话检查。
sequenceDiagram
participant C as "客户端"
participant F as "JwtAuthFilter"
participant A as "AuthController"
participant S as "AuthService"
participant J as "JwtUtils"
participant R as "ActiveSessionRegistry"
participant L as "LoginLogService"
C->>A : POST /auth/login {orgid, userid, password}
A->>S : login(orgid, userid, password, request)
S->>S : 校验用户/密码
alt 认证成功
S->>J : generateToken(userId, orgId, sessionId)
S->>R : activate(orgId, userId, sessionId)
S->>L : recordLogin(成功, sessionId)
S-->>A : {token, orgid, user}
A-->>C : ApiResponse.success(...)
else 认证失败
S->>L : recordLogin(失败, 原因)
S-->>A : Optional.empty()
A-->>C : ApiResponse.fail(401, 错误信息)
end
Note over C,F : 后续受保护接口调用
C->>F : 携带 Authorization : Bearer <token>
F->>J : parseToken(token)
F->>R : hasActiveSession(orgId, userId)
F->>R : isActive(orgId, userId, sessionId)
alt 通过
F->>F : 设置 OrgContext
F-->>C : 放行至业务接口
else 未通过
F-->>C : ApiResponse.fail(401/40101, 错误信息)
end
图表来源
- AuthController.java:26-55
- AuthService.java:34-84
- JwtUtils.java:45-66
- JwtAuthFilter.java:34-88
- ActiveSessionRegistry.java
- LoginLogService.java
详细组件分析
认证控制器 AuthController
- 路径与方法:POST /auth/login
- 请求体:JSON 对象,包含 orgid、userid、password
- 参数校验:orgid、userid 为空时抛出业务异常
- 成功响应:code=0,data 包含 token、orgid、user(含 id、account、name)
- 失败响应:code=401,message 提示“机构码、账号或密码错误”
flowchart TD
Start(["进入 /auth/login"]) --> Parse["解析请求体<br/>orgid, userid, password"]
Parse --> Validate{"orgid/userid 是否为空?"}
Validate --> |是| ThrowErr["抛出业务异常"]
Validate --> |否| CallSvc["调用 AuthService.login(...)"]
CallSvc --> Result{"是否返回用户信息?"}
Result --> |否| FailResp["返回 401 错误"]
Result --> |是| SuccessResp["返回 200 + token/orgid/user"]
ThrowErr --> End(["结束"])
FailResp --> End
SuccessResp --> End
图表来源
章节来源
认证服务 AuthService
- 登录流程:
- 规范化 orgid(大写)、userid(去空白)
- 查询用户并校验密码(安全比较)
- 生成 JWT(包含 userId、orgId、sessionId)
- 激活会话(ActiveSessionRegistry)
- 记录登录日志(成功/失败)
- 组装返回数据(token、orgid、user)
- 密码比较:使用常量时间比较避免时序攻击
- 错误处理:账号不存在或密码错误均记录日志并返回空结果
flowchart TD
S0["开始 login"] --> N1["规范化 orgid/userid"]
N1 --> Q1["查询用户"]
Q1 --> E1{"找到用户?"}
E1 --> |否| L1["记录登录失败(账号不存在)"] --> R1["返回空"]
E1 --> |是| P1["校验密码"]
P1 --> E2{"密码正确?"}
E2 --> |否| L2["记录登录失败(密码错误)"] --> R2["返回空"]
E2 --> |是| T1["生成JWT(含sessionId)"]
T1 --> A1["激活会话"]
A1 --> L3["记录登录成功(含sessionId)"]
L3 --> D1["组装返回{token, orgid, user}"]
D1 --> R3["返回结果"]
图表来源
章节来源
JWT 工具 JwtUtils
- 初始化:读取配置 fms.auth.jwt-secret、jwt-expiration,校验长度与有效期
- 签发:生成包含 subject(userId)、id(sessionId)、claim(orgId)、issuedAt、expiration 的 JWT
- 解析:提取 userId、orgId、sessionId,缺失则抛异常
- 组织上下文:配合过滤器设置 OrgContext
classDiagram
class JwtUtils {
+generateToken(userId, orgId, sessionId) String
+parseToken(token) TokenClaims
+getOrgId(token) String
}
class TokenClaims {
+String userId
+String orgId
+String sessionId
}
JwtUtils --> TokenClaims : "返回"
图表来源
章节来源
认证过滤器 JwtAuthFilter
- 过滤范围:跳过 OPTIONS 预检与 /auth/login
- 鉴权步骤:
- 从 Authorization 头提取 Bearer Token
- 解析 Token 获取 claims
- 校验是否存在活跃会话
- 校验当前 sessionId 是否仍有效(防止多设备互踢)
- 将 orgId、userId 写入上下文,放行后清理
- 失败响应:统一返回 200 状态码 + ApiResponse.fail(code, message)
flowchart TD
F0["进入 doFilterInternal"] --> H0{"是否OPTIONS或/login?"}
H0 --> |是| Skip["跳过过滤"] --> Next["继续链式过滤"]
H0 --> |否| G0["提取Authorization头"]
G0 --> P0["解析Token"]
P0 --> S0{"存在活跃会话?"}
S0 --> |否| U0["返回401 未登录或失效"]
S0 --> |是| I0{"sessionId仍有效?"}
I0 --> |否| K0["返回40101 被其他设备顶替"]
I0 --> |是| C0["设置OrgContext"] --> Next
Next --> Clean["finally清理上下文"]
图表来源
章节来源
会话管理与登录日志
- ActiveSessionRegistry:维护每个用户的活跃会话集合,支持激活、检查、有效性判断
- LoginLogService:记录登录成功/失败、原因、会话ID、请求信息等,便于审计与排查
章节来源
依赖关系分析
- 控制器依赖服务层,服务层依赖数据库工具、JWT 工具、会话注册表与日志服务
- 过滤器依赖 JWT 工具与会话注册表,用于鉴权与上下文设置
- 配置类集中管理 JWT 密钥与过期时间
graph LR
AC["AuthController"] --> AS["AuthService"]
AS --> DBU["DbUtils"]
AS --> JU["JwtUtils"]
AS --> ASR["ActiveSessionRegistry"]
AS --> LLS["LoginLogService"]
JAF["JwtAuthFilter"] --> JU
JAF --> ASR
JU --> AP["AuthProperties"]
图表来源
- AuthController.java:19-55
- AuthService.java:19-84
- JwtAuthFilter.java:19-88
- JwtUtils.java:18-80
- AuthProperties.java:7-28
章节来源
- AuthController.java:19-55
- AuthService.java:19-84
- JwtAuthFilter.java:19-88
- JwtUtils.java:18-80
- AuthProperties.java:7-28
性能考虑
- 密码比较采用常量时间比较,降低时序攻击风险
- JWT 签发与解析基于高效库实现,注意合理设置过期时间以平衡安全性与性能
- 活跃会话检查为内存级操作,适合高并发场景;如需持久化可结合缓存
- 登录日志建议异步落库,避免阻塞主流程
故障排除指南
- 登录失败
- 检查 orgid、userid、password 是否正确
- 查看登录日志中的失败原因(账号不存在、密码错误)
- 鉴权失败(401)
- 确认 Authorization 头格式为 Bearer
- 检查 Token 是否过期或被撤销
- 会话被顶替(40101)
- 同一账号在其他设备登录后,旧设备会话失效,需重新登录
- 配置问题
- fms.auth.jwt-secret 长度不足 32 字节会导致启动失败
- fms.auth.jwt-expiration 必须大于 0
章节来源
结论
FMS 认证体系通过控制器、服务层、JWT 工具与过滤器协同工作,实现了安全的登录、令牌签发与会话管理。统一响应体与明确的错误码便于前端集成与排错。建议在生产环境严格配置 JWT 密钥与过期时间,并结合登录日志进行审计与监控。
附录:API 规范与调用示例
统一响应体
- 结构:{ code, message, data }
- 常用码值:
- 0:成功
- 401:未登录或凭证无效
- 40101:会话被其他设备顶替
- 1000:业务异常
- 2001:保存错误
- 500:系统错误
章节来源
登录接口
- 方法:POST
- 路径:/auth/login
- 请求体:
- orgid:字符串,必填
- userid:字符串,必填
- password:字符串,必填
- 成功响应:
- code=0
- data.token:JWT 字符串
- data.orgid:机构码
- data.user:{ id, account, name }
- 失败响应:
- code=401
- message:错误描述
章节来源
令牌校验与会话管理
- 校验方式:在后续受保护接口的请求头中添加 Authorization: Bearer
- 过滤器行为:
- 解析 Token,校验签名与有效期
- 检查活跃会话是否存在且 sessionId 匹配
- 设置 OrgContext 后放行
- 失败响应:
- 401:未登录或凭证无效
- 40101:账号已在其他设备登录,请重新登录
章节来源
完整调用示例(文字版)
- 登录
- 发送 POST /auth/login,请求体包含 orgid、userid、password
- 成功后获取 data.token
- 访问受保护接口
- 在请求头添加 Authorization: Bearer
- 若返回 40101,说明会话被顶替,需重新登录
- 退出/注销
- 可通过服务端提供的注销接口或使 Token 失效(如加入黑名单)实现;当前代码未提供显式注销端点,可按需扩展
章节来源