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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成指定区间的 int 值
|
||||
*
|
||||
* @param min 最小值(包括)
|
||||
* @param max 最大值(包括)
|
||||
* @return /
|
||||
*/
|
||||
public static int getRandomNumber(int min, int max) {
|
||||
return ThreadLocalRandom.current().nextInt(min, max + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定元素是否为null或者空字符串
|
||||
* @param str 指定元素
|
||||
|
@ -1,5 +1,6 @@
|
||||
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.model.loader.SaClientModel;
|
||||
import org.springframework.stereotype.Component;
|
||||
@ -22,10 +23,14 @@ public class SaOAuth2DataLoaderImpl implements SaOAuth2DataLoader {
|
||||
.setClientSecret("aaaa-bbbb-cccc-dddd-eeee") // client 秘钥
|
||||
.addAllowUrls("*") // 所有允许授权的 url
|
||||
.addContractScopes("openid", "userid", "userinfo") // 所有签约的权限
|
||||
.setEnableCode(true) // 是否开启授权码模式
|
||||
.setEnableImplicit(true) // 是否开启隐式模式
|
||||
.setEnablePassword(true) // 是否开启密码模式
|
||||
.setEnableClient(true) // 是否开启客户端模式
|
||||
.addAllowGrantTypes( // 所有允许的授权模式
|
||||
GrantType.authorization_code, // 授权码式
|
||||
GrantType.implicit, // 隐式式
|
||||
GrantType.refresh_token, // 刷新令牌
|
||||
GrantType.password, // 密码式
|
||||
GrantType.client_credentials, // 客户端模式
|
||||
"phone_code" // 自定义授权模式 手机号验证码登录
|
||||
)
|
||||
;
|
||||
}
|
||||
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.ClientTokenModel;
|
@ -5,6 +5,8 @@ server:
|
||||
sa-token:
|
||||
# token名称 (同时也是 Cookie 名称)
|
||||
token-name: satoken-oauth2-server
|
||||
# 是否打印操作日志
|
||||
is-log: true
|
||||
# OAuth2.0 配置
|
||||
oauth2:
|
||||
# 是否全局开启授权码模式
|
||||
|
@ -63,7 +63,7 @@
|
||||
- [自定义 grant_type](/oauth2/oauth2-custom-grant_type)
|
||||
- [OAuth2-与登录会话实现数据互通](/oauth2/oauth2-interworking)
|
||||
- [OAuth2 代码 API 参考](/oauth2/oauth2-dev)
|
||||
- [常见问题说明](/oauth2/8)
|
||||
<!-- - [常见问题说明](/oauth2/8) -->
|
||||
<!-- - [前后端分离模式整合方案](/oauth2/4) -->
|
||||
<!-- - [平台中心模式开发](/oauth2/5) -->
|
||||
- <!-- 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.resolver.SaOAuth2DataResolver;
|
||||
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 模块 总控类
|
||||
@ -144,4 +147,40 @@ public class SaOAuth2Manager {
|
||||
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 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 类型
|
||||
*/
|
||||
|
@ -35,21 +35,21 @@ public interface SaOAuth2DataGenerate {
|
||||
* @param ra 请求参数Model
|
||||
* @return 授权码Model
|
||||
*/
|
||||
public CodeModel generateCode(RequestAuthModel ra);
|
||||
CodeModel generateCode(RequestAuthModel ra);
|
||||
|
||||
/**
|
||||
* 构建Model:Access-Token
|
||||
* @param code 授权码Model
|
||||
* @return AccessToken Model
|
||||
*/
|
||||
public AccessTokenModel generateAccessToken(String code);
|
||||
AccessTokenModel generateAccessToken(String code);
|
||||
|
||||
/**
|
||||
* 刷新Model:根据 Refresh-Token 生成一个新的 Access-Token
|
||||
* @param refreshToken Refresh-Token值
|
||||
* @return 新的 Access-Token
|
||||
*/
|
||||
public AccessTokenModel refreshAccessToken(String refreshToken);
|
||||
AccessTokenModel refreshAccessToken(String refreshToken);
|
||||
|
||||
/**
|
||||
* 构建Model:Access-Token (根据RequestAuthModel构建,用于隐藏式 and 密码式)
|
||||
@ -57,7 +57,7 @@ public interface SaOAuth2DataGenerate {
|
||||
* @param isCreateRt 是否生成对应的Refresh-Token
|
||||
* @return Access-Token Model
|
||||
*/
|
||||
public AccessTokenModel generateAccessToken(RequestAuthModel ra, boolean isCreateRt);
|
||||
AccessTokenModel generateAccessToken(RequestAuthModel ra, boolean isCreateRt);
|
||||
|
||||
/**
|
||||
* 构建Model:Client-Token
|
||||
@ -65,7 +65,7 @@ public interface SaOAuth2DataGenerate {
|
||||
* @param scopes 授权范围
|
||||
* @return Client-Token Model
|
||||
*/
|
||||
public ClientTokenModel generateClientToken(String clientId, List<String> scopes);
|
||||
ClientTokenModel generateClientToken(String clientId, List<String> scopes);
|
||||
|
||||
/**
|
||||
* 构建URL:下放Code URL (Authorization Code 授权码)
|
||||
@ -74,7 +74,7 @@ public interface SaOAuth2DataGenerate {
|
||||
* @param state state参数
|
||||
* @return 构建完毕的URL
|
||||
*/
|
||||
public String buildRedirectUri(String redirectUri, String code, String state);
|
||||
String buildRedirectUri(String redirectUri, String code, String state);
|
||||
|
||||
/**
|
||||
* 构建URL:下放Access-Token URL (implicit 隐藏式)
|
||||
@ -83,14 +83,12 @@ public interface SaOAuth2DataGenerate {
|
||||
* @param state state参数
|
||||
* @return 构建完毕的URL
|
||||
*/
|
||||
public String buildImplicitRedirectUri(String redirectUri, String token, String state);
|
||||
String buildImplicitRedirectUri(String redirectUri, String token, String state);
|
||||
|
||||
/**
|
||||
* 回收 Access-Token
|
||||
* @param accessToken Access-Token值
|
||||
*/
|
||||
public void revokeAccessToken(String accessToken);
|
||||
|
||||
|
||||
void revokeAccessToken(String accessToken);
|
||||
|
||||
}
|
||||
|
@ -52,25 +52,11 @@ public class SaClientModel implements Serializable {
|
||||
* 应用允许授权的所有URL
|
||||
*/
|
||||
public List<String> allowUrls;
|
||||
|
||||
/** 此 Client 是否打开模式:授权码(Authorization Code) */
|
||||
public Boolean enableCode = false;
|
||||
|
||||
/** 此 Client 是否打开模式:隐藏式(Implicit) */
|
||||
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;
|
||||
/**
|
||||
* 应用允许的所有 grant_type
|
||||
*/
|
||||
public List<String> allowGrantTypes = new ArrayList<>();
|
||||
|
||||
/** 单独配置此Client:是否在每次 Refresh-Token 刷新 Access-Token 时,产生一个新的 Refresh-Token [默认取全局配置] */
|
||||
public Boolean isNewRefresh;
|
||||
@ -169,86 +155,22 @@ public class SaClientModel implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 此 Client 是否打开模式:授权码(Authorization Code)
|
||||
* @return 应用允许的所有 grant_type
|
||||
*/
|
||||
public Boolean getEnableCode() {
|
||||
return enableCode;
|
||||
public List<String> getAllowGrantTypes() {
|
||||
return allowGrantTypes;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param enableCode 此 Client 是否打开模式:授权码(Authorization Code)
|
||||
* @return 对象自身
|
||||
* 应用允许的所有 grant_type
|
||||
* @param allowGrantTypes /
|
||||
* @return /
|
||||
*/
|
||||
public SaClientModel setEnableCode(Boolean enableCode) {
|
||||
this.enableCode = enableCode;
|
||||
public SaClientModel setAllowGrantTypes(List<String> allowGrantTypes) {
|
||||
this.allowGrantTypes = allowGrantTypes;
|
||||
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 [默认取全局配置]
|
||||
*/
|
||||
@ -338,11 +260,7 @@ public class SaClientModel implements Serializable {
|
||||
", clientSecret='" + clientSecret + '\'' +
|
||||
", contractScopes=" + contractScopes +
|
||||
", allowUrls=" + allowUrls +
|
||||
", isCode=" + enableCode +
|
||||
", isImplicit=" + enableImplicit +
|
||||
", isPassword=" + enablePassword +
|
||||
", isClient=" + enableClient +
|
||||
// ", isAutoMode=" + isAutoMode +
|
||||
", allowGrantTypes=" + allowGrantTypes +
|
||||
", isNewRefresh=" + isNewRefresh +
|
||||
", accessTokenTimeout=" + accessTokenTimeout +
|
||||
", refreshTokenTimeout=" + refreshTokenTimeout +
|
||||
@ -367,7 +285,7 @@ public class SaClientModel implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param urls 添加应用签约的所有权限
|
||||
* @param urls 添加应用允许授权的所有URL
|
||||
* @return 对象自身
|
||||
*/
|
||||
public SaClientModel addAllowUrls(String... urls) {
|
||||
@ -378,5 +296,17 @@ public class SaClientModel implements Serializable {
|
||||
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.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.Api;
|
||||
import cn.dev33.satoken.oauth2.consts.SaOAuth2Consts.GrantType;
|
||||
import cn.dev33.satoken.oauth2.consts.SaOAuth2Consts.Param;
|
||||
import cn.dev33.satoken.oauth2.consts.SaOAuth2Consts.ResponseType;
|
||||
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.error.SaOAuth2ErrorCode;
|
||||
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.router.SaHttpMethod;
|
||||
import cn.dev33.satoken.stp.StpLogic;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
|
||||
import java.util.List;
|
||||
@ -55,11 +54,6 @@ public class SaOAuth2ServerProcessor {
|
||||
*/
|
||||
public static SaOAuth2ServerProcessor instance = new SaOAuth2ServerProcessor();
|
||||
|
||||
/**
|
||||
* 底层 SaOAuth2Template 对象
|
||||
*/
|
||||
public SaOAuth2Template oauth2Template = new SaOAuth2Template();
|
||||
|
||||
/**
|
||||
* 处理 Server 端请求, 路由分发
|
||||
* @return 处理结果
|
||||
@ -68,7 +62,6 @@ public class SaOAuth2ServerProcessor {
|
||||
|
||||
// 获取变量
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
SaOAuth2Config cfg = SaOAuth2Manager.getConfig();
|
||||
|
||||
// ------------------ 路由分发 ------------------
|
||||
|
||||
@ -79,7 +72,7 @@ public class SaOAuth2ServerProcessor {
|
||||
|
||||
// Code 换 Access-Token || 模式三:密码式
|
||||
if(req.isPath(Api.token)) {
|
||||
return tokenOrPassword();
|
||||
return token();
|
||||
}
|
||||
|
||||
// Refresh-Token 刷新 Access-Token
|
||||
@ -122,18 +115,19 @@ public class SaOAuth2ServerProcessor {
|
||||
SaResponse res = SaHolder.getResponse();
|
||||
SaOAuth2Config cfg = SaOAuth2Manager.getConfig();
|
||||
SaOAuth2DataGenerate dataGenerate = SaOAuth2Manager.getDataGenerate();
|
||||
SaOAuth2Template oauth2Template = SaOAuth2Manager.getTemplate();
|
||||
String responseType = req.getParamNotNull(Param.response_type);
|
||||
|
||||
// 1、先判断是否开启了指定的授权模式
|
||||
checkAuthorizeResponseType(responseType, req, cfg);
|
||||
|
||||
// 2、如果尚未登录, 则先去登录
|
||||
if( ! getStpLogic().isLogin()) {
|
||||
if( ! SaOAuth2Manager.getStpLogic().isLogin()) {
|
||||
return cfg.notLoginView.get();
|
||||
}
|
||||
|
||||
// 3、构建请求 Model
|
||||
RequestAuthModel ra = SaOAuth2Manager.getDataResolver().readRequestAuthModel(req, getStpLogic().getLoginId());
|
||||
RequestAuthModel ra = SaOAuth2Manager.getDataResolver().readRequestAuthModel(req, SaOAuth2Manager.getStpLogic().getLoginId());
|
||||
|
||||
// 4、校验:重定向域名是否合法
|
||||
oauth2Template.checkRightUrl(ra.clientId, ra.redirectUri);
|
||||
@ -167,55 +161,11 @@ public class SaOAuth2ServerProcessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Code 换 Access-Token || 模式三:密码式
|
||||
* @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
|
||||
* Code 换 Access-Token / 模式三:密码式 / 自定义 grant_type
|
||||
* @return 处理结果
|
||||
*/
|
||||
public Object token() {
|
||||
// 获取变量
|
||||
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);
|
||||
|
||||
// 返回
|
||||
AccessTokenModel accessTokenModel = SaOAuth2Strategy.instance.grantTypeAuth.apply(SaHolder.getRequest());
|
||||
return SaOAuth2Manager.getDataResolver().buildTokenReturnValue(accessTokenModel);
|
||||
}
|
||||
|
||||
@ -224,27 +174,10 @@ public class SaOAuth2ServerProcessor {
|
||||
* @return 处理结果
|
||||
*/
|
||||
public Object refresh() {
|
||||
// 获取变量
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
String grantType = req.getParamNotNull(Param.grant_type);
|
||||
if(!grantType.equals(GrantType.refresh_token)) {
|
||||
throw new SaOAuth2Exception("无效 grant_type:" + grantType).setCode(SaOAuth2ErrorCode.CODE_30126);
|
||||
}
|
||||
|
||||
// 获取参数
|
||||
|
||||
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);
|
||||
|
||||
// 返回
|
||||
SaOAuth2Exception.throwBy(!grantType.equals(GrantType.refresh_token), "无效 grant_type:" + grantType, SaOAuth2ErrorCode.CODE_30126);
|
||||
AccessTokenModel accessTokenModel = SaOAuth2Strategy.instance.grantTypeAuth.apply(req);
|
||||
return SaOAuth2Manager.getDataResolver().buildRefreshTokenReturnValue(accessTokenModel);
|
||||
}
|
||||
|
||||
@ -254,6 +187,7 @@ public class SaOAuth2ServerProcessor {
|
||||
*/
|
||||
public Object revoke() {
|
||||
// 获取变量
|
||||
SaOAuth2Template oauth2Template = SaOAuth2Manager.getTemplate();
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
|
||||
// 获取参数
|
||||
@ -297,10 +231,11 @@ public class SaOAuth2ServerProcessor {
|
||||
// 获取变量
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
String clientId = req.getParamNotNull(Param.client_id);
|
||||
Object loginId = getStpLogic().getLoginId();
|
||||
Object loginId = SaOAuth2Manager.getStpLogic().getLoginId();
|
||||
String scope = req.getParamNotNull(Param.scope);
|
||||
List<String> scopes = SaOAuth2Manager.getDataConverter().convertScopeStringToList(scope);
|
||||
SaOAuth2DataGenerate dataGenerate = SaOAuth2Manager.getDataGenerate();
|
||||
SaOAuth2Template oauth2Template = SaOAuth2Manager.getTemplate();
|
||||
|
||||
// 此请求只允许 POST 方式
|
||||
if(!req.isMethod(SaHttpMethod.POST)) {
|
||||
@ -343,49 +278,6 @@ public class SaOAuth2ServerProcessor {
|
||||
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 处理结果
|
||||
@ -394,6 +286,7 @@ public class SaOAuth2ServerProcessor {
|
||||
// 获取变量
|
||||
SaRequest req = SaHolder.getRequest();
|
||||
SaOAuth2Config cfg = SaOAuth2Manager.getConfig();
|
||||
SaOAuth2Template oauth2Template = SaOAuth2Manager.getTemplate();
|
||||
|
||||
String grantType = req.getParamNotNull(Param.grant_type);
|
||||
if(!grantType.equals(GrantType.client_credentials)) {
|
||||
@ -402,7 +295,7 @@ public class SaOAuth2ServerProcessor {
|
||||
if(!cfg.enableClient) {
|
||||
throwErrorSystemNotEnableModel();
|
||||
}
|
||||
if(!currClientModel().enableClient) {
|
||||
if(!currClientModel().getAllowGrantTypes().contains(GrantType.client_credentials)) {
|
||||
throwErrorClientNotEnableModel();
|
||||
}
|
||||
|
||||
@ -434,6 +327,7 @@ public class SaOAuth2ServerProcessor {
|
||||
* @return /
|
||||
*/
|
||||
public SaClientModel currClientModel() {
|
||||
SaOAuth2Template oauth2Template = SaOAuth2Manager.getTemplate();
|
||||
ClientIdAndSecretModel clientIdAndSecret = SaOAuth2Manager.getDataResolver().readClientIdAndSecret(SaHolder.getRequest());
|
||||
return oauth2Template.checkClientModel(clientIdAndSecret.clientId);
|
||||
}
|
||||
@ -447,7 +341,7 @@ public class SaOAuth2ServerProcessor {
|
||||
if(!cfg.enableCode) {
|
||||
throwErrorSystemNotEnableModel();
|
||||
}
|
||||
if(!currClientModel().enableCode) {
|
||||
if(!currClientModel().getAllowGrantTypes().contains(GrantType.authorization_code)) {
|
||||
throwErrorClientNotEnableModel();
|
||||
}
|
||||
}
|
||||
@ -456,7 +350,7 @@ public class SaOAuth2ServerProcessor {
|
||||
if(!cfg.enableImplicit) {
|
||||
throwErrorSystemNotEnableModel();
|
||||
}
|
||||
if(!currClientModel().enableImplicit) {
|
||||
if(!currClientModel().getAllowGrantTypes().contains(GrantType.implicit)) {
|
||||
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;
|
||||
|
||||
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.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.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.handler.OidcScopeHandler;
|
||||
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 java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@ -38,6 +49,7 @@ public final class SaOAuth2Strategy {
|
||||
|
||||
private SaOAuth2Strategy() {
|
||||
registerDefaultScopeHandler();
|
||||
registerDefaultGrantTypeHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -78,9 +90,6 @@ public final class SaOAuth2Strategy {
|
||||
scopeHandlerMap.remove(scope);
|
||||
}
|
||||
|
||||
|
||||
// ----------------------- 所有策略
|
||||
|
||||
/**
|
||||
* 根据 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
|
||||
*/
|
||||
|
@ -41,7 +41,7 @@ public class SaOAuth2Util {
|
||||
* @return ClientModel
|
||||
*/
|
||||
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 .
|
||||
*/
|
||||
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 .
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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 需要校验的权限列表
|
||||
*/
|
||||
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 需要校验的权限列表
|
||||
*/
|
||||
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 是否已经授权
|
||||
*/
|
||||
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 权限(多个用逗号隔开)
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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对象
|
||||
*/
|
||||
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对象
|
||||
*/
|
||||
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对象
|
||||
*/
|
||||
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对象
|
||||
*/
|
||||
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对象
|
||||
*/
|
||||
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 数据
|
||||
@ -185,7 +185,7 @@ public class SaOAuth2Util {
|
||||
* @param 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.loader.SaOAuth2DataLoader;
|
||||
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.scope.handler.SaOAuth2ScopeHandlerInterface;
|
||||
import cn.dev33.satoken.oauth2.strategy.SaOAuth2Strategy;
|
||||
@ -62,7 +63,17 @@ public class SaOAuth2BeanInject {
|
||||
*/
|
||||
@Autowired(required = false)
|
||||
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