mirror of
https://gitee.com/dromara/sa-token.git
synced 2024-12-02 03:47:50 +08:00
新增用户数据同步/迁移方案的建议
This commit is contained in:
parent
b23aa55ffa
commit
55821d09c6
@ -147,7 +147,7 @@ Sa-Token SSO 分为三种模式,解决同域、跨域、共享Redis、跨Redis
|
||||
6. 提供前后端分离整合方案:无论是 sso-server 还是 sso-client 的前后端分离都可以整合。
|
||||
7. 提供安全校验:域名校验、ticket校验、参数签名校验,有效防 ticket 劫持,防请求重放等攻击。
|
||||
8. 参数防丢:笔者曾试验多个SSO框架,均有参数丢失情况,比如登录前是:`http://a.com?id=1&name=2`,登录成功后就变成了:`http://a.com?id=1`,Sa-Token-SSO 内有专门算法保证了参数不丢失,登录成功后精准原路返回。
|
||||
9. 提供用户数据迁移方案的建议:开发前统一迁移、运行时实时数据同步、根据关键字段匹配、根据 center_id 字段匹配。
|
||||
9. 提供用户数据同步/迁移方案的建议:开发前统一迁移、运行时实时数据同步、根据关联字段匹配、根据 center_id 字段匹配等。
|
||||
10. 提供直接可运行的 demo 示例,帮助你快速熟悉 SSO 大致登录流程。
|
||||
|
||||
|
||||
|
@ -47,6 +47,7 @@
|
||||
- [前后端分离下的整合方案](/sso/sso-h5)
|
||||
- [平台中心跳转模式](/sso/sso-home-jump)
|
||||
- [不同 Client 不同秘钥](/sso/sso-diff-key)
|
||||
- [用户数据同步 / 迁移](/sso/user-data-sync)
|
||||
- [常见问题总结](/sso/sso-questions)
|
||||
- [Sa-Sso-Pro:单点登录商业版](/sso/sso-pro)
|
||||
|
||||
|
@ -31,6 +31,8 @@
|
||||
- 重构:匿名 client 将不再能解析出所有应用的 ticket。**[不向下兼容]**
|
||||
- 新增:新增 homeRoute 配置项:在 /sso/auth 登录后不指定 redirect 参数的情况下默认跳转的路由。
|
||||
- 优化:优化登录有效期策略,SSO Client 端登录时将延续 SSO Server 端的会话剩余有效期。
|
||||
- 新增:新增 `checkTicketAppendData` 策略函数,用于在校验 ticket 后,给 sso-client 端追加返回信息。
|
||||
- 新增:SSO章节文档新增用户数据同步/迁移方案的建议。
|
||||
- 新增插件/示例:
|
||||
- 新增:新增插件 sa-token-hutool-timed-cache,用于整合 Hutool 缓存插件 TimedCache。 **[重要]**
|
||||
- 新增:新增 SSM 架构整合 Sa-Token 简单示例。 **[重要]**
|
||||
|
234
sa-token-doc/sso/user-data-sync.md
Normal file
234
sa-token-doc/sso/user-data-sync.md
Normal file
@ -0,0 +1,234 @@
|
||||
# 用户数据同步 / 迁移
|
||||
|
||||
本篇文章仅提供架构设计的略微参考,真实场景中每个公司的架构设计都是千差万别的,一套设计理论未必能够适应所有公司的项目。
|
||||
所以如果你觉着本篇文章的设计理念不能契合你公司的需求,请以你公司的原设计为准。
|
||||
|
||||
---
|
||||
|
||||
### 数据同步需求
|
||||
|
||||
在前面的不同架构 SSO 对接示例中,我们均假设了一个前提:
|
||||
|
||||
-- _所有的 sso-client 只负责业务操作,不存储 user 数据,user 数据全部来源于 sso-server,包括登录认证也都是基于 sso-server 里的 user 账号进行校验操作。_
|
||||
|
||||
这种架构比较简洁、清晰,是一种理想化的 SSO 架构模型。
|
||||
|
||||
然而更多时候,我们遇到的实际情况是:
|
||||
|
||||
-- _公司已经有了 N 多个系统,每个系统都有自己独立的一套账号认证体系,现在老板要让这 N 个毫无关系的系统集成单点登录。_
|
||||
|
||||
要完成这种需求,首先你得考虑两个问题:
|
||||
1. 问题一:sso-client 需不需要保留 user 数据。
|
||||
- sso-client 不涉及 user 信息连表查的业务,就可以不保留 user 信息。
|
||||
- sso-client 涉及 user 信息连表查业务,就需要在 sso-client 保留 user 数据。
|
||||
2. 问题二:如果保留的话,是和 sso-server 强同步,还是弱同步。
|
||||
- 强同步就是指 sso-client 的 user 数据和 sso-server 的 user 数据,字段值必须保持一致。比如说:一个用户在server端昵称修改为“张三”,那么在 client 端也要实时同步修改。
|
||||
- 弱同步就是指两边可以各改各的。比如说:一个用户 server 端修改了昵称为 “张三”,他在 client 端依然可以昵称为 “李四”。
|
||||
|
||||
|
||||
由此可大致分为三种设计方案:
|
||||
|
||||
| 方案序号 | 方案名称 | 简单说明 | 适用系统 |
|
||||
| :-------- | :-------- | :-------- | :-------- |
|
||||
| 方案一 | 统一迁移 | 统一把用户数据迁移到 sso-server 认证中心再进行对接 | 比较简单的系统,业务上不需要 user 信息连表查 |
|
||||
| 方案二 | 实时同步 | 按照一定的规则,使 sso-client 和 sso-server 保持 user 信息实时同步 | 一般业务上需要 user 信息连表查的系统 |
|
||||
| 方案三 | 字段关联 | 不同步,但找一个关键字段,将 sso-client 和 sso-server 的 user 账号进行关联起来 | sso-client 不打算过分依赖 sso-server 的 user 数据,只是想借助 sso-server 完成一下统一登录 |
|
||||
|
||||
下面逐一拆解三种方案具体实现。
|
||||
|
||||
|
||||
### 1、方案一:统一迁移
|
||||
|
||||
对接工作开发前,sso-client 的 user 数据完全迁移到 sso-server 中,且自身不再保留 user 数据,只进行业务数据处理操作。
|
||||
|
||||
这种方案其实不必过多讲解,因为数据完成迁移后整个架构就转化为了上述的“理想化SSO模型”,后续对接也比较方便。
|
||||
迁移方式可以选择数据库同步工具,或者手写代码从 sso-client 库读取数据然后 insert 到 sso-server 库中。
|
||||
这并非此文探讨的重点,因此不再过多赘述了。
|
||||
|
||||
- 方案优点:架构简洁明了,SSO 登录、注销对接起来非常方便
|
||||
- 方案缺点:sso-client 不存储 user 信息,因此业务上需要连表查询 user 信息的地方会比较麻烦(例如:拉取帖子列表时需要附加显示用户头像和昵称信息)
|
||||
|
||||
方案适用范围:适合业务比较简单,不涉及 user资料连表查业务 的子系统。
|
||||
|
||||
|
||||
### 2、方案二:实时同步
|
||||
|
||||
首先,对接前,数据还是要迁移的,只不过迁移后 sso-client 的 user 数据不删除掉,依然保留。
|
||||
|
||||
然后在项目运行阶段,每当 sso-server 的 user 数据发生变动时(增删改),逐一向每个 sso-client 推送变化信息。使 sso-client 与 sso-server 的 user 数据保持强同步。
|
||||
|
||||
你可能会有疑问,那 sso-client 的 user 数据发生变动时,要不要向 sso-server 推送信息,我的建议是:尽量不要让 sso-client 的 user 信息主动发生变化。
|
||||
|
||||
举个例子:
|
||||
|
||||
> 公司有电商、论坛、短视频 3 个子系统 + 1 个 sso-server 认证中心,无论用户从哪个子系统点击 “修改我的资料” 按钮时,都应该统一跳转到 sso-server 认证中心进行修改,
|
||||
> 修改完毕后再由 sso-server 将 user 信息推送至 3 个子系统。以此来保证 4 个系统间的 user 信息同步。
|
||||
|
||||
- 方案优点:sso-client 存储了 user 信息,可以比较方便的进行 user 连表查操作。
|
||||
- 方案缺点:sso-server 与 sso-client 的 user 数据同步功能不算简单,开发起来可能要耗费一段不小的工期。
|
||||
|
||||
方案适用范围:一般业务上需要 user 信息连表查的子系统都适合。
|
||||
|
||||
|
||||
### 3、方案三:字段关联
|
||||
|
||||
如果子系统不需要和 sso-server 做到信息强同步,可以使用字段关联法做到账户关联进行登录。
|
||||
|
||||
举个例子:公司有三个子系统,电商、论坛、短视频。同一个用户可以在这三个子系统以及 sso-server 认证中心拥有不同的昵称、头像等信息,互不干扰。
|
||||
|
||||
例如,在 sso-server 认证中心里,张三的数据库信息为:
|
||||
|
||||
| id | username | avatar | password | age | email |
|
||||
| :-------- | :-------- | :-------- | :-------- | :-------- | :-------- |
|
||||
| 10001 | ... | ... | ... | ... | ... |
|
||||
| 10002 | 小明 | cat.jpg | 123456 | 18 | `23397@xx.com` |
|
||||
| 10003 | ... | ... | ... | ... | ... |
|
||||
|
||||
在电商系统里中,张三的数据库信息为:
|
||||
|
||||
| id | name | avatar | money | email |
|
||||
| :-------- | :-------- | :-------- | :-------- | :-------- |
|
||||
| 100334 | ... | ... | ... | ... |
|
||||
| 100335 | 二明 | dog.jpg | 1000 | `23397@xx.com` |
|
||||
| 100336 | ... | ... | ... | ... |
|
||||
|
||||
这里的关键点在于,虽然用户 “张三” 在每个系统里的资料都是不同的,但是程序要想办法将它们识别为同一个用户,
|
||||
要做到这一点,就需要我们准备一个关键字段将信息打通串联起来。例如表中的 “邮箱” 信息可以作为这个“关联字段”。
|
||||
|
||||
(注:此处仅展示使用邮箱作为关联字段的操作,实际上除了邮箱以外,手机号、身份证号等具有唯一性的信息都可以作为关联字段)
|
||||
|
||||
首先,在 sso-server 端,我们需要重写一下 `checkTicketAppendData` 函数,使其在 “校验 ticket 返回 loginId” 时,追加返回 email 字段。
|
||||
|
||||
``` java
|
||||
// 配置SSO相关参数
|
||||
@Autowired
|
||||
private void configSso(SaSsoServerConfig ssoServer) {
|
||||
|
||||
// 其它配置 ...
|
||||
|
||||
// 配置:Ticket校验函数
|
||||
ssoServer.checkTicketAppendData = (loginId, result) -> {
|
||||
System.out.println("-------- 追加返回信息到 sso-client --------");
|
||||
|
||||
// 在校验 ticket 后,给 sso-client 端追加返回信息的函数
|
||||
SysUser user = sysUserMapper.getById(loginId);
|
||||
result.set("email", user.getEmail());
|
||||
// result.set("user", user); // 你也可以将整个user 对象的信息都返回到 sso-client,自由决定
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
在 sso-client 端,重写 ticketResultHandle 函数,根据 sso-server 返回的信息查询本地 user 信息并登录:
|
||||
|
||||
``` java
|
||||
// 配置SSO相关参数
|
||||
@Autowired
|
||||
private void configSso(SaSsoClientConfig ssoClient) {
|
||||
|
||||
// 其它配置 ...
|
||||
|
||||
// 自定义校验 ticket 返回值的处理逻辑 (每次从认证中心获取校验 ticket 的结果后调用)
|
||||
ssoClient.ticketResultHandle = (ctr, back) -> {
|
||||
System.out.println("--------- 自定义 ticket 校验结果处理函数 ---------");
|
||||
System.out.println("此账号在 sso-server 的 userId:" + ctr.loginId);
|
||||
System.out.println("此账号在 sso-server 会话剩余有效期:" + ctr.remainSessionTimeout + " 秒");
|
||||
System.out.println("此账号返回的 email 信息:" + ctr.result.get("email"));
|
||||
|
||||
// 模拟代码:
|
||||
// 根据 email 字段找到此账号在本系统对应的 user 信息
|
||||
String email = (String) ctr.result.get("email");
|
||||
SysUser user = sysUserMapper.getByEmail(email);
|
||||
|
||||
// 如果找不到,说明是首次登录本系统的新用户,需要自动注册一个新账号给他
|
||||
if(user == null) {
|
||||
// 涉及到数据库操作,此处仅做模拟代码
|
||||
// 1、构建 user 信息
|
||||
// 2、插入到数据库
|
||||
// 3、查询出最新刚插入的这条 user 信息
|
||||
user = sysUserMapper.getByEmail(email);
|
||||
}
|
||||
|
||||
// 进行登录
|
||||
StpUtil.login(user.getId(), ctr.remainSessionTimeout);
|
||||
StpUtil.getSession().set("user", user);
|
||||
|
||||
// 一切工作完毕,重定向回 back 页面
|
||||
return SaHolder.getResponse().redirect(back);
|
||||
};
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
至此完毕。
|
||||
|
||||
- 方案优点:
|
||||
- 1、sso-client 不需要和 sso-server 保持信息强同步,实现起来不复杂,架构也比较清晰易维护。
|
||||
- 2、同一个用户的信息,sso-client 可以和 sso-client 保持不同,各自维护各自的,互不干扰。
|
||||
- 方案缺点:好像没啥缺点,除非你觉着上述的第2条优点属于缺点。
|
||||
|
||||
方案适用范围:在 user 信息方面不打算过分依赖 sso-server 的系统,希望自己维护自己的 user 信息,只是想借助 sso-server 完成一下统一登录。
|
||||
|
||||
|
||||
### 4、扩展:没有关联字段
|
||||
|
||||
如果我们的子系统 user 表没有邮箱、手机号等唯一性字段和 sso-server 的 user 表进行关联,该怎么办呢?
|
||||
|
||||
没有字段,那就创造个字段,例如:
|
||||
|
||||
| id | name | avatar | age | center_id |
|
||||
| :-------- | :-------- | :-------- | :-------- | :-------- |
|
||||
| 205421 | ... | ... | ... | ... |
|
||||
| 205422 | 小风筝 | dog.jpg | 21 | 10002 |
|
||||
| 205423 | ... | ... | ... | ... |
|
||||
|
||||
如上表所示,我们可以在子系统的 user 表新增一列 `center_id`,记录这个用户在认证中心所属的账号id。然后在登录时根据这个 `center_id` 来查找相应的用户。
|
||||
|
||||
由于 sso-server 端默认就是会返回 loginId 参数的,因此在 sso-server 端不必再重写一下 `checkTicketAppendData` 函数来追加返回信息了,
|
||||
我们只需要重写 sso-client 端的 `ticketResultHandle` 函数即可:
|
||||
|
||||
``` java
|
||||
// 配置SSO相关参数
|
||||
@Autowired
|
||||
private void configSso(SaSsoClientConfig ssoClient) {
|
||||
|
||||
// 其它配置 ...
|
||||
|
||||
// 自定义校验 ticket 返回值的处理逻辑 (每次从认证中心获取校验 ticket 的结果后调用)
|
||||
ssoClient.ticketResultHandle = (ctr, back) -> {
|
||||
System.out.println("--------- 自定义 ticket 校验结果处理函数 ---------");
|
||||
System.out.println("此账号在 sso-server 的 userId:" + ctr.loginId);
|
||||
System.out.println("此账号在 sso-server 会话剩余有效期:" + ctr.remainSessionTimeout + " 秒");
|
||||
|
||||
// 模拟代码:
|
||||
// 根据 center_id 字段找到此账号在本系统对应的 user 信息
|
||||
long centerId = SaFoxUtil.getValueByType(ctr.loginId, long.class);
|
||||
SysUser user = sysUserMapper.getByCenterId(centerId);
|
||||
|
||||
// 如果找不到,说明是首次登录本系统的新用户,需要自动注册一个新账号给他
|
||||
if(user == null) {
|
||||
// 涉及到数据库操作,此处仅做模拟
|
||||
// 1、构建 user 信息
|
||||
// 2、插入到数据库
|
||||
// 3、查询出最新刚插入的这条 user 信息
|
||||
user = sysUserMapper.getByCenterId(userId);
|
||||
}
|
||||
|
||||
// 进行登录
|
||||
StpUtil.login(user.getId(), ctr.remainSessionTimeout);
|
||||
StpUtil.getSession().set("user", user);
|
||||
|
||||
// 一切工作完毕,重定向回 back 页面
|
||||
return SaHolder.getResponse().redirect(back);
|
||||
};
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
至此完毕。
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user