sa-token/sa-token-doc/oauth2/oauth2-openid.md
2025-01-31 06:00:43 +08:00

192 lines
6.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# OpenId 与 UnionId
<p><a class="case-btn case-btn-video" href="https://www.bilibili.com/video/BV1oz6AY5ERJ/" target="_blank">
参考视频OAuth2 授权流程中的 clientId、openId、unionId、userId 都是干嘛的?
</a></p>
### 1、OpenId
openid 是用户在某一 client 下的唯一标识,其有如下特点:
- 一个用户在同一个 client 下openid 是固定的,每次请求都会返回相同的值。
- 一个用户在不同的 client 下openid 是不同的,会返回不同的值。
oauth2-client 在每次授权时可根据返回的 openid 值来确定用户身份。
框架默认的 openid 生成算法为:
``` java
md5(prefix + "_" + clientId + "_" + loginId);
```
其中的 prefix 前缀默认值为:`openid_default_digest_prefix`,你可以通过以下方式配置:
<!---------------------------- tabs:start ---------------------------->
<!------------- tab:yaml 风格 ------------->
``` yaml
# sa-token配置
sa-token:
oauth2-server:
# 默认 openid 生成算法中使用的摘要前缀
openid-digest-prefix: xxxxxx
```
<!------------- tab:properties 风格 ------------->
``` properties
# 默认 openid 生成算法中使用的摘要前缀
sa-token.oauth2-server.openid-digest-prefix=xxxxxx
```
<!---------------------------- tabs:end ---------------------------->
你也可以通过实现 `SaOAuth2DataLoader` 接口完全自定义 OpenId 生成算法:
``` java
/**
* Sa-Token OAuth2自定义数据加载器
*/
@Component
public class SaOAuth2DataLoaderImpl implements SaOAuth2DataLoader {
// 自定义 openid 生成算法
@Override
public String getOpenid(String clientId, Object loginId) {
// 此种写法代表使用框架默认算法生成 openid真实环境建议改为从数据库查询
return SaOAuth2DataLoader.super.getOpenid(clientId, loginId);
}
}
```
#### openid 算法要求
正常来讲openid 算法需要保证:
1. 单个 clientId 下同一 loginId 生成的 `openid` 一致。[必须]
2. 多个 clientId 下同一 loginId 生成的 `openid` 不一致。[非常建议]
3. 客户端无法通过 clientId + loginId 推测 `openid` 值。[建议]
4. 客户端无法通过 clientId + loginId + openid 推测该 loginId 在其它 clientId 下的 `openid` 值。[建议]
5. oauth2-server 自身由 `openid` 可以反查出对应的 clientId 和 loginId。[根据业务需求而定是否满足]
框架内置的算法,可以满足 1和2如果自定义了 `sa-token.oauth2-server.openid-digest-prefix` 配置可以满足3。
如果自定义配置的 prefix 长度较短,或比较简单呈现规律性,则有客户端根据 clientId + loginId + openid 穷举爆破出 `prefix` 的风险,
从而获得提前计算彩虹表来推测出其它 clientId、loginId 对应 openid 值的能力。
如果自定义的 prefix 前缀比较复杂让客户端无法爆破则可以满足4。但依然无法满足5。
所以 openid 算法的最优解,应该是 oauth2-server 采用随机字符串作为 openid然后自建数据库表来维护其映射关系这样可以同时满足12345。
表结构参考如下:
- id数据id主键。
- client_id应用id。
- user_id用户账号id。
- openid对应的 openid 值,随机字符串。
- create_time数据创建时间。
- xxx其它需要扩展的字段。
### 2、UnionId
`UnionId` 的特点与 `OpenId` 几乎一致:同一用户在不同 client 里的 UnionId 值是不同的,除非这些应用属于同一主体。
例如:甲公司申请了`应用A`、`应用B`、`应用C`,乙公司申请了`应用D`、`应用F`,那么用户张三:
- 在应用 A、B、C 里的 UnionId 值一致。
- 在应用 D、F 里的 UnionId 值一致。
- 在应用 A 和 应用 D 之间UnionId 值不一致。
那么 Sa-Token 框架是如何识别到某两个应用是否为同一主体的呢?这就需要你在注册应用时指定 `subjectId` 属性了:
``` java
/**
* Sa-Token OAuth2自定义数据加载器
*/
@Component
public class SaOAuth2DataLoaderImpl implements SaOAuth2DataLoader {
// 根据 clientId 获取 Client 信息
@Override
public SaClientModel getClientModel(String clientId) {
// 此为模拟数据,真实环境需要从数据库查询
if("1001".equals(clientId)) {
return new SaClientModel()
.setClientId("xxxx")
.setClientSecret("xxxx")
.setSubjectId("1000001") // 关键代码:主体 id (可选)
// ....
;
}
return null;
}
}
```
`subjectId` 代表此应用的拥有者,相同 `subjectId` 值的应用将被识别为同一主体,在授权中返回的 `unionid` 值也将一致。
框架默认的 `unionid` 生成算法为:
``` java
md5(prefix + "_" + clientId + "_" + loginId);
```
其中的 prefix 前缀默认值为:`unionid_default_digest_prefix`,你可以通过以下方式配置:
<!---------------------------- tabs:start ---------------------------->
<!------------- tab:yaml 风格 ------------->
``` yaml
# sa-token配置
sa-token:
oauth2-server:
# 默认 unionid 生成算法中使用的摘要前缀
unionid-digest-prefix: xxxxxx
```
<!------------- tab:properties 风格 ------------->
``` properties
# 默认 unionid 生成算法中使用的摘要前缀
sa-token.oauth2-server.unionid-digest-prefix=xxxxxx
```
<!---------------------------- tabs:end ---------------------------->
你也可以通过实现 `SaOAuth2DataLoader` 接口完全自定义 UnionId 生成算法:
``` java
/**
* Sa-Token OAuth2自定义数据加载器
*/
@Component
public class SaOAuth2DataLoaderImpl implements SaOAuth2DataLoader {
// 自定义 unionid 生成算法
@Override
public String getUnionid(String subjectId, Object loginId) {
// 此种写法代表使用框架默认算法生成 unionid真实环境建议改为从数据库查询
return SaOAuth2DataLoader.super.getUnionid(subjectId, loginId);
}
}
```
unionid 算法要求与 openid 基本一致,可参考上述 openid 算法要求介绍,此处暂不赘述。
### 3、总结
| 类型 | 概念 |
| :-------- | :-------- |
| userid | 在 oauth2-server 端的用户,其唯一标识 |
| clientid | 第三方公司在 oauth2-server 开放平台申请的应用,其唯一标识 |
| openid | 用户在某个应用下的唯一标识 |
| unionid | 用户在某一组应用下的唯一标识 按照主体id分组 |