完整适配拆分式路由写法

This commit is contained in:
click33
2024-08-19 23:29:05 +08:00
parent 25b24414ff
commit 174a94db01
13 changed files with 335 additions and 160 deletions

View File

@@ -34,16 +34,16 @@ public class SaOAuth2Config implements Serializable {
private static final long serialVersionUID = -6541180061782004705L;
/** 是否打开模式授权码Authorization Code */
public Boolean isCode = true;
public Boolean enableCode = true;
/** 是否打开模式隐藏式Implicit */
public Boolean isImplicit = true;
public Boolean enableImplicit = true;
/** 是否打开模式密码式Password */
public Boolean isPassword = true;
public Boolean enablePassword = true;
/** 是否打开模式凭证式Client Credentials */
public Boolean isClient = true;
public Boolean enableClient = true;
/** 是否在每次 Refresh-Token 刷新 Access-Token 时,产生一个新的 Refresh-Token */
public Boolean isNewRefresh = false;
@@ -69,59 +69,59 @@ public class SaOAuth2Config implements Serializable {
/**
* @return isCode
* @return enableCode
*/
public Boolean getIsCode() {
return isCode;
public Boolean getEnableCode() {
return enableCode;
}
/**
* @param isCode 要设置的 isCode
* @param enableCode 要设置的 enableCode
*/
public void setIsCode(Boolean isCode) {
this.isCode = isCode;
public void setEnableCode(Boolean enableCode) {
this.enableCode = enableCode;
}
/**
* @return isImplicit
* @return enableImplicit
*/
public Boolean getIsImplicit() {
return isImplicit;
public Boolean getEnableImplicit() {
return enableImplicit;
}
/**
* @param isImplicit 要设置的 isImplicit
* @param enableImplicit 要设置的 enableImplicit
*/
public void setIsImplicit(Boolean isImplicit) {
this.isImplicit = isImplicit;
public void setEnableImplicit(Boolean enableImplicit) {
this.enableImplicit = enableImplicit;
}
/**
* @return isPassword
* @return enablePassword
*/
public Boolean getIsPassword() {
return isPassword;
public Boolean getEnablePassword() {
return enablePassword;
}
/**
* @param isPassword 要设置的 isPassword
* @param enablePassword 要设置的 enablePassword
*/
public void setIsPassword(Boolean isPassword) {
this.isPassword = isPassword;
public void setEnablePassword(Boolean enablePassword) {
this.enablePassword = enablePassword;
}
/**
* @return isClient
* @return enableClient
*/
public Boolean getIsClient() {
return isClient;
public Boolean getEnableClient() {
return enableClient;
}
/**
* @param isClient 要设置的 isClient
* @param enableClient 要设置的 enableClient
*/
public void setIsClient(Boolean isClient) {
this.isClient = isClient;
public void setEnableClient(Boolean enableClient) {
this.enableClient = enableClient;
}
/**
@@ -254,8 +254,11 @@ public class SaOAuth2Config implements Serializable {
@Override
public String toString() {
return "SaOAuth2Config [isCode=" + isCode + ", isImplicit=" + isImplicit + ", isPassword=" + isPassword
+ ", isClient=" + isClient
return "SaOAuth2Config [" +
"enableCode=" + enableCode
+ ", enableImplicit=" + enableImplicit
+ ", enablePassword=" + enablePassword
+ ", enableClient=" + enableClient
+ ", isNewRefresh=" + isNewRefresh
+ ", codeTimeout=" + codeTimeout
+ ", accessTokenTimeout=" + accessTokenTimeout

View File

@@ -100,8 +100,8 @@ public class SaOAuth2DataConverterDefaultImpl implements SaOAuth2DataConverter {
rt.expiresTime = System.currentTimeMillis() + (clientModel.getRefreshTokenTimeout() * 1000);
rt.extraData = new LinkedHashMap<>(at.extraData);
// 改变 at 属性
at.refreshToken = rt.refreshToken;
at.refreshExpiresTime = rt.expiresTime;
// at.refreshToken = rt.refreshToken;
// at.refreshExpiresTime = rt.expiresTime;
return rt;
}

View File

@@ -87,10 +87,10 @@ public class SaOAuth2DataGenerateDefaultImpl implements SaOAuth2DataGenerate {
// 3、生成token
AccessTokenModel at = dataConverter.convertCodeToAccessToken(cm);
SaOAuth2Strategy.instance.workAccessTokenByScope.accept(at);
RefreshTokenModel rt = dataConverter.convertAccessTokenToRefreshToken(at);
at.refreshToken = rt.refreshToken;
at.refreshExpiresTime = rt.expiresTime;
SaOAuth2Strategy.instance.workAccessTokenByScope.accept(at);
// 4、保存token
dao.saveAccessToken(at);
@@ -166,14 +166,20 @@ public class SaOAuth2DataGenerateDefaultImpl implements SaOAuth2DataGenerate {
// 2、生成 新Access-Token
String newAtValue = SaOAuth2Strategy.instance.createAccessToken.execute(ra.clientId, ra.loginId, ra.scopes);
AccessTokenModel at = new AccessTokenModel(newAtValue, ra.clientId, ra.loginId, ra.scopes);
// TODO 此处的 openid 应该怎么加载?
// at.openid = SaOAuth2Manager.getDataLoader().getOpenid(ra.clientId, ra.loginId);
// 3、根据权限构建额外参数
at.extraData = new LinkedHashMap<>();
SaOAuth2Strategy.instance.workAccessTokenByScope.accept(at);
SaClientModel clientModel = SaOAuth2Manager.getDataLoader().getClientModelNotNull(ra.clientId);
at.expiresTime = System.currentTimeMillis() + (clientModel.getAccessTokenTimeout() * 1000);
// 3、生成&保存 Refresh-Token
if(isCreateRt) {
RefreshTokenModel rt = SaOAuth2Manager.getDataConverter().convertAccessTokenToRefreshToken(at);
at.refreshToken = rt.refreshToken;
at.refreshExpiresTime = rt.expiresTime;
dao.saveRefreshToken(rt);
dao.saveRefreshTokenIndex(rt);
}

View File

@@ -54,23 +54,23 @@ public class SaClientModel implements Serializable {
public List<String> allowUrls;
/** 此 Client 是否打开模式授权码Authorization Code */
public Boolean isCode = false;
public Boolean enableCode = false;
/** 此 Client 是否打开模式隐藏式Implicit */
public Boolean isImplicit = false;
public Boolean enableImplicit = false;
/** 此 Client 是否打开模式密码式Password */
public Boolean isPassword = false;
public Boolean enablePassword = false;
/** 此 Client 是否打开模式凭证式Client Credentials */
public Boolean isClient = false;
public Boolean enableClient = false;
/**
* 是否自动判断此 Client 开放的授权模式
* <br> 此值为true时四种模式isCode、isImplicit、isPassword、isClient是否生效依靠全局设置
* <br> 此值为false时四种模式isCode、isImplicit、isPassword、isClient是否生效依靠局部配置+全局配置
*/
public Boolean isAutoMode = true;
// /**
// * 是否自动判断此 Client 开放的授权模式
// * <br> 此值为true时四种模式isCode、isImplicit、isPassword、isClient是否生效依靠全局设置
// * <br> 此值为false时四种模式isCode、isImplicit、isPassword、isClient是否生效依靠局部配置+全局配置
// */
// public Boolean isAutoMode = true;
/** 单独配置此Client是否在每次 Refresh-Token 刷新 Access-Token 时,产生一个新的 Refresh-Token [默认取全局配置] */
public Boolean isNewRefresh;
@@ -171,83 +171,83 @@ public class SaClientModel implements Serializable {
/**
* @return 此 Client 是否打开模式授权码Authorization Code
*/
public Boolean getIsCode() {
return isCode;
public Boolean getEnableCode() {
return enableCode;
}
/**
* @param isCode 此 Client 是否打开模式授权码Authorization Code
* @param enableCode 此 Client 是否打开模式授权码Authorization Code
* @return 对象自身
*/
public SaClientModel setIsCode(Boolean isCode) {
this.isCode = isCode;
public SaClientModel setEnableCode(Boolean enableCode) {
this.enableCode = enableCode;
return this;
}
/**
* @return 此 Client 是否打开模式隐藏式Implicit
*/
public Boolean getIsImplicit() {
return isImplicit;
public Boolean getEnableImplicit() {
return enableImplicit;
}
/**
* @param isImplicit 此 Client 是否打开模式隐藏式Implicit
* @param enableImplicit 此 Client 是否打开模式隐藏式Implicit
* @return 对象自身
*/
public SaClientModel setIsImplicit(Boolean isImplicit) {
this.isImplicit = isImplicit;
public SaClientModel setEnableImplicit(Boolean enableImplicit) {
this.enableImplicit = enableImplicit;
return this;
}
/**
* @return 此 Client 是否打开模式密码式Password
*/
public Boolean getIsPassword() {
return isPassword;
public Boolean getEnablePassword() {
return enablePassword;
}
/**
* @param isPassword 此 Client 是否打开模式密码式Password
* @param enablePassword 此 Client 是否打开模式密码式Password
* @return 对象自身
*/
public SaClientModel setIsPassword(Boolean isPassword) {
this.isPassword = isPassword;
public SaClientModel setEnablePassword(Boolean enablePassword) {
this.enablePassword = enablePassword;
return this;
}
/**
* @return 此 Client 是否打开模式凭证式Client Credentials
*/
public Boolean getIsClient() {
return isClient;
public Boolean getEnableClient() {
return enableClient;
}
/**
* @param isClient 此 Client 是否打开模式凭证式Client Credentials
* @param enableClient 此 Client 是否打开模式凭证式Client Credentials
* @return 对象自身
*/
public SaClientModel setIsClient(Boolean isClient) {
this.isClient = isClient;
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 开放的授权模式
// */
// 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 +338,11 @@ public class SaClientModel implements Serializable {
", clientSecret='" + clientSecret + '\'' +
", contractScopes=" + contractScopes +
", allowUrls=" + allowUrls +
", isCode=" + isCode +
", isImplicit=" + isImplicit +
", isPassword=" + isPassword +
", isClient=" + isClient +
", isAutoMode=" + isAutoMode +
", isCode=" + enableCode +
", isImplicit=" + enableImplicit +
", isPassword=" + enablePassword +
", isClient=" + enableClient +
// ", isAutoMode=" + isAutoMode +
", isNewRefresh=" + isNewRefresh +
", accessTokenTimeout=" + accessTokenTimeout +
", refreshTokenTimeout=" + refreshTokenTimeout +

View File

@@ -113,7 +113,7 @@ public class SaOAuth2DataResolverDefaultImpl implements SaOAuth2DataResolver {
public Map<String, Object> buildClientTokenReturnValue(ClientTokenModel ct) {
Map<String, Object> map = new LinkedHashMap<>();
map.put("client_token", ct.clientToken);
map.put("access_token", ct.clientToken); // 兼容 OAuth2 协议
// map.put("access_token", ct.clientToken); // 兼容 OAuth2 协议
map.put("expires_in", ct.getExpiresIn());
map.put("client_id", ct.clientId);
map.put("scope", SaOAuth2Manager.getDataConverter().convertScopeListToString(ct.scopes));

View File

@@ -98,6 +98,9 @@ public interface SaOAuth2ErrorCode {
/** 无效response_type */
int CODE_30125 = 30125;
/** 无效grant_type */
int CODE_30126 = 30126;
/** 暂未开放授权码模式 */
int CODE_30131 = 30131;

View File

@@ -71,28 +71,24 @@ public class SaOAuth2ServerProcessor {
// ------------------ 路由分发 ------------------
// 模式一Code授权码
if(req.isPath(Api.authorize) && req.isParam(Param.response_type, ResponseType.code)) {
SaClientModel cm = currClientModel();
if(cfg.getIsCode() && (cm.isCode || cm.isAutoMode)) {
return authorize();
}
throw new SaOAuth2Exception("暂未开放的授权模式").setCode(SaOAuth2ErrorCode.CODE_30131);
// 模式一Code授权码 || 模式二:隐藏式
if(req.isPath(Api.authorize)) {
return authorize();
}
// Code授权码 获取 Access-Token
if(req.isPath(Api.token) && req.isParam(Param.grant_type, GrantType.authorization_code)) {
return token();
// Code Access-Token || 模式三:密码式
if(req.isPath(Api.token)) {
return tokenOrPassword();
}
// Refresh-Token 刷新 Access-Token
if(req.isPath(Api.refresh) && req.isParam(Param.grant_type, GrantType.refresh_token)) {
return refreshToken();
if(req.isPath(Api.refresh)) {
return refresh();
}
// 回收 Access-Token
if(req.isPath(Api.revoke)) {
return revokeToken();
return revoke();
}
// doLogin 登录接口
@@ -105,31 +101,9 @@ public class SaOAuth2ServerProcessor {
return doConfirm();
}
// 模式二:隐藏式
if(req.isPath(Api.authorize) && req.isParam(Param.response_type, ResponseType.token)) {
SaClientModel cm = currClientModel();
if(cfg.getIsImplicit() && (cm.isImplicit || cm.isAutoMode)) {
return authorize();
}
throw new SaOAuth2Exception("暂未开放的授权模式").setCode(SaOAuth2ErrorCode.CODE_30132);
}
// 模式三:密码式
if(req.isPath(Api.token) && req.isParam(Param.grant_type, GrantType.password)) {
SaClientModel cm = currClientModel();
if(cfg.getIsPassword() && (cm.isPassword || cm.isAutoMode)) {
return password();
}
throw new SaOAuth2Exception("暂未开放的授权模式").setCode(SaOAuth2ErrorCode.CODE_30133);
}
// 模式四:凭证式
if(req.isPath(Api.client_token) && req.isParam(Param.grant_type, GrantType.client_credentials)) {
SaClientModel cm = currClientModel();
if(cfg.getIsClient() && (cm.isClient || cm.isAutoMode)) {
return clientToken();
}
throw new SaOAuth2Exception("暂未开放的授权模式").setCode(SaOAuth2ErrorCode.CODE_30134);
if(req.isPath(Api.client_token)) {
return clientToken();
}
// 默认返回
@@ -141,42 +115,67 @@ public class SaOAuth2ServerProcessor {
* @return 处理结果
*/
public Object authorize() {
// 获取变量
SaRequest req = SaHolder.getRequest();
SaResponse res = SaHolder.getResponse();
SaOAuth2Config cfg = SaOAuth2Manager.getConfig();
SaOAuth2DataGenerate dataGenerate = SaOAuth2Manager.getDataGenerate();
String responseType = req.getParamNotNull(Param.response_type);
// 1、如果尚未登录, 则先去登录
// 1、先判断是否开启了指定的授权模式
// 模式一Code授权码
if(responseType.equals(ResponseType.code)) {
if(!cfg.enableCode) {
throwErrorSystemNotEnableModel();
}
if(!currClientModel().enableCode) {
throwErrorClientNotEnableModel();
}
}
// 模式二:隐藏式
else if(responseType.equals(ResponseType.token)) {
if(!cfg.enableImplicit) {
throwErrorSystemNotEnableModel();
}
if(!currClientModel().enableImplicit) {
throwErrorClientNotEnableModel();
}
}
// 其它
else {
throw new SaOAuth2Exception("无效 response_type: " + req.getParam(Param.response_type)).setCode(SaOAuth2ErrorCode.CODE_30125);
}
// 2、如果尚未登录, 则先去登录
if( ! getStpLogic().isLogin()) {
return cfg.notLoginView.get();
}
// 2、构建请求 Model
// 3、构建请求 Model
RequestAuthModel ra = SaOAuth2Manager.getDataResolver().readRequestAuthModel(req, getStpLogic().getLoginId());
// 3、校验:重定向域名是否合法
// 4、校验:重定向域名是否合法
oauth2Template.checkRightUrl(ra.clientId, ra.redirectUri);
// 4、校验此次申请的Scope该Client是否已经签约
// 5、校验此次申请的Scope该Client是否已经签约
oauth2Template.checkContract(ra.clientId, ra.scopes);
// 5、判断如果此次申请的Scope该用户尚未授权则转到授权页面
// 6、判断如果此次申请的Scope该用户尚未授权则转到授权页面
boolean isGrant = oauth2Template.isGrant(ra.loginId, ra.clientId, ra.scopes);
if( ! isGrant) {
return cfg.confirmView.apply(ra.clientId, ra.scopes);
}
// 6、判断授权类型
// 如果是 授权码式开始重定向授权下放code
// 7、判断授权类型重定向到不同地址
// 如果是 授权码式开始重定向授权下放code
if(ResponseType.code.equals(ra.responseType)) {
CodeModel codeModel = dataGenerate.generateCode(ra);
String redirectUri = dataGenerate.buildRedirectUri(ra.redirectUri, codeModel.code, ra.state);
return res.redirect(redirectUri);
}
// 如果是 隐藏式,则:开始重定向授权,下放 token
// 如果是 隐藏式,则:开始重定向授权,下放 token
if(ResponseType.token.equals(ra.responseType)) {
AccessTokenModel at = dataGenerate.generateAccessToken(ra, false);
String redirectUri = dataGenerate.buildImplicitRedirectUri(ra.redirectUri, at.accessToken, ra.state);
@@ -187,6 +186,34 @@ public class SaOAuth2ServerProcessor {
throw new SaOAuth2Exception("无效response_type: " + ra.responseType).setCode(SaOAuth2ErrorCode.CODE_30125);
}
/**
* 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
* @return 处理结果
@@ -216,9 +243,13 @@ public class SaOAuth2ServerProcessor {
* Refresh-Token 刷新 Access-Token
* @return 处理结果
*/
public Object refreshToken() {
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);
}
// 获取参数
@@ -241,7 +272,7 @@ public class SaOAuth2ServerProcessor {
* 回收 Access-Token
* @return 处理结果
*/
public Object revokeToken() {
public Object revoke() {
// 获取变量
SaRequest req = SaHolder.getRequest();
@@ -344,6 +375,18 @@ public class SaOAuth2ServerProcessor {
public Object clientToken() {
// 获取变量
SaRequest req = SaHolder.getRequest();
SaOAuth2Config cfg = SaOAuth2Manager.getConfig();
String grantType = req.getParamNotNull(Param.grant_type);
if(!grantType.equals(GrantType.client_credentials)) {
throw new SaOAuth2Exception("无效 grant_type" + grantType).setCode(SaOAuth2ErrorCode.CODE_30126);
}
if(!cfg.enableClient) {
throwErrorSystemNotEnableModel();
}
if(!currClientModel().enableClient) {
throwErrorClientNotEnableModel();
}
// 获取参数
ClientIdAndSecretModel clientIdAndSecret = SaOAuth2Manager.getDataResolver().readClientIdAndSecret(req);
@@ -383,4 +426,18 @@ public class SaOAuth2ServerProcessor {
return StpUtil.stpLogic;
}
/**
* 系统未开放此授权模式时抛出异常
*/
public void throwErrorSystemNotEnableModel() {
throw new SaOAuth2Exception("系统暂未开放此授权模式").setCode(SaOAuth2ErrorCode.CODE_30131);
}
/**
* 应用未开放此授权模式时抛出异常
*/
public void throwErrorClientNotEnableModel() {
throw new SaOAuth2Exception("应用暂未开放此授权模式").setCode(SaOAuth2ErrorCode.CODE_30131);
}
}