mirror of
https://gitee.com/dromara/sa-token.git
synced 2025-06-28 13:34:18 +08:00
sa-token-oauth2 新增自定义 grant_type 能力
This commit is contained in:
parent
4aa4941598
commit
3345e3aaf9
@ -75,6 +75,17 @@ public class SaFoxUtil {
|
|||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成指定区间的 int 值
|
||||||
|
*
|
||||||
|
* @param min 最小值(包括)
|
||||||
|
* @param max 最大值(包括)
|
||||||
|
* @return /
|
||||||
|
*/
|
||||||
|
public static int getRandomNumber(int min, int max) {
|
||||||
|
return ThreadLocalRandom.current().nextInt(min, max + 1);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 指定元素是否为null或者空字符串
|
* 指定元素是否为null或者空字符串
|
||||||
* @param str 指定元素
|
* @param str 指定元素
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.pj.oauth2;
|
package com.pj.oauth2;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.oauth2.consts.GrantType;
|
||||||
import cn.dev33.satoken.oauth2.data.loader.SaOAuth2DataLoader;
|
import cn.dev33.satoken.oauth2.data.loader.SaOAuth2DataLoader;
|
||||||
import cn.dev33.satoken.oauth2.data.model.loader.SaClientModel;
|
import cn.dev33.satoken.oauth2.data.model.loader.SaClientModel;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
@ -22,10 +23,14 @@ public class SaOAuth2DataLoaderImpl implements SaOAuth2DataLoader {
|
|||||||
.setClientSecret("aaaa-bbbb-cccc-dddd-eeee") // client 秘钥
|
.setClientSecret("aaaa-bbbb-cccc-dddd-eeee") // client 秘钥
|
||||||
.addAllowUrls("*") // 所有允许授权的 url
|
.addAllowUrls("*") // 所有允许授权的 url
|
||||||
.addContractScopes("openid", "userid", "userinfo") // 所有签约的权限
|
.addContractScopes("openid", "userid", "userinfo") // 所有签约的权限
|
||||||
.setEnableCode(true) // 是否开启授权码模式
|
.addAllowGrantTypes( // 所有允许的授权模式
|
||||||
.setEnableImplicit(true) // 是否开启隐式模式
|
GrantType.authorization_code, // 授权码式
|
||||||
.setEnablePassword(true) // 是否开启密码模式
|
GrantType.implicit, // 隐式式
|
||||||
.setEnableClient(true) // 是否开启客户端模式
|
GrantType.refresh_token, // 刷新令牌
|
||||||
|
GrantType.password, // 密码式
|
||||||
|
GrantType.client_credentials, // 客户端模式
|
||||||
|
"phone_code" // 自定义授权模式 手机号验证码登录
|
||||||
|
)
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
package com.pj.oauth2.custom;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.SaManager;
|
||||||
|
import cn.dev33.satoken.context.model.SaRequest;
|
||||||
|
import cn.dev33.satoken.oauth2.SaOAuth2Manager;
|
||||||
|
import cn.dev33.satoken.oauth2.data.model.AccessTokenModel;
|
||||||
|
import cn.dev33.satoken.oauth2.data.model.request.RequestAuthModel;
|
||||||
|
import cn.dev33.satoken.oauth2.exception.SaOAuth2Exception;
|
||||||
|
import cn.dev33.satoken.oauth2.granttype.handler.SaOAuth2GrantTypeHandlerInterface;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义 phone_code 授权模式处理器
|
||||||
|
*
|
||||||
|
* @author click33
|
||||||
|
* @since 2024/8/23
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class PhoneCodeGrantTypeHandler implements SaOAuth2GrantTypeHandlerInterface {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHandlerGrantType() {
|
||||||
|
return "phone_code";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AccessTokenModel getAccessTokenModel(SaRequest req, String clientId, List<String> scopes) {
|
||||||
|
|
||||||
|
String phone = req.getParamNotNull("phone");
|
||||||
|
String code = req.getParamNotNull("code");
|
||||||
|
String realCode = SaManager.getSaTokenDao().get("phone_code:" + phone);
|
||||||
|
|
||||||
|
// 1、校验验证码是否正确
|
||||||
|
if(!code.equals(realCode)) {
|
||||||
|
throw new SaOAuth2Exception("验证码错误");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2、校验通过,删除验证码
|
||||||
|
SaManager.getSaTokenDao().delete("phone_code:" + phone);
|
||||||
|
|
||||||
|
// 3、登录
|
||||||
|
long userId = 10001; // 模拟 userId,真实项目应该根据手机号从数据库查询
|
||||||
|
|
||||||
|
// 4、构建 ra 对象
|
||||||
|
RequestAuthModel ra = new RequestAuthModel();
|
||||||
|
ra.clientId = clientId;
|
||||||
|
ra.loginId = userId;
|
||||||
|
ra.scopes = scopes;
|
||||||
|
|
||||||
|
// 5、生成 Access-Token
|
||||||
|
AccessTokenModel at = SaOAuth2Manager.getDataGenerate().generateAccessToken(ra, true);
|
||||||
|
return at;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package com.pj.oauth2.custom;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.SaManager;
|
||||||
|
import cn.dev33.satoken.util.SaFoxUtil;
|
||||||
|
import cn.dev33.satoken.util.SaResult;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义手机登录接口
|
||||||
|
*
|
||||||
|
* @author click33
|
||||||
|
* @since 2024/8/23
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
public class PhoneLoginController {
|
||||||
|
|
||||||
|
@RequestMapping("/oauth2/sendPhoneCode")
|
||||||
|
public SaResult sendCode(String phone) {
|
||||||
|
String code = SaFoxUtil.getRandomNumber(100000, 999999) + "";
|
||||||
|
SaManager.getSaTokenDao().set("phone_code:" + phone, code, 60 * 5);
|
||||||
|
System.out.println("手机号:" + phone + ",验证码:" + code + ",已发送成功");
|
||||||
|
return SaResult.ok("验证码发送成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package com.pj.oauth2;
|
package com.pj.oauth2.custom;
|
||||||
|
|
||||||
import cn.dev33.satoken.oauth2.data.model.AccessTokenModel;
|
import cn.dev33.satoken.oauth2.data.model.AccessTokenModel;
|
||||||
import cn.dev33.satoken.oauth2.data.model.ClientTokenModel;
|
import cn.dev33.satoken.oauth2.data.model.ClientTokenModel;
|
@ -5,6 +5,8 @@ server:
|
|||||||
sa-token:
|
sa-token:
|
||||||
# token名称 (同时也是 Cookie 名称)
|
# token名称 (同时也是 Cookie 名称)
|
||||||
token-name: satoken-oauth2-server
|
token-name: satoken-oauth2-server
|
||||||
|
# 是否打印操作日志
|
||||||
|
is-log: true
|
||||||
# OAuth2.0 配置
|
# OAuth2.0 配置
|
||||||
oauth2:
|
oauth2:
|
||||||
# 是否全局开启授权码模式
|
# 是否全局开启授权码模式
|
||||||
|
@ -63,7 +63,7 @@
|
|||||||
- [自定义 grant_type](/oauth2/oauth2-custom-grant_type)
|
- [自定义 grant_type](/oauth2/oauth2-custom-grant_type)
|
||||||
- [OAuth2-与登录会话实现数据互通](/oauth2/oauth2-interworking)
|
- [OAuth2-与登录会话实现数据互通](/oauth2/oauth2-interworking)
|
||||||
- [OAuth2 代码 API 参考](/oauth2/oauth2-dev)
|
- [OAuth2 代码 API 参考](/oauth2/oauth2-dev)
|
||||||
- [常见问题说明](/oauth2/8)
|
<!-- - [常见问题说明](/oauth2/8) -->
|
||||||
<!-- - [前后端分离模式整合方案](/oauth2/4) -->
|
<!-- - [前后端分离模式整合方案](/oauth2/4) -->
|
||||||
<!-- - [平台中心模式开发](/oauth2/5) -->
|
<!-- - [平台中心模式开发](/oauth2/5) -->
|
||||||
- <!-- jwt风格token、使用注解校验权限、state参数详解 -->
|
- <!-- jwt风格token、使用注解校验权限、state参数详解 -->
|
||||||
|
174
sa-token-doc/oauth2/oauth2-custom-grant_type.md
Normal file
174
sa-token-doc/oauth2/oauth2-custom-grant_type.md
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
# OAuth2-自定义权限处理器
|
||||||
|
|
||||||
|
|
||||||
|
### 1、需求场景
|
||||||
|
|
||||||
|
OAuth2 协议的 `/oauth2/token` 接口定义了两种获取 `access_token` 的 `grant_type`,分别是:
|
||||||
|
- `authorization_code`:使用用户授权的授权码获取 access_token。
|
||||||
|
- `password`:使用用户提交的账号、密码来获取 access_token。
|
||||||
|
|
||||||
|
除此之外,你还可以自定义 `grant_type`,来支持更多的场景。
|
||||||
|
|
||||||
|
假设有以下需求:通过 手机号+验证码 登录,返回 `access_token`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
### 2、实现步骤
|
||||||
|
|
||||||
|
#### 2.1、新增验证码发送接口
|
||||||
|
|
||||||
|
首先在 oauth2-server 端开放一个接口,为指定手机号发送验证码。
|
||||||
|
|
||||||
|
``` java
|
||||||
|
/**
|
||||||
|
* 自定义手机登录接口
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
public class PhoneLoginController {
|
||||||
|
|
||||||
|
@RequestMapping("/oauth2/sendPhoneCode")
|
||||||
|
public SaResult sendCode(String phone) {
|
||||||
|
String code = SaFoxUtil.getRandomNumber(100000, 999999) + "";
|
||||||
|
SaManager.getSaTokenDao().set("phone_code:" + phone, code, 60 * 5);
|
||||||
|
System.out.println("手机号:" + phone + ",验证码:" + code + ",已发送成功");
|
||||||
|
return SaResult.ok("验证码发送成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
真实项目肯定是要对接短信服务商的,此处我们仅做模拟代码,将发送的验证码打印在控制台上。
|
||||||
|
|
||||||
|
|
||||||
|
#### 2.2、自定义 grant_type 处理器
|
||||||
|
|
||||||
|
在 oauth2-server 新建 `PhoneCodeGrantTypeHandler` 实现 `SaOAuth2GrantTypeHandlerInterface` 接口:
|
||||||
|
|
||||||
|
``` java
|
||||||
|
/**
|
||||||
|
* 自定义 phone_code 授权模式处理器
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class PhoneCodeGrantTypeHandler implements SaOAuth2GrantTypeHandlerInterface {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHandlerGrantType() {
|
||||||
|
return "phone_code";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AccessTokenModel getAccessTokenModel(SaRequest req, String clientId, List<String> scopes) {
|
||||||
|
|
||||||
|
String phone = req.getParamNotNull("phone");
|
||||||
|
String code = req.getParamNotNull("code");
|
||||||
|
String realCode = SaManager.getSaTokenDao().get("phone_code:" + phone);
|
||||||
|
|
||||||
|
// 1、校验验证码是否正确
|
||||||
|
if(!code.equals(realCode)) {
|
||||||
|
throw new SaOAuth2Exception("验证码错误");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2、校验通过,删除验证码
|
||||||
|
SaManager.getSaTokenDao().delete("phone_code:" + phone);
|
||||||
|
|
||||||
|
// 3、登录
|
||||||
|
long userId = 10001; // 模拟 userId,真实项目应该根据手机号从数据库查询
|
||||||
|
|
||||||
|
// 4、构建 ra 对象
|
||||||
|
RequestAuthModel ra = new RequestAuthModel();
|
||||||
|
ra.clientId = clientId;
|
||||||
|
ra.loginId = userId;
|
||||||
|
ra.scopes = scopes;
|
||||||
|
|
||||||
|
// 5、生成 Access-Token
|
||||||
|
AccessTokenModel at = SaOAuth2Manager.getDataGenerate().generateAccessToken(ra, true);
|
||||||
|
return at;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2.3、为应用添加允许的授权类型
|
||||||
|
|
||||||
|
在 `SaOAuth2DataLoader` 实现类中,为 client 的允许授权类型增加自定义的 `phone_code`
|
||||||
|
|
||||||
|
``` java
|
||||||
|
// Sa-Token OAuth2:自定义数据加载器
|
||||||
|
@Component
|
||||||
|
public class SaOAuth2DataLoaderImpl implements SaOAuth2DataLoader {
|
||||||
|
@Override
|
||||||
|
public SaClientModel getClientModel(String clientId) {
|
||||||
|
if("1001".equals(clientId)) {
|
||||||
|
return new SaClientModel()
|
||||||
|
.setClientId("1001")
|
||||||
|
.setClientSecret("aaaa-bbbb-cccc-dddd-eeee")
|
||||||
|
.addAllowUrls("*")
|
||||||
|
.addContractScopes("openid", "userid", "userinfo")
|
||||||
|
.addAllowGrantTypes(
|
||||||
|
GrantType.authorization_code,
|
||||||
|
GrantType.implicit,
|
||||||
|
GrantType.refresh_token,
|
||||||
|
GrantType.password,
|
||||||
|
GrantType.client_credentials,
|
||||||
|
"phone_code" // 重要代码:自定义授权模式 手机号验证码登录
|
||||||
|
)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// 其它代码 ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
完工,开始测试。
|
||||||
|
|
||||||
|
|
||||||
|
### 3、测试步骤
|
||||||
|
|
||||||
|
#### 1、先发送验证码
|
||||||
|
|
||||||
|
``` url
|
||||||
|
http://sa-oauth-server.com:8000/oauth2/sendPhoneCode?phone=13144556677
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2、请求 token
|
||||||
|
|
||||||
|
注意 `grant_type` 要填写我们自定义的 `phone_code`,code 的具体值可以在后端的控制台上看到
|
||||||
|
|
||||||
|
``` url
|
||||||
|
http://sa-oauth-server.com:8000/oauth2/token
|
||||||
|
?grant_type=phone_code
|
||||||
|
&client_id=1001
|
||||||
|
&client_secret=aaaa-bbbb-cccc-dddd-eeee
|
||||||
|
&scope=openid
|
||||||
|
&phone=13144556677
|
||||||
|
&code={value}
|
||||||
|
```
|
||||||
|
|
||||||
|
返回结果参考如下:
|
||||||
|
|
||||||
|
``` js
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "ok",
|
||||||
|
"data": null,
|
||||||
|
"token_type": "bearer",
|
||||||
|
"access_token": "pfxRz6KVacwvKNu4IHmDsCJs33kvvARs2z1lTch7stog8nRt6rfVLowtAZ0E",
|
||||||
|
"refresh_token": "qcFD6Wo2qZidofXQtWF5oK5ML6ljHKufQ5SbouBxzGnHhnMjUG4VV0iXZhdE",
|
||||||
|
"expires_in": 7199,
|
||||||
|
"refresh_expires_in": 2591999,
|
||||||
|
"client_id": "1001",
|
||||||
|
"scope": "openid",
|
||||||
|
"openid": "ded91dc189a437dd1bac2274be167d50"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -26,6 +26,9 @@ import cn.dev33.satoken.oauth2.data.loader.SaOAuth2DataLoader;
|
|||||||
import cn.dev33.satoken.oauth2.data.loader.SaOAuth2DataLoaderDefaultImpl;
|
import cn.dev33.satoken.oauth2.data.loader.SaOAuth2DataLoaderDefaultImpl;
|
||||||
import cn.dev33.satoken.oauth2.data.resolver.SaOAuth2DataResolver;
|
import cn.dev33.satoken.oauth2.data.resolver.SaOAuth2DataResolver;
|
||||||
import cn.dev33.satoken.oauth2.data.resolver.SaOAuth2DataResolverDefaultImpl;
|
import cn.dev33.satoken.oauth2.data.resolver.SaOAuth2DataResolverDefaultImpl;
|
||||||
|
import cn.dev33.satoken.oauth2.template.SaOAuth2Template;
|
||||||
|
import cn.dev33.satoken.stp.StpLogic;
|
||||||
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sa-Token-OAuth2 模块 总控类
|
* Sa-Token-OAuth2 模块 总控类
|
||||||
@ -144,4 +147,40 @@ public class SaOAuth2Manager {
|
|||||||
SaOAuth2Manager.dao = dao;
|
SaOAuth2Manager.dao = dao;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OAuth2 模板方法 Bean
|
||||||
|
*/
|
||||||
|
private static volatile SaOAuth2Template template;
|
||||||
|
public static SaOAuth2Template getTemplate() {
|
||||||
|
if (template == null) {
|
||||||
|
synchronized (SaOAuth2Manager.class) {
|
||||||
|
if (template == null) {
|
||||||
|
setTemplate(new SaOAuth2Template());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
public static void setTemplate(SaOAuth2Template template) {
|
||||||
|
SaOAuth2Manager.template = template;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OAuth2 StpLogic
|
||||||
|
*/
|
||||||
|
private static volatile StpLogic stpLogic;
|
||||||
|
public static StpLogic getStpLogic() {
|
||||||
|
if (stpLogic == null) {
|
||||||
|
synchronized (SaOAuth2Manager.class) {
|
||||||
|
if (stpLogic == null) {
|
||||||
|
setStpLogic(StpUtil.stpLogic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stpLogic;
|
||||||
|
}
|
||||||
|
public static void setStpLogic(StpLogic stpLogic) {
|
||||||
|
SaOAuth2Manager.stpLogic = stpLogic;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
package cn.dev33.satoken.oauth2.consts;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 所有授权类型
|
||||||
|
*/
|
||||||
|
public final class GrantType {
|
||||||
|
public static String authorization_code = "authorization_code";
|
||||||
|
public static String refresh_token = "refresh_token";
|
||||||
|
public static String password = "password";
|
||||||
|
public static String client_credentials = "client_credentials";
|
||||||
|
public static String implicit = "implicit";
|
||||||
|
}
|
@ -68,17 +68,6 @@ public class SaOAuth2Consts {
|
|||||||
public static String token = "token";
|
public static String token = "token";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 所有授权类型
|
|
||||||
*/
|
|
||||||
public static final class GrantType {
|
|
||||||
public static String authorization_code = "authorization_code";
|
|
||||||
public static String refresh_token = "refresh_token";
|
|
||||||
public static String password = "password";
|
|
||||||
public static String client_credentials = "client_credentials";
|
|
||||||
public static String implicit = "implicit";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 所有 token 类型
|
* 所有 token 类型
|
||||||
*/
|
*/
|
||||||
|
@ -35,21 +35,21 @@ public interface SaOAuth2DataGenerate {
|
|||||||
* @param ra 请求参数Model
|
* @param ra 请求参数Model
|
||||||
* @return 授权码Model
|
* @return 授权码Model
|
||||||
*/
|
*/
|
||||||
public CodeModel generateCode(RequestAuthModel ra);
|
CodeModel generateCode(RequestAuthModel ra);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建Model:Access-Token
|
* 构建Model:Access-Token
|
||||||
* @param code 授权码Model
|
* @param code 授权码Model
|
||||||
* @return AccessToken Model
|
* @return AccessToken Model
|
||||||
*/
|
*/
|
||||||
public AccessTokenModel generateAccessToken(String code);
|
AccessTokenModel generateAccessToken(String code);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 刷新Model:根据 Refresh-Token 生成一个新的 Access-Token
|
* 刷新Model:根据 Refresh-Token 生成一个新的 Access-Token
|
||||||
* @param refreshToken Refresh-Token值
|
* @param refreshToken Refresh-Token值
|
||||||
* @return 新的 Access-Token
|
* @return 新的 Access-Token
|
||||||
*/
|
*/
|
||||||
public AccessTokenModel refreshAccessToken(String refreshToken);
|
AccessTokenModel refreshAccessToken(String refreshToken);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建Model:Access-Token (根据RequestAuthModel构建,用于隐藏式 and 密码式)
|
* 构建Model:Access-Token (根据RequestAuthModel构建,用于隐藏式 and 密码式)
|
||||||
@ -57,7 +57,7 @@ public interface SaOAuth2DataGenerate {
|
|||||||
* @param isCreateRt 是否生成对应的Refresh-Token
|
* @param isCreateRt 是否生成对应的Refresh-Token
|
||||||
* @return Access-Token Model
|
* @return Access-Token Model
|
||||||
*/
|
*/
|
||||||
public AccessTokenModel generateAccessToken(RequestAuthModel ra, boolean isCreateRt);
|
AccessTokenModel generateAccessToken(RequestAuthModel ra, boolean isCreateRt);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建Model:Client-Token
|
* 构建Model:Client-Token
|
||||||
@ -65,7 +65,7 @@ public interface SaOAuth2DataGenerate {
|
|||||||
* @param scopes 授权范围
|
* @param scopes 授权范围
|
||||||
* @return Client-Token Model
|
* @return Client-Token Model
|
||||||
*/
|
*/
|
||||||
public ClientTokenModel generateClientToken(String clientId, List<String> scopes);
|
ClientTokenModel generateClientToken(String clientId, List<String> scopes);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建URL:下放Code URL (Authorization Code 授权码)
|
* 构建URL:下放Code URL (Authorization Code 授权码)
|
||||||
@ -74,7 +74,7 @@ public interface SaOAuth2DataGenerate {
|
|||||||
* @param state state参数
|
* @param state state参数
|
||||||
* @return 构建完毕的URL
|
* @return 构建完毕的URL
|
||||||
*/
|
*/
|
||||||
public String buildRedirectUri(String redirectUri, String code, String state);
|
String buildRedirectUri(String redirectUri, String code, String state);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建URL:下放Access-Token URL (implicit 隐藏式)
|
* 构建URL:下放Access-Token URL (implicit 隐藏式)
|
||||||
@ -83,14 +83,12 @@ public interface SaOAuth2DataGenerate {
|
|||||||
* @param state state参数
|
* @param state state参数
|
||||||
* @return 构建完毕的URL
|
* @return 构建完毕的URL
|
||||||
*/
|
*/
|
||||||
public String buildImplicitRedirectUri(String redirectUri, String token, String state);
|
String buildImplicitRedirectUri(String redirectUri, String token, String state);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 回收 Access-Token
|
* 回收 Access-Token
|
||||||
* @param accessToken Access-Token值
|
* @param accessToken Access-Token值
|
||||||
*/
|
*/
|
||||||
public void revokeAccessToken(String accessToken);
|
void revokeAccessToken(String accessToken);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -53,24 +53,10 @@ public class SaClientModel implements Serializable {
|
|||||||
*/
|
*/
|
||||||
public List<String> allowUrls;
|
public List<String> allowUrls;
|
||||||
|
|
||||||
/** 此 Client 是否打开模式:授权码(Authorization Code) */
|
/**
|
||||||
public Boolean enableCode = false;
|
* 应用允许的所有 grant_type
|
||||||
|
*/
|
||||||
/** 此 Client 是否打开模式:隐藏式(Implicit) */
|
public List<String> allowGrantTypes = new ArrayList<>();
|
||||||
public Boolean enableImplicit = false;
|
|
||||||
|
|
||||||
/** 此 Client 是否打开模式:密码式(Password) */
|
|
||||||
public Boolean enablePassword = false;
|
|
||||||
|
|
||||||
/** 此 Client 是否打开模式:凭证式(Client Credentials) */
|
|
||||||
public Boolean enableClient = false;
|
|
||||||
|
|
||||||
// /**
|
|
||||||
// * 是否自动判断此 Client 开放的授权模式
|
|
||||||
// * <br> 此值为true时:四种模式(isCode、isImplicit、isPassword、isClient)是否生效,依靠全局设置
|
|
||||||
// * <br> 此值为false时:四种模式(isCode、isImplicit、isPassword、isClient)是否生效,依靠局部配置+全局配置
|
|
||||||
// */
|
|
||||||
// public Boolean isAutoMode = true;
|
|
||||||
|
|
||||||
/** 单独配置此Client:是否在每次 Refresh-Token 刷新 Access-Token 时,产生一个新的 Refresh-Token [默认取全局配置] */
|
/** 单独配置此Client:是否在每次 Refresh-Token 刷新 Access-Token 时,产生一个新的 Refresh-Token [默认取全局配置] */
|
||||||
public Boolean isNewRefresh;
|
public Boolean isNewRefresh;
|
||||||
@ -169,86 +155,22 @@ public class SaClientModel implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return 此 Client 是否打开模式:授权码(Authorization Code)
|
* @return 应用允许的所有 grant_type
|
||||||
*/
|
*/
|
||||||
public Boolean getEnableCode() {
|
public List<String> getAllowGrantTypes() {
|
||||||
return enableCode;
|
return allowGrantTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param enableCode 此 Client 是否打开模式:授权码(Authorization Code)
|
* 应用允许的所有 grant_type
|
||||||
* @return 对象自身
|
* @param allowGrantTypes /
|
||||||
|
* @return /
|
||||||
*/
|
*/
|
||||||
public SaClientModel setEnableCode(Boolean enableCode) {
|
public SaClientModel setAllowGrantTypes(List<String> allowGrantTypes) {
|
||||||
this.enableCode = enableCode;
|
this.allowGrantTypes = allowGrantTypes;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return 此 Client 是否打开模式:隐藏式(Implicit)
|
|
||||||
*/
|
|
||||||
public Boolean getEnableImplicit() {
|
|
||||||
return enableImplicit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param enableImplicit 此 Client 是否打开模式:隐藏式(Implicit)
|
|
||||||
* @return 对象自身
|
|
||||||
*/
|
|
||||||
public SaClientModel setEnableImplicit(Boolean enableImplicit) {
|
|
||||||
this.enableImplicit = enableImplicit;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return 此 Client 是否打开模式:密码式(Password)
|
|
||||||
*/
|
|
||||||
public Boolean getEnablePassword() {
|
|
||||||
return enablePassword;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param enablePassword 此 Client 是否打开模式:密码式(Password)
|
|
||||||
* @return 对象自身
|
|
||||||
*/
|
|
||||||
public SaClientModel setEnablePassword(Boolean enablePassword) {
|
|
||||||
this.enablePassword = enablePassword;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return 此 Client 是否打开模式:凭证式(Client Credentials)
|
|
||||||
*/
|
|
||||||
public Boolean getEnableClient() {
|
|
||||||
return enableClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param enableClient 此 Client 是否打开模式:凭证式(Client Credentials)
|
|
||||||
* @return 对象自身
|
|
||||||
*/
|
|
||||||
public SaClientModel setEnableClient(Boolean enableClient) {
|
|
||||||
this.enableClient = enableClient;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * @return 是否自动判断此 Client 开放的授权模式
|
|
||||||
// */
|
|
||||||
// public Boolean getIsAutoMode() {
|
|
||||||
// return isAutoMode;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * @param isAutoMode 是否自动判断此 Client 开放的授权模式
|
|
||||||
// * @return 对象自身
|
|
||||||
// */
|
|
||||||
// public SaClientModel setIsAutoMode(Boolean isAutoMode) {
|
|
||||||
// this.isAutoMode = isAutoMode;
|
|
||||||
// return this;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return 此Client:是否在每次 Refresh-Token 刷新 Access-Token 时,产生一个新的 Refresh-Token [默认取全局配置]
|
* @return 此Client:是否在每次 Refresh-Token 刷新 Access-Token 时,产生一个新的 Refresh-Token [默认取全局配置]
|
||||||
*/
|
*/
|
||||||
@ -338,11 +260,7 @@ public class SaClientModel implements Serializable {
|
|||||||
", clientSecret='" + clientSecret + '\'' +
|
", clientSecret='" + clientSecret + '\'' +
|
||||||
", contractScopes=" + contractScopes +
|
", contractScopes=" + contractScopes +
|
||||||
", allowUrls=" + allowUrls +
|
", allowUrls=" + allowUrls +
|
||||||
", isCode=" + enableCode +
|
", allowGrantTypes=" + allowGrantTypes +
|
||||||
", isImplicit=" + enableImplicit +
|
|
||||||
", isPassword=" + enablePassword +
|
|
||||||
", isClient=" + enableClient +
|
|
||||||
// ", isAutoMode=" + isAutoMode +
|
|
||||||
", isNewRefresh=" + isNewRefresh +
|
", isNewRefresh=" + isNewRefresh +
|
||||||
", accessTokenTimeout=" + accessTokenTimeout +
|
", accessTokenTimeout=" + accessTokenTimeout +
|
||||||
", refreshTokenTimeout=" + refreshTokenTimeout +
|
", refreshTokenTimeout=" + refreshTokenTimeout +
|
||||||
@ -367,7 +285,7 @@ public class SaClientModel implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param urls 添加应用签约的所有权限
|
* @param urls 添加应用允许授权的所有URL
|
||||||
* @return 对象自身
|
* @return 对象自身
|
||||||
*/
|
*/
|
||||||
public SaClientModel addAllowUrls(String... urls) {
|
public SaClientModel addAllowUrls(String... urls) {
|
||||||
@ -378,5 +296,17 @@ public class SaClientModel implements Serializable {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param grantTypes 应用允许的所有 grant_type
|
||||||
|
* @return 对象自身
|
||||||
|
*/
|
||||||
|
public SaClientModel addAllowGrantTypes(String... grantTypes) {
|
||||||
|
if(this.allowGrantTypes == null) {
|
||||||
|
this.allowGrantTypes = new ArrayList<>();
|
||||||
|
}
|
||||||
|
this.allowGrantTypes.addAll(Arrays.asList(grantTypes));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020-2099 sa-token.cc
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package cn.dev33.satoken.oauth2.function.strategy;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.context.model.SaRequest;
|
||||||
|
import cn.dev33.satoken.oauth2.data.model.AccessTokenModel;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 函数式接口:GrantType 认证
|
||||||
|
*
|
||||||
|
* <p> 参数:SaRequest、grant_type </p>
|
||||||
|
* <p> 返回:处理结果 </p>
|
||||||
|
*
|
||||||
|
* @author click33
|
||||||
|
* @since 1.39.0
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface SaOAuth2GrantTypeAuthFunction extends Function<SaRequest, AccessTokenModel> {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020-2099 sa-token.cc
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package cn.dev33.satoken.oauth2.granttype.handler;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.context.model.SaRequest;
|
||||||
|
import cn.dev33.satoken.oauth2.SaOAuth2Manager;
|
||||||
|
import cn.dev33.satoken.oauth2.consts.SaOAuth2Consts;
|
||||||
|
import cn.dev33.satoken.oauth2.consts.GrantType;
|
||||||
|
import cn.dev33.satoken.oauth2.data.model.AccessTokenModel;
|
||||||
|
import cn.dev33.satoken.oauth2.data.model.request.ClientIdAndSecretModel;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* authorization_code grant_type 处理器
|
||||||
|
*
|
||||||
|
* @author click33
|
||||||
|
* @since 1.39.0
|
||||||
|
*/
|
||||||
|
public class AuthorizationCodeGrantTypeHandler implements SaOAuth2GrantTypeHandlerInterface {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHandlerGrantType() {
|
||||||
|
return GrantType.authorization_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AccessTokenModel getAccessTokenModel(SaRequest req, String clientId, List<String> scopes) {
|
||||||
|
// 获取参数
|
||||||
|
ClientIdAndSecretModel clientIdAndSecret = SaOAuth2Manager.getDataResolver().readClientIdAndSecret(req);
|
||||||
|
// String clientId = clientIdAndSecret.clientId;
|
||||||
|
String clientSecret = clientIdAndSecret.clientSecret;
|
||||||
|
String code = req.getParamNotNull(SaOAuth2Consts.Param.code);
|
||||||
|
String redirectUri = req.getParam(SaOAuth2Consts.Param.redirect_uri);
|
||||||
|
|
||||||
|
// 校验参数
|
||||||
|
SaOAuth2Manager.getTemplate().checkGainTokenParam(code, clientId, clientSecret, redirectUri);
|
||||||
|
|
||||||
|
// 构建 Access-Token、返回
|
||||||
|
AccessTokenModel accessTokenModel = SaOAuth2Manager.getDataGenerate().generateAccessToken(code);
|
||||||
|
return accessTokenModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020-2099 sa-token.cc
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package cn.dev33.satoken.oauth2.granttype.handler;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.context.model.SaRequest;
|
||||||
|
import cn.dev33.satoken.oauth2.SaOAuth2Manager;
|
||||||
|
import cn.dev33.satoken.oauth2.consts.GrantType;
|
||||||
|
import cn.dev33.satoken.oauth2.consts.SaOAuth2Consts;
|
||||||
|
import cn.dev33.satoken.oauth2.data.model.AccessTokenModel;
|
||||||
|
import cn.dev33.satoken.oauth2.data.model.request.RequestAuthModel;
|
||||||
|
import cn.dev33.satoken.oauth2.exception.SaOAuth2Exception;
|
||||||
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* password grant_type 处理器
|
||||||
|
*
|
||||||
|
* @author click33
|
||||||
|
* @since 1.39.0
|
||||||
|
*/
|
||||||
|
public class PasswordGrantTypeHandler implements SaOAuth2GrantTypeHandlerInterface {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHandlerGrantType() {
|
||||||
|
return GrantType.password;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AccessTokenModel getAccessTokenModel(SaRequest req, String clientId, List<String> scopes) {
|
||||||
|
|
||||||
|
// 1、获取请求参数
|
||||||
|
String username = req.getParamNotNull(SaOAuth2Consts.Param.username);
|
||||||
|
String password = req.getParamNotNull(SaOAuth2Consts.Param.password);
|
||||||
|
|
||||||
|
// 3、调用API 开始登录,如果没能成功登录,则直接退出
|
||||||
|
loginByUsernamePassword(username, password);
|
||||||
|
Object loginId = StpUtil.getLoginIdDefaultNull();
|
||||||
|
if(loginId == null) {
|
||||||
|
throw new SaOAuth2Exception("登录失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4、构建 ra 对象
|
||||||
|
RequestAuthModel ra = new RequestAuthModel();
|
||||||
|
ra.clientId = clientId;
|
||||||
|
ra.loginId = loginId;
|
||||||
|
ra.scopes = scopes;
|
||||||
|
|
||||||
|
// 5、生成 Access-Token
|
||||||
|
AccessTokenModel at = SaOAuth2Manager.getDataGenerate().generateAccessToken(ra, true);
|
||||||
|
return at;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 username、password 进行登录,如果登录失败请直接抛出异常
|
||||||
|
* @param username /
|
||||||
|
* @param password /
|
||||||
|
*/
|
||||||
|
public void loginByUsernamePassword(String username, String password) {
|
||||||
|
SaOAuth2Manager.getConfig().doLoginHandle.apply(username, password);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020-2099 sa-token.cc
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package cn.dev33.satoken.oauth2.granttype.handler;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.context.model.SaRequest;
|
||||||
|
import cn.dev33.satoken.oauth2.SaOAuth2Manager;
|
||||||
|
import cn.dev33.satoken.oauth2.consts.GrantType;
|
||||||
|
import cn.dev33.satoken.oauth2.consts.SaOAuth2Consts;
|
||||||
|
import cn.dev33.satoken.oauth2.data.model.AccessTokenModel;
|
||||||
|
import cn.dev33.satoken.oauth2.data.model.RefreshTokenModel;
|
||||||
|
import cn.dev33.satoken.oauth2.error.SaOAuth2ErrorCode;
|
||||||
|
import cn.dev33.satoken.oauth2.exception.SaOAuth2Exception;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* refresh_token grant_type 处理器
|
||||||
|
*
|
||||||
|
* @author click33
|
||||||
|
* @since 1.39.0
|
||||||
|
*/
|
||||||
|
public class RefreshTokenGrantTypeHandler implements SaOAuth2GrantTypeHandlerInterface {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHandlerGrantType() {
|
||||||
|
return GrantType.refresh_token;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AccessTokenModel getAccessTokenModel(SaRequest req, String clientId, List<String> scopes) {
|
||||||
|
// 获取参数
|
||||||
|
String refreshToken = req.getParamNotNull(SaOAuth2Consts.Param.refresh_token);
|
||||||
|
|
||||||
|
// 校验:Refresh-Token 是否存在
|
||||||
|
RefreshTokenModel rt = SaOAuth2Manager.getDao().getRefreshToken(refreshToken);
|
||||||
|
SaOAuth2Exception.throwBy(rt == null, "无效refresh_token: " + refreshToken, SaOAuth2ErrorCode.CODE_30121);
|
||||||
|
|
||||||
|
// 校验:Refresh-Token 代表的 ClientId 与提供的 ClientId 是否一致
|
||||||
|
SaOAuth2Exception.throwBy( ! rt.clientId.equals(clientId), "无效client_id: " + clientId, SaOAuth2ErrorCode.CODE_30122);
|
||||||
|
|
||||||
|
// 获取新 Access-Token
|
||||||
|
AccessTokenModel accessTokenModel = SaOAuth2Manager.getDataGenerate().refreshAccessToken(refreshToken);
|
||||||
|
return accessTokenModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020-2099 sa-token.cc
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package cn.dev33.satoken.oauth2.granttype.handler;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.context.model.SaRequest;
|
||||||
|
import cn.dev33.satoken.oauth2.data.model.AccessTokenModel;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 所有 OAuth2 GrantType 处理器的父接口
|
||||||
|
*
|
||||||
|
* @author click33
|
||||||
|
* @since 1.39.0
|
||||||
|
*/
|
||||||
|
public interface SaOAuth2GrantTypeHandlerInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所要处理的 GrantType
|
||||||
|
*
|
||||||
|
* @return /
|
||||||
|
*/
|
||||||
|
String getHandlerGrantType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 AccessTokenModel 对象
|
||||||
|
*
|
||||||
|
* @param req /
|
||||||
|
* @return /
|
||||||
|
*/
|
||||||
|
AccessTokenModel getAccessTokenModel(SaRequest req, String clientId, List<String> scopes);
|
||||||
|
|
||||||
|
}
|
@ -20,9 +20,9 @@ import cn.dev33.satoken.context.model.SaRequest;
|
|||||||
import cn.dev33.satoken.context.model.SaResponse;
|
import cn.dev33.satoken.context.model.SaResponse;
|
||||||
import cn.dev33.satoken.oauth2.SaOAuth2Manager;
|
import cn.dev33.satoken.oauth2.SaOAuth2Manager;
|
||||||
import cn.dev33.satoken.oauth2.config.SaOAuth2Config;
|
import cn.dev33.satoken.oauth2.config.SaOAuth2Config;
|
||||||
|
import cn.dev33.satoken.oauth2.consts.GrantType;
|
||||||
import cn.dev33.satoken.oauth2.consts.SaOAuth2Consts;
|
import cn.dev33.satoken.oauth2.consts.SaOAuth2Consts;
|
||||||
import cn.dev33.satoken.oauth2.consts.SaOAuth2Consts.Api;
|
import cn.dev33.satoken.oauth2.consts.SaOAuth2Consts.Api;
|
||||||
import cn.dev33.satoken.oauth2.consts.SaOAuth2Consts.GrantType;
|
|
||||||
import cn.dev33.satoken.oauth2.consts.SaOAuth2Consts.Param;
|
import cn.dev33.satoken.oauth2.consts.SaOAuth2Consts.Param;
|
||||||
import cn.dev33.satoken.oauth2.consts.SaOAuth2Consts.ResponseType;
|
import cn.dev33.satoken.oauth2.consts.SaOAuth2Consts.ResponseType;
|
||||||
import cn.dev33.satoken.oauth2.data.generate.SaOAuth2DataGenerate;
|
import cn.dev33.satoken.oauth2.data.generate.SaOAuth2DataGenerate;
|
||||||
@ -34,10 +34,9 @@ import cn.dev33.satoken.oauth2.data.model.request.ClientIdAndSecretModel;
|
|||||||
import cn.dev33.satoken.oauth2.data.model.request.RequestAuthModel;
|
import cn.dev33.satoken.oauth2.data.model.request.RequestAuthModel;
|
||||||
import cn.dev33.satoken.oauth2.error.SaOAuth2ErrorCode;
|
import cn.dev33.satoken.oauth2.error.SaOAuth2ErrorCode;
|
||||||
import cn.dev33.satoken.oauth2.exception.SaOAuth2Exception;
|
import cn.dev33.satoken.oauth2.exception.SaOAuth2Exception;
|
||||||
|
import cn.dev33.satoken.oauth2.strategy.SaOAuth2Strategy;
|
||||||
import cn.dev33.satoken.oauth2.template.SaOAuth2Template;
|
import cn.dev33.satoken.oauth2.template.SaOAuth2Template;
|
||||||
import cn.dev33.satoken.router.SaHttpMethod;
|
import cn.dev33.satoken.router.SaHttpMethod;
|
||||||
import cn.dev33.satoken.stp.StpLogic;
|
|
||||||
import cn.dev33.satoken.stp.StpUtil;
|
|
||||||
import cn.dev33.satoken.util.SaResult;
|
import cn.dev33.satoken.util.SaResult;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -55,11 +54,6 @@ public class SaOAuth2ServerProcessor {
|
|||||||
*/
|
*/
|
||||||
public static SaOAuth2ServerProcessor instance = new SaOAuth2ServerProcessor();
|
public static SaOAuth2ServerProcessor instance = new SaOAuth2ServerProcessor();
|
||||||
|
|
||||||
/**
|
|
||||||
* 底层 SaOAuth2Template 对象
|
|
||||||
*/
|
|
||||||
public SaOAuth2Template oauth2Template = new SaOAuth2Template();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理 Server 端请求, 路由分发
|
* 处理 Server 端请求, 路由分发
|
||||||
* @return 处理结果
|
* @return 处理结果
|
||||||
@ -68,7 +62,6 @@ public class SaOAuth2ServerProcessor {
|
|||||||
|
|
||||||
// 获取变量
|
// 获取变量
|
||||||
SaRequest req = SaHolder.getRequest();
|
SaRequest req = SaHolder.getRequest();
|
||||||
SaOAuth2Config cfg = SaOAuth2Manager.getConfig();
|
|
||||||
|
|
||||||
// ------------------ 路由分发 ------------------
|
// ------------------ 路由分发 ------------------
|
||||||
|
|
||||||
@ -79,7 +72,7 @@ public class SaOAuth2ServerProcessor {
|
|||||||
|
|
||||||
// Code 换 Access-Token || 模式三:密码式
|
// Code 换 Access-Token || 模式三:密码式
|
||||||
if(req.isPath(Api.token)) {
|
if(req.isPath(Api.token)) {
|
||||||
return tokenOrPassword();
|
return token();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh-Token 刷新 Access-Token
|
// Refresh-Token 刷新 Access-Token
|
||||||
@ -122,18 +115,19 @@ public class SaOAuth2ServerProcessor {
|
|||||||
SaResponse res = SaHolder.getResponse();
|
SaResponse res = SaHolder.getResponse();
|
||||||
SaOAuth2Config cfg = SaOAuth2Manager.getConfig();
|
SaOAuth2Config cfg = SaOAuth2Manager.getConfig();
|
||||||
SaOAuth2DataGenerate dataGenerate = SaOAuth2Manager.getDataGenerate();
|
SaOAuth2DataGenerate dataGenerate = SaOAuth2Manager.getDataGenerate();
|
||||||
|
SaOAuth2Template oauth2Template = SaOAuth2Manager.getTemplate();
|
||||||
String responseType = req.getParamNotNull(Param.response_type);
|
String responseType = req.getParamNotNull(Param.response_type);
|
||||||
|
|
||||||
// 1、先判断是否开启了指定的授权模式
|
// 1、先判断是否开启了指定的授权模式
|
||||||
checkAuthorizeResponseType(responseType, req, cfg);
|
checkAuthorizeResponseType(responseType, req, cfg);
|
||||||
|
|
||||||
// 2、如果尚未登录, 则先去登录
|
// 2、如果尚未登录, 则先去登录
|
||||||
if( ! getStpLogic().isLogin()) {
|
if( ! SaOAuth2Manager.getStpLogic().isLogin()) {
|
||||||
return cfg.notLoginView.get();
|
return cfg.notLoginView.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3、构建请求 Model
|
// 3、构建请求 Model
|
||||||
RequestAuthModel ra = SaOAuth2Manager.getDataResolver().readRequestAuthModel(req, getStpLogic().getLoginId());
|
RequestAuthModel ra = SaOAuth2Manager.getDataResolver().readRequestAuthModel(req, SaOAuth2Manager.getStpLogic().getLoginId());
|
||||||
|
|
||||||
// 4、校验:重定向域名是否合法
|
// 4、校验:重定向域名是否合法
|
||||||
oauth2Template.checkRightUrl(ra.clientId, ra.redirectUri);
|
oauth2Template.checkRightUrl(ra.clientId, ra.redirectUri);
|
||||||
@ -167,55 +161,11 @@ public class SaOAuth2ServerProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Code 换 Access-Token || 模式三:密码式
|
* Code 换 Access-Token / 模式三:密码式 / 自定义 grant_type
|
||||||
* @return 处理结果
|
|
||||||
*/
|
|
||||||
public Object tokenOrPassword() {
|
|
||||||
|
|
||||||
String grantType = SaHolder.getRequest().getParamNotNull(Param.grant_type);
|
|
||||||
|
|
||||||
// Code 换 Access-Token
|
|
||||||
if(grantType.equals(GrantType.authorization_code)) {
|
|
||||||
return token();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 模式三:密码式
|
|
||||||
if(grantType.equals(GrantType.password)) {
|
|
||||||
SaOAuth2Config cfg = SaOAuth2Manager.getConfig();
|
|
||||||
if(!cfg.enablePassword) {
|
|
||||||
throwErrorSystemNotEnableModel();
|
|
||||||
}
|
|
||||||
if(!currClientModel().enablePassword) {
|
|
||||||
throwErrorClientNotEnableModel();
|
|
||||||
}
|
|
||||||
return password();
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new SaOAuth2Exception("无效 grant_type:" + grantType);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Code授权码 获取 Access-Token
|
|
||||||
* @return 处理结果
|
* @return 处理结果
|
||||||
*/
|
*/
|
||||||
public Object token() {
|
public Object token() {
|
||||||
// 获取变量
|
AccessTokenModel accessTokenModel = SaOAuth2Strategy.instance.grantTypeAuth.apply(SaHolder.getRequest());
|
||||||
SaRequest req = SaHolder.getRequest();
|
|
||||||
|
|
||||||
// 获取参数
|
|
||||||
ClientIdAndSecretModel clientIdAndSecret = SaOAuth2Manager.getDataResolver().readClientIdAndSecret(req);
|
|
||||||
String clientId = clientIdAndSecret.clientId;
|
|
||||||
String clientSecret = clientIdAndSecret.clientSecret;
|
|
||||||
String code = req.getParamNotNull(Param.code);
|
|
||||||
String redirectUri = req.getParam(Param.redirect_uri);
|
|
||||||
|
|
||||||
// 校验参数
|
|
||||||
oauth2Template.checkGainTokenParam(code, clientId, clientSecret, redirectUri);
|
|
||||||
|
|
||||||
// 构建 Access-Token
|
|
||||||
AccessTokenModel accessTokenModel = SaOAuth2Manager.getDataGenerate().generateAccessToken(code);
|
|
||||||
|
|
||||||
// 返回
|
|
||||||
return SaOAuth2Manager.getDataResolver().buildTokenReturnValue(accessTokenModel);
|
return SaOAuth2Manager.getDataResolver().buildTokenReturnValue(accessTokenModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,27 +174,10 @@ public class SaOAuth2ServerProcessor {
|
|||||||
* @return 处理结果
|
* @return 处理结果
|
||||||
*/
|
*/
|
||||||
public Object refresh() {
|
public Object refresh() {
|
||||||
// 获取变量
|
|
||||||
SaRequest req = SaHolder.getRequest();
|
SaRequest req = SaHolder.getRequest();
|
||||||
String grantType = req.getParamNotNull(Param.grant_type);
|
String grantType = req.getParamNotNull(Param.grant_type);
|
||||||
if(!grantType.equals(GrantType.refresh_token)) {
|
SaOAuth2Exception.throwBy(!grantType.equals(GrantType.refresh_token), "无效 grant_type:" + grantType, SaOAuth2ErrorCode.CODE_30126);
|
||||||
throw new SaOAuth2Exception("无效 grant_type:" + grantType).setCode(SaOAuth2ErrorCode.CODE_30126);
|
AccessTokenModel accessTokenModel = SaOAuth2Strategy.instance.grantTypeAuth.apply(req);
|
||||||
}
|
|
||||||
|
|
||||||
// 获取参数
|
|
||||||
|
|
||||||
ClientIdAndSecretModel clientIdAndSecret = SaOAuth2Manager.getDataResolver().readClientIdAndSecret(req);
|
|
||||||
String clientId = clientIdAndSecret.clientId;
|
|
||||||
String clientSecret = clientIdAndSecret.clientSecret;
|
|
||||||
String refreshToken = req.getParamNotNull(Param.refresh_token);
|
|
||||||
|
|
||||||
// 校验参数
|
|
||||||
oauth2Template.checkRefreshTokenParam(clientId, clientSecret, refreshToken);
|
|
||||||
|
|
||||||
// 获取新 Access-Token
|
|
||||||
AccessTokenModel accessTokenModel = SaOAuth2Manager.getDataGenerate().refreshAccessToken(refreshToken);
|
|
||||||
|
|
||||||
// 返回
|
|
||||||
return SaOAuth2Manager.getDataResolver().buildRefreshTokenReturnValue(accessTokenModel);
|
return SaOAuth2Manager.getDataResolver().buildRefreshTokenReturnValue(accessTokenModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,6 +187,7 @@ public class SaOAuth2ServerProcessor {
|
|||||||
*/
|
*/
|
||||||
public Object revoke() {
|
public Object revoke() {
|
||||||
// 获取变量
|
// 获取变量
|
||||||
|
SaOAuth2Template oauth2Template = SaOAuth2Manager.getTemplate();
|
||||||
SaRequest req = SaHolder.getRequest();
|
SaRequest req = SaHolder.getRequest();
|
||||||
|
|
||||||
// 获取参数
|
// 获取参数
|
||||||
@ -297,10 +231,11 @@ public class SaOAuth2ServerProcessor {
|
|||||||
// 获取变量
|
// 获取变量
|
||||||
SaRequest req = SaHolder.getRequest();
|
SaRequest req = SaHolder.getRequest();
|
||||||
String clientId = req.getParamNotNull(Param.client_id);
|
String clientId = req.getParamNotNull(Param.client_id);
|
||||||
Object loginId = getStpLogic().getLoginId();
|
Object loginId = SaOAuth2Manager.getStpLogic().getLoginId();
|
||||||
String scope = req.getParamNotNull(Param.scope);
|
String scope = req.getParamNotNull(Param.scope);
|
||||||
List<String> scopes = SaOAuth2Manager.getDataConverter().convertScopeStringToList(scope);
|
List<String> scopes = SaOAuth2Manager.getDataConverter().convertScopeStringToList(scope);
|
||||||
SaOAuth2DataGenerate dataGenerate = SaOAuth2Manager.getDataGenerate();
|
SaOAuth2DataGenerate dataGenerate = SaOAuth2Manager.getDataGenerate();
|
||||||
|
SaOAuth2Template oauth2Template = SaOAuth2Manager.getTemplate();
|
||||||
|
|
||||||
// 此请求只允许 POST 方式
|
// 此请求只允许 POST 方式
|
||||||
if(!req.isMethod(SaHttpMethod.POST)) {
|
if(!req.isMethod(SaHttpMethod.POST)) {
|
||||||
@ -343,49 +278,6 @@ public class SaOAuth2ServerProcessor {
|
|||||||
throw new SaOAuth2Exception("无效response_type: " + ra.responseType).setCode(SaOAuth2ErrorCode.CODE_30125);
|
throw new SaOAuth2Exception("无效response_type: " + ra.responseType).setCode(SaOAuth2ErrorCode.CODE_30125);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 模式三:密码式
|
|
||||||
* @return 处理结果
|
|
||||||
*/
|
|
||||||
public Object password() {
|
|
||||||
// 获取变量
|
|
||||||
SaRequest req = SaHolder.getRequest();
|
|
||||||
SaOAuth2Config cfg = SaOAuth2Manager.getConfig();
|
|
||||||
|
|
||||||
// 1、获取请求参数
|
|
||||||
String username = req.getParamNotNull(Param.username);
|
|
||||||
String password = req.getParamNotNull(Param.password);
|
|
||||||
ClientIdAndSecretModel clientIdAndSecret = SaOAuth2Manager.getDataResolver().readClientIdAndSecret(req);
|
|
||||||
String clientId = clientIdAndSecret.clientId;
|
|
||||||
String clientSecret = clientIdAndSecret.clientSecret;
|
|
||||||
String scope = req.getParam(Param.scope, "");
|
|
||||||
List<String> scopes = SaOAuth2Manager.getDataConverter().convertScopeStringToList(scope);
|
|
||||||
|
|
||||||
// 2、校验 ClientScope 和 scope
|
|
||||||
oauth2Template.checkClientSecretAndScope(clientId, clientSecret, scopes);
|
|
||||||
|
|
||||||
// 3、防止因前端误传token造成逻辑干扰
|
|
||||||
// SaHolder.getStorage().set(getStpLogic().stpLogic.splicingKeyJustCreatedSave(), "no-token");
|
|
||||||
|
|
||||||
// 3、调用API 开始登录,如果没能成功登录,则直接退出
|
|
||||||
Object retObj = cfg.doLoginHandle.apply(username, password);
|
|
||||||
if( ! getStpLogic().isLogin()) {
|
|
||||||
return retObj;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4、构建 ra对象
|
|
||||||
RequestAuthModel ra = new RequestAuthModel();
|
|
||||||
ra.clientId = clientId;
|
|
||||||
ra.loginId = getStpLogic().getLoginId();
|
|
||||||
ra.scopes = scopes;
|
|
||||||
|
|
||||||
// 5、生成 Access-Token
|
|
||||||
AccessTokenModel at = SaOAuth2Manager.getDataGenerate().generateAccessToken(ra, true);
|
|
||||||
|
|
||||||
// 6、返回 Access-Token
|
|
||||||
return SaOAuth2Manager.getDataResolver().buildPasswordReturnValue(at);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 模式四:凭证式
|
* 模式四:凭证式
|
||||||
* @return 处理结果
|
* @return 处理结果
|
||||||
@ -394,6 +286,7 @@ public class SaOAuth2ServerProcessor {
|
|||||||
// 获取变量
|
// 获取变量
|
||||||
SaRequest req = SaHolder.getRequest();
|
SaRequest req = SaHolder.getRequest();
|
||||||
SaOAuth2Config cfg = SaOAuth2Manager.getConfig();
|
SaOAuth2Config cfg = SaOAuth2Manager.getConfig();
|
||||||
|
SaOAuth2Template oauth2Template = SaOAuth2Manager.getTemplate();
|
||||||
|
|
||||||
String grantType = req.getParamNotNull(Param.grant_type);
|
String grantType = req.getParamNotNull(Param.grant_type);
|
||||||
if(!grantType.equals(GrantType.client_credentials)) {
|
if(!grantType.equals(GrantType.client_credentials)) {
|
||||||
@ -402,7 +295,7 @@ public class SaOAuth2ServerProcessor {
|
|||||||
if(!cfg.enableClient) {
|
if(!cfg.enableClient) {
|
||||||
throwErrorSystemNotEnableModel();
|
throwErrorSystemNotEnableModel();
|
||||||
}
|
}
|
||||||
if(!currClientModel().enableClient) {
|
if(!currClientModel().getAllowGrantTypes().contains(GrantType.client_credentials)) {
|
||||||
throwErrorClientNotEnableModel();
|
throwErrorClientNotEnableModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -434,6 +327,7 @@ public class SaOAuth2ServerProcessor {
|
|||||||
* @return /
|
* @return /
|
||||||
*/
|
*/
|
||||||
public SaClientModel currClientModel() {
|
public SaClientModel currClientModel() {
|
||||||
|
SaOAuth2Template oauth2Template = SaOAuth2Manager.getTemplate();
|
||||||
ClientIdAndSecretModel clientIdAndSecret = SaOAuth2Manager.getDataResolver().readClientIdAndSecret(SaHolder.getRequest());
|
ClientIdAndSecretModel clientIdAndSecret = SaOAuth2Manager.getDataResolver().readClientIdAndSecret(SaHolder.getRequest());
|
||||||
return oauth2Template.checkClientModel(clientIdAndSecret.clientId);
|
return oauth2Template.checkClientModel(clientIdAndSecret.clientId);
|
||||||
}
|
}
|
||||||
@ -447,7 +341,7 @@ public class SaOAuth2ServerProcessor {
|
|||||||
if(!cfg.enableCode) {
|
if(!cfg.enableCode) {
|
||||||
throwErrorSystemNotEnableModel();
|
throwErrorSystemNotEnableModel();
|
||||||
}
|
}
|
||||||
if(!currClientModel().enableCode) {
|
if(!currClientModel().getAllowGrantTypes().contains(GrantType.authorization_code)) {
|
||||||
throwErrorClientNotEnableModel();
|
throwErrorClientNotEnableModel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -456,7 +350,7 @@ public class SaOAuth2ServerProcessor {
|
|||||||
if(!cfg.enableImplicit) {
|
if(!cfg.enableImplicit) {
|
||||||
throwErrorSystemNotEnableModel();
|
throwErrorSystemNotEnableModel();
|
||||||
}
|
}
|
||||||
if(!currClientModel().enableImplicit) {
|
if(!currClientModel().getAllowGrantTypes().contains(GrantType.implicit)) {
|
||||||
throwErrorClientNotEnableModel();
|
throwErrorClientNotEnableModel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -466,15 +360,6 @@ public class SaOAuth2ServerProcessor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取底层使用的会话对象
|
|
||||||
*
|
|
||||||
* @return /
|
|
||||||
*/
|
|
||||||
public StpLogic getStpLogic() {
|
|
||||||
return StpUtil.stpLogic;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 系统未开放此授权模式时抛出异常
|
* 系统未开放此授权模式时抛出异常
|
||||||
*/
|
*/
|
||||||
|
@ -16,8 +16,18 @@
|
|||||||
package cn.dev33.satoken.oauth2.strategy;
|
package cn.dev33.satoken.oauth2.strategy;
|
||||||
|
|
||||||
import cn.dev33.satoken.SaManager;
|
import cn.dev33.satoken.SaManager;
|
||||||
|
import cn.dev33.satoken.oauth2.SaOAuth2Manager;
|
||||||
|
import cn.dev33.satoken.oauth2.config.SaOAuth2Config;
|
||||||
|
import cn.dev33.satoken.oauth2.consts.GrantType;
|
||||||
import cn.dev33.satoken.oauth2.consts.SaOAuth2Consts;
|
import cn.dev33.satoken.oauth2.consts.SaOAuth2Consts;
|
||||||
|
import cn.dev33.satoken.oauth2.data.model.loader.SaClientModel;
|
||||||
|
import cn.dev33.satoken.oauth2.data.model.request.ClientIdAndSecretModel;
|
||||||
|
import cn.dev33.satoken.oauth2.exception.SaOAuth2Exception;
|
||||||
import cn.dev33.satoken.oauth2.function.strategy.*;
|
import cn.dev33.satoken.oauth2.function.strategy.*;
|
||||||
|
import cn.dev33.satoken.oauth2.granttype.handler.AuthorizationCodeGrantTypeHandler;
|
||||||
|
import cn.dev33.satoken.oauth2.granttype.handler.PasswordGrantTypeHandler;
|
||||||
|
import cn.dev33.satoken.oauth2.granttype.handler.RefreshTokenGrantTypeHandler;
|
||||||
|
import cn.dev33.satoken.oauth2.granttype.handler.SaOAuth2GrantTypeHandlerInterface;
|
||||||
import cn.dev33.satoken.oauth2.scope.CommonScope;
|
import cn.dev33.satoken.oauth2.scope.CommonScope;
|
||||||
import cn.dev33.satoken.oauth2.scope.handler.OidcScopeHandler;
|
import cn.dev33.satoken.oauth2.scope.handler.OidcScopeHandler;
|
||||||
import cn.dev33.satoken.oauth2.scope.handler.OpenIdScopeHandler;
|
import cn.dev33.satoken.oauth2.scope.handler.OpenIdScopeHandler;
|
||||||
@ -26,6 +36,7 @@ import cn.dev33.satoken.oauth2.scope.handler.UserIdScopeHandler;
|
|||||||
import cn.dev33.satoken.util.SaFoxUtil;
|
import cn.dev33.satoken.util.SaFoxUtil;
|
||||||
|
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -38,6 +49,7 @@ public final class SaOAuth2Strategy {
|
|||||||
|
|
||||||
private SaOAuth2Strategy() {
|
private SaOAuth2Strategy() {
|
||||||
registerDefaultScopeHandler();
|
registerDefaultScopeHandler();
|
||||||
|
registerDefaultGrantTypeHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -78,9 +90,6 @@ public final class SaOAuth2Strategy {
|
|||||||
scopeHandlerMap.remove(scope);
|
scopeHandlerMap.remove(scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ----------------------- 所有策略
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据 scope 信息对一个 AccessTokenModel 进行加工处理
|
* 根据 scope 信息对一个 AccessTokenModel 进行加工处理
|
||||||
*/
|
*/
|
||||||
@ -117,6 +126,75 @@ public final class SaOAuth2Strategy {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// grant_type 处理器
|
||||||
|
|
||||||
|
/**
|
||||||
|
* grant_type 处理器集合
|
||||||
|
*/
|
||||||
|
public Map<String, SaOAuth2GrantTypeHandlerInterface> grantTypeHandlerMap = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册所有默认的权限处理器
|
||||||
|
*/
|
||||||
|
public void registerDefaultGrantTypeHandler() {
|
||||||
|
grantTypeHandlerMap.put(GrantType.authorization_code, new AuthorizationCodeGrantTypeHandler());
|
||||||
|
grantTypeHandlerMap.put(GrantType.password, new PasswordGrantTypeHandler());
|
||||||
|
grantTypeHandlerMap.put(GrantType.refresh_token, new RefreshTokenGrantTypeHandler());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册一个权限处理器
|
||||||
|
*/
|
||||||
|
public void registerGrantTypeHandler(SaOAuth2GrantTypeHandlerInterface handler) {
|
||||||
|
grantTypeHandlerMap.put(handler.getHandlerGrantType(), handler);
|
||||||
|
// TODO 优化日志输出
|
||||||
|
SaManager.getLog().info("新增GrantType处理器:" + handler.getHandlerGrantType());
|
||||||
|
// SaTokenEventCenter.doRegisterAnnotationHandler(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除一个权限处理器
|
||||||
|
*/
|
||||||
|
public void removeGrantTypeHandler(String scope) {
|
||||||
|
scopeHandlerMap.remove(scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 scope 信息对一个 AccessTokenModel 进行加工处理
|
||||||
|
*/
|
||||||
|
public SaOAuth2GrantTypeAuthFunction grantTypeAuth = (req) -> {
|
||||||
|
String grantType = req.getParamNotNull(SaOAuth2Consts.Param.grant_type);
|
||||||
|
SaOAuth2GrantTypeHandlerInterface grantTypeHandler = grantTypeHandlerMap.get(grantType);
|
||||||
|
if(grantTypeHandler == null) {
|
||||||
|
throw new RuntimeException("无效 grant_type: " + grantType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 看看全局是否开启了此 grantType
|
||||||
|
SaOAuth2Config config = SaOAuth2Manager.getConfig();
|
||||||
|
if(grantType.equals(GrantType.authorization_code) && !config.getEnableCode() ) {
|
||||||
|
throw new SaOAuth2Exception("系统未开放的 grant_type: " + grantType);
|
||||||
|
}
|
||||||
|
if(grantType.equals(GrantType.password) && !config.getEnablePassword() ) {
|
||||||
|
throw new SaOAuth2Exception("系统未开放的 grant_type: " + grantType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验 clientSecret 和 scope
|
||||||
|
ClientIdAndSecretModel clientIdAndSecretModel = SaOAuth2Manager.getDataResolver().readClientIdAndSecret(req);
|
||||||
|
List<String> scopes = SaOAuth2Manager.getDataConverter().convertScopeStringToList(req.getParam(SaOAuth2Consts.Param.scope));
|
||||||
|
SaClientModel clientModel = SaOAuth2Manager.getTemplate().checkClientSecretAndScope(clientIdAndSecretModel.getClientId(), clientIdAndSecretModel.getClientSecret(), scopes);
|
||||||
|
|
||||||
|
// 检测应用是否开启此 grantType
|
||||||
|
if(!clientModel.getAllowGrantTypes().contains(grantType)) {
|
||||||
|
throw new SaOAuth2Exception("应用未开放的 grant_type: " + grantType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用 处理器
|
||||||
|
return grantTypeHandler.getAccessTokenModel(req, clientIdAndSecretModel.getClientId(), scopes);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------- 所有策略
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建一个 code value
|
* 创建一个 code value
|
||||||
*/
|
*/
|
||||||
|
@ -41,7 +41,7 @@ public class SaOAuth2Util {
|
|||||||
* @return ClientModel
|
* @return ClientModel
|
||||||
*/
|
*/
|
||||||
public static SaClientModel checkClientModel(String clientId) {
|
public static SaClientModel checkClientModel(String clientId) {
|
||||||
return SaOAuth2ServerProcessor.instance.oauth2Template.checkClientModel(clientId);
|
return SaOAuth2Manager.getTemplate().checkClientModel(clientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -50,7 +50,7 @@ public class SaOAuth2Util {
|
|||||||
* @return .
|
* @return .
|
||||||
*/
|
*/
|
||||||
public static AccessTokenModel checkAccessToken(String accessToken) {
|
public static AccessTokenModel checkAccessToken(String accessToken) {
|
||||||
return SaOAuth2ServerProcessor.instance.oauth2Template.checkAccessToken(accessToken);
|
return SaOAuth2Manager.getTemplate().checkAccessToken(accessToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -59,7 +59,7 @@ public class SaOAuth2Util {
|
|||||||
* @return .
|
* @return .
|
||||||
*/
|
*/
|
||||||
public static ClientTokenModel checkClientToken(String clientToken) {
|
public static ClientTokenModel checkClientToken(String clientToken) {
|
||||||
return SaOAuth2ServerProcessor.instance.oauth2Template.checkClientToken(clientToken);
|
return SaOAuth2Manager.getTemplate().checkClientToken(clientToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -68,7 +68,7 @@ public class SaOAuth2Util {
|
|||||||
* @return LoginId
|
* @return LoginId
|
||||||
*/
|
*/
|
||||||
public static Object getLoginIdByAccessToken(String accessToken) {
|
public static Object getLoginIdByAccessToken(String accessToken) {
|
||||||
return SaOAuth2ServerProcessor.instance.oauth2Template.getLoginIdByAccessToken(accessToken);
|
return SaOAuth2Manager.getTemplate().getLoginIdByAccessToken(accessToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -77,7 +77,7 @@ public class SaOAuth2Util {
|
|||||||
* @param scopes 需要校验的权限列表
|
* @param scopes 需要校验的权限列表
|
||||||
*/
|
*/
|
||||||
public static void checkScope(String accessToken, String... scopes) {
|
public static void checkScope(String accessToken, String... scopes) {
|
||||||
SaOAuth2ServerProcessor.instance.oauth2Template.checkScope(accessToken, scopes);
|
SaOAuth2Manager.getTemplate().checkScope(accessToken, scopes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -86,7 +86,7 @@ public class SaOAuth2Util {
|
|||||||
* @param scopes 需要校验的权限列表
|
* @param scopes 需要校验的权限列表
|
||||||
*/
|
*/
|
||||||
public static void checkClientTokenScope(String clientToken, String... scopes) {
|
public static void checkClientTokenScope(String clientToken, String... scopes) {
|
||||||
SaOAuth2ServerProcessor.instance.oauth2Template.checkClientTokenScope(clientToken, scopes);
|
SaOAuth2Manager.getTemplate().checkClientTokenScope(clientToken, scopes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -100,7 +100,7 @@ public class SaOAuth2Util {
|
|||||||
* @return 是否已经授权
|
* @return 是否已经授权
|
||||||
*/
|
*/
|
||||||
public static boolean isGrant(Object loginId, String clientId, List<String> scopes) {
|
public static boolean isGrant(Object loginId, String clientId, List<String> scopes) {
|
||||||
return SaOAuth2ServerProcessor.instance.oauth2Template.isGrant(loginId, clientId, scopes);
|
return SaOAuth2Manager.getTemplate().isGrant(loginId, clientId, scopes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -109,7 +109,7 @@ public class SaOAuth2Util {
|
|||||||
* @param scopes 权限(多个用逗号隔开)
|
* @param scopes 权限(多个用逗号隔开)
|
||||||
*/
|
*/
|
||||||
public static void checkContract(String clientId, List<String> scopes) {
|
public static void checkContract(String clientId, List<String> scopes) {
|
||||||
SaOAuth2ServerProcessor.instance.oauth2Template.checkContract(clientId, scopes);
|
SaOAuth2Manager.getTemplate().checkContract(clientId, scopes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -118,7 +118,7 @@ public class SaOAuth2Util {
|
|||||||
* @param url 指定url
|
* @param url 指定url
|
||||||
*/
|
*/
|
||||||
public static void checkRightUrl(String clientId, String url) {
|
public static void checkRightUrl(String clientId, String url) {
|
||||||
SaOAuth2ServerProcessor.instance.oauth2Template.checkRightUrl(clientId, url);
|
SaOAuth2Manager.getTemplate().checkRightUrl(clientId, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -128,7 +128,7 @@ public class SaOAuth2Util {
|
|||||||
* @return SaClientModel对象
|
* @return SaClientModel对象
|
||||||
*/
|
*/
|
||||||
public static SaClientModel checkClientSecret(String clientId, String clientSecret) {
|
public static SaClientModel checkClientSecret(String clientId, String clientSecret) {
|
||||||
return SaOAuth2ServerProcessor.instance.oauth2Template.checkClientSecret(clientId, clientSecret);
|
return SaOAuth2Manager.getTemplate().checkClientSecret(clientId, clientSecret);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -139,7 +139,7 @@ public class SaOAuth2Util {
|
|||||||
* @return SaClientModel对象
|
* @return SaClientModel对象
|
||||||
*/
|
*/
|
||||||
public static SaClientModel checkClientSecretAndScope(String clientId, String clientSecret, List<String> scopes) {
|
public static SaClientModel checkClientSecretAndScope(String clientId, String clientSecret, List<String> scopes) {
|
||||||
return SaOAuth2ServerProcessor.instance.oauth2Template.checkClientSecretAndScope(clientId, clientSecret, scopes);
|
return SaOAuth2Manager.getTemplate().checkClientSecretAndScope(clientId, clientSecret, scopes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -151,7 +151,7 @@ public class SaOAuth2Util {
|
|||||||
* @return CodeModel对象
|
* @return CodeModel对象
|
||||||
*/
|
*/
|
||||||
public static CodeModel checkGainTokenParam(String code, String clientId, String clientSecret, String redirectUri) {
|
public static CodeModel checkGainTokenParam(String code, String clientId, String clientSecret, String redirectUri) {
|
||||||
return SaOAuth2ServerProcessor.instance.oauth2Template.checkGainTokenParam(code, clientId, clientSecret, redirectUri);
|
return SaOAuth2Manager.getTemplate().checkGainTokenParam(code, clientId, clientSecret, redirectUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -162,7 +162,7 @@ public class SaOAuth2Util {
|
|||||||
* @return CodeModel对象
|
* @return CodeModel对象
|
||||||
*/
|
*/
|
||||||
public static RefreshTokenModel checkRefreshTokenParam(String clientId, String clientSecret, String refreshToken) {
|
public static RefreshTokenModel checkRefreshTokenParam(String clientId, String clientSecret, String refreshToken) {
|
||||||
return SaOAuth2ServerProcessor.instance.oauth2Template.checkRefreshTokenParam(clientId, clientSecret, refreshToken);
|
return SaOAuth2Manager.getTemplate().checkRefreshTokenParam(clientId, clientSecret, refreshToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -173,7 +173,7 @@ public class SaOAuth2Util {
|
|||||||
* @return SaClientModel对象
|
* @return SaClientModel对象
|
||||||
*/
|
*/
|
||||||
public static AccessTokenModel checkAccessTokenParam(String clientId, String clientSecret, String accessToken) {
|
public static AccessTokenModel checkAccessTokenParam(String clientId, String clientSecret, String accessToken) {
|
||||||
return SaOAuth2ServerProcessor.instance.oauth2Template.checkAccessTokenParam(clientId, clientSecret, accessToken);
|
return SaOAuth2Manager.getTemplate().checkAccessTokenParam(clientId, clientSecret, accessToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------- save 数据
|
// ------------------- save 数据
|
||||||
@ -185,7 +185,7 @@ public class SaOAuth2Util {
|
|||||||
* @param scopes 权限列表
|
* @param scopes 权限列表
|
||||||
*/
|
*/
|
||||||
public static void saveGrantScope(String clientId, Object loginId, List<String> scopes) {
|
public static void saveGrantScope(String clientId, Object loginId, List<String> scopes) {
|
||||||
SaOAuth2ServerProcessor.instance.oauth2Template.saveGrantScope(clientId, loginId, scopes);
|
SaOAuth2Manager.getTemplate().saveGrantScope(clientId, loginId, scopes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ import cn.dev33.satoken.oauth2.data.convert.SaOAuth2DataConverter;
|
|||||||
import cn.dev33.satoken.oauth2.data.generate.SaOAuth2DataGenerate;
|
import cn.dev33.satoken.oauth2.data.generate.SaOAuth2DataGenerate;
|
||||||
import cn.dev33.satoken.oauth2.data.loader.SaOAuth2DataLoader;
|
import cn.dev33.satoken.oauth2.data.loader.SaOAuth2DataLoader;
|
||||||
import cn.dev33.satoken.oauth2.data.resolver.SaOAuth2DataResolver;
|
import cn.dev33.satoken.oauth2.data.resolver.SaOAuth2DataResolver;
|
||||||
|
import cn.dev33.satoken.oauth2.granttype.handler.SaOAuth2GrantTypeHandlerInterface;
|
||||||
import cn.dev33.satoken.oauth2.processor.SaOAuth2ServerProcessor;
|
import cn.dev33.satoken.oauth2.processor.SaOAuth2ServerProcessor;
|
||||||
import cn.dev33.satoken.oauth2.scope.handler.SaOAuth2ScopeHandlerInterface;
|
import cn.dev33.satoken.oauth2.scope.handler.SaOAuth2ScopeHandlerInterface;
|
||||||
import cn.dev33.satoken.oauth2.strategy.SaOAuth2Strategy;
|
import cn.dev33.satoken.oauth2.strategy.SaOAuth2Strategy;
|
||||||
@ -62,7 +63,17 @@ public class SaOAuth2BeanInject {
|
|||||||
*/
|
*/
|
||||||
@Autowired(required = false)
|
@Autowired(required = false)
|
||||||
public void setSaOAuth2Template(SaOAuth2Template saOAuth2Template) {
|
public void setSaOAuth2Template(SaOAuth2Template saOAuth2Template) {
|
||||||
SaOAuth2ServerProcessor.instance.oauth2Template = saOAuth2Template;
|
SaOAuth2Manager.setTemplate(saOAuth2Template);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注入 OAuth2 请求处理器
|
||||||
|
*
|
||||||
|
* @param serverProcessor 请求处理器
|
||||||
|
*/
|
||||||
|
@Autowired(required = false)
|
||||||
|
public void setSaOAuth2Template(SaOAuth2ServerProcessor serverProcessor) {
|
||||||
|
SaOAuth2ServerProcessor.instance = serverProcessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -127,4 +138,16 @@ public class SaOAuth2BeanInject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注入自定义 grant_type 处理器
|
||||||
|
*
|
||||||
|
* @param handlerList 自定义 grant_type 处理器集合
|
||||||
|
*/
|
||||||
|
@Autowired(required = false)
|
||||||
|
public void setSaOAuth2GrantTypeHandlerInterface(List<SaOAuth2GrantTypeHandlerInterface> handlerList) {
|
||||||
|
for (SaOAuth2GrantTypeHandlerInterface handler : handlerList) {
|
||||||
|
SaOAuth2Strategy.instance.registerGrantTypeHandler(handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user