认证授权
概述
系统采用 JWT Token 认证方案,基于 OAuth2 协议实现。用户端认证入口为 AppAuthController(路径前缀 /member/auth/),支持多种登录方式。
认证流程
用户发起登 录请求
↓
后端校验凭据(手机号+密码 / 验证码 / 社交授权码)
↓
创建 OAuth2 AccessToken + RefreshToken
↓
返回 { accessToken, refreshToken, openid }
↓
前端保存 Token 到本地存储
↓
后续请求在 Header 中携带 Authorization: Bearer {accessToken}
登录方式
1. 手机号 + 密码登录
接口:POST /app-api/member/auth/login
// 请求体
{
"mobile": "13800138000",
"password": "123456",
"socialType": null, // 可选,同时绑定社交账号
"socialCode": null,
"socialState": null
}
处理流程:
- 根据手机号查找用户
- 校验密码(BCrypt 加密比对)
- 若
socialType非空,同时绑定社交用户 - 创建 Token 令牌
- 记录登录日志
2. 手机号 + 验证码登录
发送验证码:POST /app-api/member/auth/send-sms-code
{ "mobile": "13800138000" }
验证码登录:POST /app-api/member/auth/sms-login
{
"mobile": "13800138000",
"code": "1234",
"loginType": 10, // 10=普通验证码登录,20=一键快捷登录
"socialType": null, // 可选
"socialCode": null,
"socialState": null
}
处理流程:
- 校验短信验证码(测试号
18612982956跳过验证) - 自动创建用户(如不存在)
- 校验用户状态(是否被禁用)
- 创建 Token 令牌
3. 微信小程序登录
获取 openId:POST /app-api/member/auth/get-wx-openid
{ "loginCode": "微信临时登录凭证" }
一键登录:POST /app-api/member/auth/weixin-mini-app-login
{
"loginCode": "微信临时登录凭证",
"phoneCode": "手机号授权码" // 可选,获取绑定手机号
}
处理流程:
- 通过
loginCode调用微信接口获取sessionKey+openId - 根据
openId查找已绑定用户,不存在则自动创建 - 若提供了
phoneCode,解密获取手机号并绑定 - 创建 Token 令牌,返回中包含
openid
4. 社交快捷登录
获取授权跳转 URL:GET /app-api/member/auth/social-auth-redirect
| 参数 | 说明 |
|---|---|
type | 社交类型枚举值 |
redirectUri | 回调地址 |
授权码登录:POST /app-api/member/auth/social-login
{
"type": 1,
"code": "社交授权码",
"state": "防 CSRF 状态值"
}
处理流程:
- 通过
code获取社交用户信息 - 若已绑定用户 → 直接登录
- 若未绑定 → 自动创建用户并绑定
- 创建 Token 令牌
Token 机制
令牌结构
| 字段 | 说明 |
|---|---|
accessToken | 访问令牌,用于 API 认证 |
refreshToken | 刷新令牌,用于获取新的 accessToken |
openid | 微信 openId(仅微信登录时返回) |
令牌刷新
接口:POST /app-api/member/auth/refresh-token
{ "refreshToken": "刷新令牌" }
前端自动刷新逻辑:
请求返回 401
↓
检查是否为 refresh-token 请求本身 → 是 → 直接拒绝
↓
检查 isRefreshToken 标志
↓ 否
设置 isRefreshToken = true
↓
调用 refresh-token 接口
↓ 成功
更新 Token → 回放队列中的请求 → isRefreshToken = false
↓ 失败
清空队列 → 清除登录状态 → 跳转登录页
并发请求处理:当多个请求同时返回 401 时,只有第一个请求执行刷新操作,其余请求进入等待队列。刷新成功后统一回放。
登出
接口:POST /app-api/member/auth/logout
前端调用 setToken('') 清除本地 Token,同时触发:
- 移除
StorageKeyEnum.TOKEN - 移除
StorageKeyEnum.REFRESH_TOKEN - 清空用户信息
- 设置
isLogin = false
请求认证机制
请求头
| Header | 说明 |
|---|---|
Authorization | Bearer {accessToken} 访问令牌 |
terminal | 终端类型枚举(10=小程序,11=公众号,20=H5,31=App) |
@PermitAll 注解
标记为 @PermitAll 的接口无需携带 Token,所有认证相关接口均使用此注解:
@PostMapping("/login")
@Operation(summary = "使用手机 + 密码登录")
@PermitAll
public CommonResult<AppAuthLoginRespVO> login(...) { }
登录日志
每次登录操作均记录登录日志,包含以下信息:
| 字段 | 说明 |
|---|---|
| 用户 ID | 登录用户 |
| 手机号 | 登录手机号 |
| 登录类型 | LOGIN_MOBILE / LOGIN_SMS / LOGIN_SOCIAL |
| 登录结果 | 成功 / 用户禁用 / 密码错误 等 |
| 用户 IP | 客户端 IP |
| User Agent | 浏览器信息 |
| 终端 | terminal 枚举值 |
短信验证码配置
yudao:
sms-code:
expire-times: 10m # 验证码有效期 10 分钟
send-frequency: 1m # 发送间隔最少 1 分钟
send-maximum-quantity-per-day: 10 # 每日最多发送 10 条
begin-code: 0000 # 验证码范围起始
end-code: 9999 # 验证码范围结束
前端登录状态管理
登录成功后的连锁操作
// store.setToken() 内部流程
this.isLogin = true
uni.setStorageSync(StorageKeyEnum.TOKEN, token)
uni.setStorageSync(StorageKeyEnum.REFRESH_TOKEN, refreshToken)
this.user = await getUserInfoApi() // 拉取用户信息
await useIM().login() // 登录腾讯云 IM
被踢下线处理
当同一账号在其他设备登录时,IM SDK 触发 KICKED_OUT 事件:
const onKickedOut = () => {
showToast('账号在其他设备登录', async () => {
useStore().setToken() // 清除本地 Token
await useIM().logout() // 登出 IM
relaunchApp() // 重启应用
})
}
微信 JS-SDK 签名
用于微信分享、支付等 JS-SDK 功能:
接口:POST /app-api/member/auth/create-weixin-jsapi-signature?url=xxx
返回签名信息:
{
"appId": "wx...",
"timestamp": 1234567890,
"nonceStr": "xxx",
"signature": "xxx"
}