登录方式说明
本文档说明 Cyber Hub 当前支持的登录方式、配置开关、接口字段和登录后的 token 行为。规则以现有代码为准,主要对应前端登录页和后端 /account/api/v1/oauth/* 接口。
总览
登录相关接口主要挂在 /account/api/v1 下。当前支持以下登录方式:
| 登录方式 | 前端入口 | 登录接口 | 是否受开关控制 |
|---|---|---|---|
| 账号密码登录 | /login | POST /account/api/v1/oauth/login | 否,默认入口 |
| 2FA 登录 | /login/2fa | POST /account/api/v1/oauth/login/2fa | enable2FA |
| 手机验证码登录 | /login/phone | POST /account/api/v1/oauth/login/mobile | tencent.sms.enableSms |
| 邮箱验证码登录 | /login/email | POST /account/api/v1/oauth/login/email | tencent.email.enableEmail |
| 企业微信登录 | /login/wecom | GET /account/api/v1/oauth/login/wecom?code=... | tencent.wecom.enableWecom |
登录入口开关由前端调用以下接口获取:
GET /sansi/daemon/api/v1/config/account/loginSwitch响应字段:
| 字段 | 来源配置 | 说明 |
|---|---|---|
enableWecom | tencent.wecom.enableWecom | 是否显示企业微信登录入口 |
enableSms | tencent.sms.enableSms | 是否显示手机验证码登录入口 |
enableEmail | tencent.email.enableEmail | 是否显示邮箱验证码登录入口 |
enable2FA | enable2FA | 是否显示 2FA 登录入口 |
运行时配置来自 /config/account.json,默认模板在 server/src/config/account.json。这些开关主要控制前端入口显示,服务端登录接口本身仍会按账号、验证码、企业微信 code 等实际数据校验。这里设计有点别扭,别把“入口隐藏”理解成“接口禁用”。
账号密码登录
接口:
POST /account/api/v1/oauth/login
Content-Type: application/json请求体:
{
"username": "admin",
"password": "base64-encoded-or-plain-password"
}校验规则:
- 用户名必须存在。
- 用户必须启用,且未过期。
- 后端会尝试将
password按 Base64 解码;前端当前会提交 Base64 后的密码。 - 解码后的密码必须符合密码策略:至少 8 位,不能与账号相同,且至少包含小写字母、大写字母、数字、特殊字符中的 2 类。
- 密码使用 bcrypt 校验。
- 同一账号 30 分钟内出现 5 次账号密码错误,会拒绝继续登录,并提示剩余等待分钟数。
账号密码登录失败锁定只统计 LoginTypeUsername 且消息为用户名密码错误的失败日志,不会统计手机、邮箱、2FA、企业微信登录失败。
2FA 登录
接口:
POST /account/api/v1/oauth/login/2fa
Content-Type: application/json请求体:
{
"username": "admin",
"password": "base64-encoded-or-plain-password",
"otpcode": "123456"
}校验规则:
- 用户名必须存在。
- 用户必须启用,且未过期。
- 密码必须通过 bcrypt 校验。
otpcode必须能通过 TOTP 校验,校验 secret 来自用户绑定的otpSecret。
2FA 是否展示由 enable2FA 控制,但真正能不能登录还取决于用户是否已经绑定 2FA。没有绑定 secret 的账号硬上 2FA 登录,只会失败。
手机验证码登录
发送验证码接口:
POST /account/api/v1/sms/info
Content-Type: application/json请求体:
{
"countryCode": "+86",
"mobile": "13800000000"
}登录接口:
POST /account/api/v1/oauth/login/mobile
Content-Type: application/json请求体:
{
"countryCode": "+86",
"mobile": "13800000000",
"code": "123456"
}校验规则:
- 手机号必须绑定到本地用户。
- 用户必须启用,且未过期。
- 验证码必须和
countryCode + mobile匹配。 - 验证码校验成功后会删除验证码记录,不能重复使用。
verificationCodeExpire 当前主要用于短信/邮件模板展示;验证码实际校验逻辑是按内容和账号匹配后删除,代码里没有按该字段做过期时间判断。这个坑别脑补,文档先按代码写。
邮箱验证码登录
发送验证码接口:
POST /account/api/v1/email/info
Content-Type: application/json请求体:
{
"email": "user@example.com"
}登录接口:
POST /account/api/v1/oauth/login/email
Content-Type: application/json请求体:
{
"email": "user@example.com",
"code": "123456"
}校验规则:
- 邮箱必须绑定到本地用户。
- 用户必须启用,且未过期。
- 验证码必须和邮箱匹配。
- 验证码校验成功后会删除验证码记录,不能重复使用。
企业微信登录
接口:
GET /account/api/v1/oauth/login/wecom?code=企业微信授权码校验和处理规则:
- 后端使用企业微信
code获取企业微信用户信息。 - 如果本地不存在对应
wwUserId用户,会自动创建用户。 - 自动创建的用户默认角色为
cyberhub:user。 - 自动创建的用户默认密码使用系统默认密码哈希值,当前默认密码常量为
sansi1280。 - 用户必须启用,且未过期。
企业微信配置来自 account.json 的 tencent.wecom 节点,至少需要正确配置企业微信应用相关参数。入口开关只负责显示入口,不代表企业微信侧配置已经可用。
登录成功后的统一行为
以上登录方式成功后都会生成登录 token,响应结构一致:
{
"accessToken": "access-token",
"accessTokenCreateAt": 1710000000000,
"accessTokenExpiresIn": 1710604800000,
"refreshToken": "refresh-token",
"refreshTokenCreateAt": 1710000000000,
"refreshTokenExpiresIn": 1712592000000
}统一行为:
- 服务端创建
LoginToken记录。 - 服务端更新用户最后登录时间。
- 服务端记录登录成功日志。
- 服务端写入 HttpOnly Cookie:
authorizationKey=<accessToken>,当前 max-age 为 86400 秒。 - 前端会把
accessToken写入localStorage.authorizationKey和localStorage.ACCESS_TOKEN。 - 前端后续请求通过
Authorization: Bearer <accessToken>携带 token。
token 有效期规则:
| token | 有效期 | 说明 |
|---|---|---|
| access token | 7 天 | 用于接口鉴权 |
| refresh token | 30 天 | 可通过刷新接口换新 token |
刷新 token:
POST /account/api/v1/oauth/refreshToken
Content-Type: application/json请求体:
{
"refreshToken": "refresh-token"
}登出
当前会话登出:
GET /account/api/v1/oauth/logout
Authorization: Bearer <accessToken>规则:
- 清除
authorizationKeyCookie。 - 如果当前 token 是普通登录 token,会删除对应
LoginToken。 - 如果当前 token 是用户自建 token,不删除 token 记录。
登出所有普通登录会话:
GET /account/api/v1/oauth/logout/all
Authorization: Bearer <accessToken>规则:
- 清除
authorizationKeyCookie。 - 删除当前用户所有普通登录 token。
鉴权读取 token 的优先级
受保护接口读取 token 的优先级如下:
- URL 查询参数:
authorizationKey - Cookie:
authorizationKey - Header:
Authorization: Bearer <token>
正常前端请求走 Header。URL 参数和 Cookie 更多是兼容入口,别在新逻辑里乱塞 token,安全味儿不太对。
常见排查点
- 登录页没有显示某种登录方式:先查
GET /sansi/daemon/api/v1/config/account/loginSwitch返回值,再查/config/account.json。 - 手机/邮箱验证码登录失败:确认用户已绑定对应手机号/邮箱,且验证码和手机号/邮箱完全匹配。
- 2FA 登录失败:确认用户已绑定
otpSecret,并检查客户端时间是否和服务器时间偏差过大。 - 接口返回未认证:检查请求是否带
Authorization: Bearer <accessToken>,以及 token 是否过期或已被登出删除。