sa-token/sa-token-doc/oauth2/oauth2-custom-scope.md
2024-09-02 17:41:06 +08:00

223 lines
6.2 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.

# OAuth2-自定义 Scope 权限及处理器
---
### 1、需求场景
一般情况下,对于第三方 oauth2-client 来讲,仅仅拿到用户的 access_token 是不够的,还需要拿到更多的信息,比如用户昵称、头像等资料。
sa-token-oauth2 提供两种模式,让 access_token 可以得到更多信息。
- 自定义接口模式:在 oauth2-server 端开放一个资料查询接口,在 oauth2-client 得到 `access_token` 后,再次调用这个接口来获取 `userinfo` 信息。
- 自定义权限处理器模式:自定义一个 `ScopeHandler`,直接在返回 `access_token` 时追加字段,将 `userinfo` 信息和 `access_token` 一并返回到 oauth2-client。
### 2、自定义接口模式
#### 1、新建查询接口
在 oauth2-server 新建接口,查询指定 `access_token` 代表的 `userId``userinfo`
``` java
// 获取 userinfo 信息:昵称、头像、性别等等
@RequestMapping("/oauth2/userinfo")
public SaResult userinfo() {
// 获取 Access-Token 对应的账号id
String accessToken = SaOAuth2Manager.getDataResolver().readAccessToken(SaHolder.getRequest());
Object loginId = SaOAuth2Util.getLoginIdByAccessToken(accessToken);
System.out.println("-------- 此Access-Token对应的账号id: " + loginId);
// 校验 Access-Token 是否具有权限: userinfo
SaOAuth2Util.checkAccessTokenScope(accessToken, "userinfo");
// 模拟账号信息 (真实环境需要查询数据库获取信息)
Map<String, Object> map = new LinkedHashMap<>();
// map.put("userId", loginId); 一般原则下oauth2-server 不能把 userId 返回给 oauth2-client
map.put("nickname", "林小林");
map.put("avatar", "http://xxx.com/1.jpg");
map.put("age", "18");
map.put("sex", "男");
map.put("address", "山东省 青岛市 城阳区");
return SaResult.ok().setMap(map);
}
```
#### 2、申请 code 时指定权限
oauth2-client 申请 `code` 时,一定需要加上 `userinfo` 权限
``` url
http://sa-oauth-server.com:8000/oauth2/authorize
?response_type=code
&client_id=1001
&redirect_uri=http://sa-oauth-client.com:8002/
&scope=userinfo
```
#### 3、code 换 access_token
访问上述链接后,得到 `code` 授权码,然后我们拿着 `code``access_token`
``` url
http://sa-oauth-server.com:8000/oauth2/token
?grant_type=authorization_code
&client_id=1001
&client_secret=aaaa-bbbb-cccc-dddd-eeee
&code=${code}
```
#### 4、access_token 取 userinfo
使用返回的 `access_token` 再次访问接口 `/oauth2/userinfo`
``` url
http://sa-oauth-server.com:8000/oauth2/userinfo?access_token=${access_token}
```
返回以下结果:
``` js
{
"code": 200,
"msg": "ok",
"data": null,
"nickname": "林小林",
"avatar": "http://xxx.com/1.jpg",
"age": "18",
"sex": "男",
"address": "山东省 青岛市 城阳区"
}
```
拿到 userinfo。
### 3、自定义权限处理器模式
#### 1、新建权限处理器
在 oauth2-server 新建 `UserinfoScopeHandler.java` 实现 `SaOAuth2ScopeHandlerInterface` 接口:
``` java
@Component
public class UserinfoScopeHandler implements SaOAuth2ScopeHandlerInterface {
@Override
public String getHandlerScope() {
return "userinfo";
}
@Override
public void workAccessToken(AccessTokenModel at) {
System.out.println("--------- userinfo 权限,加工 AccessTokenModel --------- ");
// 模拟账号信息 (真实环境需要查询数据库获取信息)
Map<String, Object> map = new LinkedHashMap<String, Object>();
map.put("userId", "10008");
map.put("nickname", "shengzhang_");
map.put("avatar", "http://xxx.com/1.jpg");
map.put("age", "18");
map.put("sex", "男");
map.put("address", "山东省 青岛市 城阳区");
at.extraData.putAll(map);
}
@Override
public void workClientToken(ClientTokenModel ct) {
}
}
```
如上所述,所有写入到 `extraData` 中的数据,都将追加返回到 oauth2-client 端。
#### 2、申请 code 时指定权限
oauth2-client 申请 `code` 时,一定需要加上 `userinfo` 权限
``` url
http://sa-oauth-server.com:8000/oauth2/authorize
?response_type=code
&client_id=1001
&redirect_uri=http://sa-oauth-client.com:8002/
&scope=userinfo
```
#### 3、code 换 access_token
访问上述链接后,得到 `code` 授权码,然后我们拿着 `code` 换 `access_token`
``` url
http://sa-oauth-server.com:8000/oauth2/token
?grant_type=authorization_code
&client_id=1001
&client_secret=aaaa-bbbb-cccc-dddd-eeee
&code=${code}
```
返回结果如下
``` js
{
"code": 200,
"msg": "ok",
"data": null,
"token_type": "bearer",
"access_token": "LQ24xI0hX25vIzvciHPA0PNsnGCweSFM1Bzl8783li07VAXpw8sEfn9xsta2",
"refresh_token": "rKB8mby1Mw8yZXHbWzliHx6lmatcLcULLw5C5cUMBhMMRx72DFg5u0owZgrA",
"expires_in": 7199,
"refresh_expires_in": 2591999,
"client_id": "1001",
"scope": "openid,userid,userinfo",
"userinfo": {
"userId": "10008",
"nickname": "shengzhang_",
"avatar": "http://xxx.com/1.jpg",
"age": "18",
"sex": "男",
"address": "山东省 青岛市 城阳区"
}
}
```
拿到 userinfo。
#### 总结
相比于自定义接口模式,自定义权限处理器模式可以少一次网络请求,让 oauth2-client 端提前拿到 `userinfo` 信息。
### 4、最终权限处理器
当一个自定义权限处理器,监听的 scope 字符串为 `_FINALLY_WORK_SCOPE` 时,则代表这个权限处理器为“最终权限处理器”。
最终权限处理器会永远在所有权限处理器工作完成之后执行一次,即使 oauth2-client 端没有申请任何 scope最终权限处理器也会固定执行。
示例:
``` java
/**
* 最终权限处理器:在所有权限处理器工作完成之后,执行此权限处理器
*/
@Component
public class FinallyWorkScopeHandler implements SaOAuth2ScopeHandlerInterface {
@Override
public String getHandlerScope() {
return SaOAuth2Consts._FINALLY_WORK_SCOPE;
}
@Override
public void workAccessToken(AccessTokenModel at) {
// 在所有权限处理器工作完成之后,执行此处方法加工 AccessToken
// System.out.println(123);
}
@Override
public void workClientToken(ClientTokenModel ct) {
// System.out.println(456);
}
}
```