chat
CRUD
在已有的这2个表(sso_client_uri+sso_tickets)的 crud 基础上,sso 平台已有登录功能。
在这个基础上增强,让其支持 sso 单点登录。
给出 sso 前端的登录调整+sso 后端的提供 openapi 接口,可以是 /openapi 开头的 http 请求。
其他系统请求根据 ticket 换取 token 时,需要携带对应的 apiKey,和普通的用户登录区分开。
这个以前已经维护了
desc sys_user_apikey;
+-------------+--------------+------+-----+----------------------+--------------------------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------+------+-----+----------------------+--------------------------------+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| user_id | bigint(20) | NO | MUL | NULL | |
| api_key | varchar(128) | NO | UNI | NULL | |
| name | varchar(64) | NO | | NULL | |
| status | tinyint(4) | NO | MUL | 1 | |
| expire_time | datetime(3) | YES | MUL | NULL | |
| remark | varchar(255) | YES | | NULL | |
| create_time | datetime(3) | NO | MUL | CURRENT_TIMESTAMP(3) | |
| update_time | datetime(3) | NO | | CURRENT_TIMESTAMP(3) | on update CURRENT_TIMESTAMP(3) |
| creator_id | bigint(20) | YES | | NULL | |
| updater_id | bigint(20) | YES | | NULL | |
| delete_flag | tinyint(4) | NO | | 0 | |
+-------------+--------------+------+-----+----------------------+--------------------------------+
访问的开放接口定义+访问日志以前表也定义好了:
mysql> desc sys_openapi;
+-------------+--------------+------+-----+----------------------+--------------------------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------+------+-----+----------------------+--------------------------------+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| name | varchar(128) | NO | | NULL | |
| code | varchar(128) | NO | UNI | NULL | |
| description | varchar(512) | YES | | NULL | |
| method | varchar(16) | YES | | NULL | |
| path | varchar(256) | YES | | NULL | |
| status | tinyint(4) | NO | MUL | 1 | |
| create_time | datetime(3) | NO | | CURRENT_TIMESTAMP(3) | |
| update_time | datetime(3) | NO | | CURRENT_TIMESTAMP(3) | on update CURRENT_TIMESTAMP(3) |
| creator_id | bigint(20) | YES | | NULL | |
| updater_id | bigint(20) | YES | | NULL | |
| delete_flag | tinyint(4) | NO | | 0 | |
+-------------+--------------+------+-----+----------------------+--------------------------------+
> desc sys_openapi_access_log;
+----------------+--------------+------+-----+----------------------+--------------------------------+
| Field | Type | Null | Key | Default | Extra |
+----------------+--------------+------+-----+----------------------+--------------------------------+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| apikey_id | bigint(20) | NO | MUL | NULL | |
| openapi_id | bigint(20) | NO | MUL | NULL | |
| request_params | text | YES | | NULL | |
| response_body | text | YES | | NULL | |
| response_code | int(11) | YES | | NULL | |
| response_time | int(11) | YES | | NULL | |
| success_flag | tinyint(1) | YES | MUL | 1 | |
| ip_address | varchar(64) | YES | | NULL | |
| status | tinyint(4) | NO | MUL | 1 | |
| remark | varchar(255) | YES | | NULL | |
| error_code | varchar(64) | YES | MUL | NULL | |
| error_message | text | YES | | NULL | |
| create_time | datetime(3) | NO | MUL | CURRENT_TIMESTAMP(3) | |
| update_time | datetime(3) | NO | | CURRENT_TIMESTAMP(3) | on update CURRENT_TIMESTAMP(3) |
| creator_id | bigint(20) | YES | | NULL | |
| updater_id | bigint(20) | YES | | NULL | |
| delete_flag | tinyint(4) | NO | | 0 | |
+----------------+--------------+------+-----+----------------------+--------------------------------+
同时创建一个用于测试的前后端项目,验证 sso 登录的正确性。
sso-client-backend
sso-client-frontend
技术栈和现在的类似,功能尽可能的简化。
给出单点登录的详细设计+交互设计,不需要具体的代码。
最少改动
好的,我们把你的 SSO 设计进一步极限简化——后端只新增一个接口:根据 ticket 换 user 信息。 登录、JWT、会话、密码校验全部复用你当前已有的 UserController.login 逻辑,只是在登录成功后,额外发放一次性 Ticket。
下面给你一个 最小但完整的“可落地的 SSO 能力”设计,包括流程、接口、页面交互、错误处理、测试客户端结构等。
✅ 一、最终版 SSO 方案(极简)
SSO 平台复用原有登录接口,只做两件事:
1)当其他系统跳转到 /login 时,SSO 平台能识别一个 client 登录请求,并在登录成功后颁发一次性 ticket。
2)新增一个开放接口:根据 ticket 换取用户信息(或签发 jwt)
其他系统统一调用此接口,不需要其他开放接口。
✨ 二、前端交互(SSO 登录页)
1. 登录页 URL 结构(新增参数)
/login?client_id=xxx&redirect_uri=xxx&state=xxx
参数说明:
| 参数 | 说明 |
|---|---|
| client_id | 在 sso_client_uri 注册的系统标识 |
| redirect_uri | 业务系统传来的回跳地址,必须在白名单内 |
| state | 业务系统原始 state,用于防止 CSRF |
这三个参数只有“业务系统发起登录”时才存在,用户在 SSO 网站本身登录不需要。
2. 登录页判断逻辑
(1)如果无 client_id,就是普通登录
→ 走现有逻辑 → 返回用户自己的 SSO 首页
(2)如果带 client_id,则是 SSO 登录
流程变为:
- 用户输入用户名密码
- 调用 UserController.login(已有)
- 登录成功 → 创建 ticket → 302 回跳业务系统
- 回跳 URL:
redirect_uri?ticket=XXXXX&state=YYY
🎫 三、一次性 Ticket 发放逻辑
你已有 sso_tickets 表,流程如下:
- 生成随机 ticket(128 长度)
- 写入表
- expire_time = now() + 60s
- used = 0
写入示例:
| 字段 | 内容 |
|---|---|
| ticket | 随机 128 位字符串 |
| user_id | 当前用户 |
| client_id | 从请求参数而来 |
| redirect_uri | 从本次请求带来 |
| state | 从请求带来 |
| expire_time | now()+60s |
| used | 0 |
此逻辑直接加到 UserController.login 的成功返回之前。
🔐 四、后端新增唯一接口:ticket 换 user 信息
新增开放接口(openapi 系统内注册):
POST /openapi/sso/ticket/verify
请求参数(POST JSON)
{
"ticket": "xxxx",
"apiKey": "client-api-key"
}
调用方流程
- 请求头可带签名,也可不用(你现有规范即可)
- apiKey 在 sys_user_apikey 表中校验
- ticket 必须存在、未过期、未使用、未删除
接口校验规则
| 校验项 | 说明 |
|---|---|
| ticket 存在 | 必须在 sso_tickets |
| ticket.used == 0 | Ticket 必须一次性 |
| ticket.expire_time > now() | 必须未过期 |
| apiKey 有效 | 必须存在且未过期 |
| 关联关系验证(可选) | apiKey 对应用户与 client_id 允不允许(可配可不配) |
返回结果示例
✔ 正常返回
{
"success": true,
"user_id": 1001,
"username": "echo",
"extra": {
"roles": ["admin"],
"email": "a@b.com"
}
}
并把 ticket.used = 1
❌ ticket 过期
{
"success": false,
"error": "TICKET_EXPIRED"
}
❌ apiKey 无效
{
"success": false,
"error": "APIKEY_INVALID"
}
🖼 五、业务系统(sso-client)侧流程
业务系统也极简:
1. 未登录,保护接口发现无 token
跳转 SSO:
https://sso.com/login
?client_id=client-A
&redirect_uri=https://a.com/sso/callback
&state=xyz123
2. 登录后回跳(带 ticket)
GET /sso/callback?ticket=xxxx&state=xyz123
3. 后端调用 SSO ticket 校验接口
POST https://sso.com/openapi/sso/ticket/verify
{
"ticket": "xxxx",
"apiKey": "client_api_key"
}
4. 校验成功 → 业务系统给用户签发自己的 JWT
业务系统用自己的 token 管用户本地会话:
jwt = generateJWT(user)
set-cookie jwt
redirect to home
🧪 六、测试用 sso-client 项目结构(极简)
sso-client-backend/
├── /login-check 未登录跳转 sso
├── /sso/callback sso 回跳处理,换 user 并签发本系统 jwt
└── /me 测试当前用户
sso-client-frontend/
├── 登录检测中间件
├── /profile 页面
└── axios 封装
你只需要:
- 前端检测无 token → 跳 SSO
- 后端收到 ticket → 调 verify → 发自己的 token → 前端写 cookie
🚪 七、SSO 登录页交互 UX
登录页 UI 不变,只是增强行为:
(1)当 url 中包含 client_id 时
顶部提示:
🔐 你正在登录系统:Client-A
(2)登录成功直接 302 回跳
(3)失败正常展示错误
📌 八、必要的配置/安全考虑
| 关键点 | 作用 |
|---|---|
| Ticket 一次性使用 | 安全性核心 |
| Ticket 过期时间建议 60 秒 | 防止劫持 |
| redirect_uri 必须在白名单中 | 防止钓鱼攻击 |
| client_id 必须在 sso_client_uri 注册 | 来源可信 |
| apiKey 必须属于该 client 的系统账号 | 权限隔离 |
