认证与授权
概述
系统采用 JWT + Redis 的认证方案,支持多设备登录控制、Token 刷新、OAuth2 多平台登录。
认证流程
┌──────────┐ POST /login ┌──────────┐
│ 客户端 │ ───────────────→ │ 后端 │
│ │ ←─────────────── │ │
│ │ accessToken │ 1. 验证密码│
│ │ refreshToken │ 2. 生成JWT │
│ │ │ 3. 缓存Redis│
│ │ └──────────┘
│ │ 请求头 Bearer
│ │ ───────────────→ ┌──────────┐
│ │ │ 中间件 │
│ │ │ 1. 解析JWT │
│ │ │ 2. Redis读│
│ │ │ 角色信息 │
│ │ │ 3. 权限检查│
└──────────┘ └──────────┘JWT Token 设计
Token 结构
Token 中仅存储身份标识,不存储角色等动态信息:
python
token_data = {
"sub": user.id, # 用户ID
"username": user.username, # 用户名
"device_id": device_id, # 设备标识
"exp": expire_time, # 过期时间
"type": "access" # Token类型
}Token 配置
| 配置项 | 默认值 | 说明 |
|---|---|---|
JWT_SECRET_KEY | - | JWT 密钥(生产环境必须修改) |
JWT_ALGORITHM | HS256 | 加密算法 |
ACCESS_TOKEN_EXPIRE_MINUTES | 1440 | Access Token 过期时间(24小时) |
REFRESH_TOKEN_EXPIRE_DAYS | 7 | Refresh Token 过期时间(7天) |
ALLOW_MULTI_DEVICE_LOGIN | True | 是否允许多设备同时登录 |
Redis 存储
登录成功后,在 Redis 中存储以下信息:
| Key | 过期时间 | 说明 |
|---|---|---|
refresh_token:{user_id}:{device_id} | 7天 | Refresh Token |
access_token:{user_id}:{device_id} | 24小时 | Access Token |
device_info:{user_id}:{device_id} | 7天 | 设备信息 Hash |
user_info_cache:{user_id} | - | 用户角色等动态信息 |
登录接口
JSON 登录
POST /api/core/login
Content-Type: application/json
{
"username": "admin",
"password": "123456"
}OAuth2 登录(Swagger 用)
POST /api/core/login/oauth2
Content-Type: application/x-www-form-urlencoded
username=admin&password=123456登录响应
json
{
"accessToken": "eyJhbGciOi...",
"refreshToken": "eyJhbGciOi...",
"tokenType": "bearer",
"expireTime": 86400
}登录流程
- 验证用户:用户名 + 密码(bcrypt 加密)
- 检查状态:is_active、user_status(0=禁用、1=正常、2=锁定)
- 生成 Token:Access Token + Refresh Token(包含 device_id)
- 缓存信息:用户角色信息写入 Redis
- 设备管理:单设备模式下清除其他设备 Token
- 记录日志:登录日志(IP、浏览器、设备类型)
Token 刷新
POST /api/core/refresh_token
Content-Type: application/json
{
"refresh_token": "eyJhbGciOi..."
}并发刷新处理:刷新时保留上一个 Token 60 秒(prev 键),防止并发请求导致 Token 失效。
多设备登录控制
通过 ALLOW_MULTI_DEVICE_LOGIN 配置:
- True(默认):允许多设备同时在线
- False:新登录时清除该用户所有旧设备的 Token
设备标识基于 User-Agent + IP 生成。
OAuth2 多平台登录
支持 10+ OAuth 平台:
| 平台 | 配置前缀 | 说明 |
|---|---|---|
| Gitee | GITEE_ | Gitee 登录 |
| GitHub | GITHUB_ | GitHub 登录 |
QQ_ | QQ 登录 | |
GOOGLE_ | Google 登录 | |
| 微信 | WECHAT_ | 微信开放平台 |
| Microsoft | MICROSOFT_ | Microsoft 登录 |
| 钉钉 | DINGTALK_ | 钉钉登录 |
| 飞书 | FEISHU_ | 飞书登录 |
| 企业微信 | WECOM_ | 企业微信登录 |
每个平台需要配置 CLIENT_ID、CLIENT_SECRET、REDIRECT_URI。
OAuth 登录流程:
- 前端跳转到平台授权页
- 用户授权后回调到
REDIRECT_URI - 后端用
code换取用户信息 - 自动创建/关联用户
- 生成 JWT Token 返回
认证中间件
AuthPermissionMiddleware 在每个请求前执行:
- 白名单检查:
/login、/docs、/openapi.json等路径跳过认证 - 解析 Token:从
Authorization: Bearer {token}提取并验证 JWT - 加载用户信息:从 Redis 获取角色、部门等信息
- 注入请求上下文:将
user_id、role_ids、dept_id、is_superuser写入request.state
python
# 在 API 中获取当前用户信息
@router.get("/")
async def get_data(request: Request):
user_id = request.state.user_id
role_ids = request.state.role_ids
dept_id = request.state.dept_id
is_superuser = request.state.is_superuser权限系统
三级权限体系
操作权限 (Permission)
└── 控制用户能执行哪些操作(查看/新增/编辑/删除/导出/导入)
数据权限 (Data Scope)
└── 控制用户能看到哪些数据(全部/本人/本部门/本部门及子部门/自定义)
字段权限 (Field Permission)
└── 控制用户能看到哪些字段(可写/只读/隐藏/脱敏)操作权限
基于 RBAC(角色-权限模型):
用户 → 角色 → 权限 → 菜单/操作权限编码格式:form:{form_code}:{action}
| action | 说明 |
|---|---|
view | 查看 |
add | 新增 |
edit | 编辑 |
delete | 删除 |
export | 导出 |
import | 导入 |
数据权限
通过 ResourceDataScopeConfig 绑定到资源类型:
| scope_type | 说明 |
|---|---|
all | 全部数据 |
self | 仅本人创建的数据 |
dept | 本部门数据 |
dept_and_children | 本部门及子部门数据 |
custom | 自定义部门范围 |
字段权限
通过 ResourceFieldPermission 绑定到角色和资源类型:
| 权限级别 | 说明 |
|---|---|
write | 可读写 |
read | 只读 |
hidden | 隐藏(不返回该字段) |
masked | 脱敏(如手机号显示为 138****8888) |
脱敏规则:
- 手机号:保留前 3 后 4
- 邮箱:保留前 2 后域名
- 身份证:保留前 3 后 4
- 姓名:保留首字
- 默认:保留前 2 后 2