mirror of
				https://gitee.com/dromara/sa-token.git
				synced 2025-10-25 10:09:01 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			192 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			192 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| # 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 + "_" + unionid + "_" + 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分组)					|
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| 
 | 
