重构 sa-token-oauth2:提取数据加载器 Bean,提供更方便的自定义数据加载方案。

This commit is contained in:
click33 2024-08-12 07:05:43 +08:00
parent 101a577ded
commit eeed140424
14 changed files with 547 additions and 442 deletions

View File

@ -60,11 +60,11 @@
</dependency>
<!-- 热刷新 -->
<dependency>
<!--<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>provided</scope>
</dependency>
</dependency>-->
<!-- ConfigurationProperties -->
<dependency>

View File

@ -1,18 +1,18 @@
package com.pj.oauth2;
import cn.dev33.satoken.oauth2.dataloader.SaOAuth2DataLoader;
import cn.dev33.satoken.oauth2.model.SaClientModel;
import org.springframework.stereotype.Component;
import cn.dev33.satoken.oauth2.logic.SaOAuth2Template;
import cn.dev33.satoken.oauth2.model.SaClientModel;
/**
* Sa-Token OAuth2.0 整合实现
* Sa-Token OAuth2自定义数据加载器
*
* @author click33
*/
@Component
public class SaOAuth2TemplateImpl extends SaOAuth2Template {
public class SaOAuth2DataLoaderImpl implements SaOAuth2DataLoader {
// 根据 id 获取 Client 信息
// 根据 client_id 获取 Client 信息
@Override
public SaClientModel getClientModel(String clientId) {
// 此为模拟数据真实环境需要从数据库查询
@ -33,7 +33,5 @@ public class SaOAuth2TemplateImpl extends SaOAuth2Template {
// 此为模拟数据真实环境需要从数据库查询
return "gr_SwoIN0MC1ewxHX_vfCW3BothWDZMMtx__";
}
// -------------- 其它需要重写的函数
}

View File

@ -1,21 +1,20 @@
package com.pj.oauth2;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.oauth2.config.SaOAuth2Config;
import cn.dev33.satoken.oauth2.processor.SaOAuth2ServerProcessor;
import cn.dev33.satoken.oauth2.template.SaOAuth2Util;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.oauth2.config.SaOAuth2Config;
import cn.dev33.satoken.oauth2.logic.SaOAuth2Handle;
import cn.dev33.satoken.oauth2.logic.SaOAuth2Util;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Sa-OAuth2 Server端 控制器
@ -29,7 +28,7 @@ public class SaOAuth2ServerController {
@RequestMapping("/oauth2/*")
public Object request() {
System.out.println("------- 进入请求: " + SaHolder.getRequest().getUrl());
return SaOAuth2Handle.serverRequest();
return SaOAuth2ServerProcessor.instance.dister();
}
// Sa-OAuth2 定制化配置

View File

@ -16,6 +16,8 @@
package cn.dev33.satoken.oauth2;
import cn.dev33.satoken.oauth2.config.SaOAuth2Config;
import cn.dev33.satoken.oauth2.dataloader.SaOAuth2DataLoader;
import cn.dev33.satoken.oauth2.dataloader.SaOAuth2DataLoaderDefaultImpl;
/**
* Sa-Token-OAuth2 模块 总控类
@ -44,4 +46,22 @@ public class SaOAuth2Manager {
SaOAuth2Manager.config = config;
}
/**
* OAuth2 数据加载器 Bean
*/
private static volatile SaOAuth2DataLoader dataLoader;
public static SaOAuth2DataLoader getDataLoader() {
if (dataLoader == null) {
synchronized (SaOAuth2Manager.class) {
if (dataLoader == null) {
setDataLoader(new SaOAuth2DataLoaderDefaultImpl());
}
}
}
return dataLoader;
}
public static void setDataLoader(SaOAuth2DataLoader dataLoader) {
SaOAuth2Manager.dataLoader = dataLoader;
}
}

View File

@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.dev33.satoken.oauth2.logic;
package cn.dev33.satoken.oauth2.consts;
/**
* Sa-Token-OAuth2 所有常量
* Sa-Token OAuth2 所有常量
*
* @author click33
* @since 1.23.0

View File

@ -0,0 +1,51 @@
/*
* 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.dataloader;
import cn.dev33.satoken.oauth2.model.SaClientModel;
/**
* Sa-Token OAuth2 数据加载器
*
* @author click33
* @since 1.39.0
*/
public interface SaOAuth2DataLoader {
/**
* 根据 id 获取 Client 信息
*
* @param clientId 应用id
* @return ClientModel
*/
default SaClientModel getClientModel(String clientId) {
return null;
}
/**
* 根据ClientId LoginId 获取openid
*
* @param clientId 应用id
* @param loginId 账号id
* @return 此账号在此Client下的openid
*/
default String getOpenid(String clientId, Object loginId) {
return null;
}
}

View File

@ -0,0 +1,28 @@
/*
* 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.dataloader;
/**
* Sa-Token OAuth2 数据加载器 默认实现类
*
* @author click33
* @since 1.39.0
*/
public class SaOAuth2DataLoaderDefaultImpl implements SaOAuth2DataLoader{
// be empty of
}

View File

@ -1,348 +1,360 @@
/*
* 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.logic;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.context.model.SaRequest;
import cn.dev33.satoken.context.model.SaResponse;
import cn.dev33.satoken.httpauth.basic.SaHttpBasicUtil;
import cn.dev33.satoken.oauth2.SaOAuth2Manager;
import cn.dev33.satoken.oauth2.config.SaOAuth2Config;
import cn.dev33.satoken.oauth2.error.SaOAuth2ErrorCode;
import cn.dev33.satoken.oauth2.exception.SaOAuth2Exception;
import cn.dev33.satoken.oauth2.logic.SaOAuth2Consts.Api;
import cn.dev33.satoken.oauth2.logic.SaOAuth2Consts.GrantType;
import cn.dev33.satoken.oauth2.logic.SaOAuth2Consts.Param;
import cn.dev33.satoken.oauth2.logic.SaOAuth2Consts.ResponseType;
import cn.dev33.satoken.oauth2.model.*;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaFoxUtil;
import cn.dev33.satoken.util.SaResult;
/**
* Sa-Token-OAuth2 请求处理类封装
*
* @author click33
* @since 1.23.0
*/
public class SaOAuth2Handle {
/**
* 处理Server端请求 路由分发
* @return 处理结果
*/
public static Object serverRequest() {
// 获取变量
SaRequest req = SaHolder.getRequest();
SaResponse res = SaHolder.getResponse();
SaOAuth2Config cfg = SaOAuth2Manager.getConfig();
// ------------------ 路由分发 ------------------
// 模式一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(req, res, cfg);
}
throw new SaOAuth2Exception("暂未开放的授权模式").setCode(SaOAuth2ErrorCode.CODE_30131);
}
// Code授权码 获取 Access-Token
if(req.isPath(Api.token) && req.isParam(Param.grant_type, GrantType.authorization_code)) {
return token(req, res, cfg);
}
// Refresh-Token 刷新 Access-Token
if(req.isPath(Api.refresh) && req.isParam(Param.grant_type, GrantType.refresh_token)) {
return refreshToken(req);
}
// 回收 Access-Token
if(req.isPath(Api.revoke)) {
return revokeToken(req);
}
// doLogin 登录接口
if(req.isPath(Api.doLogin)) {
return doLogin(req, res, cfg);
}
// doConfirm 确认授权接口
if(req.isPath(Api.doConfirm)) {
return doConfirm(req);
}
// 模式二隐藏式
if(req.isPath(Api.authorize) && req.isParam(Param.response_type, ResponseType.token)) {
SaClientModel cm = currClientModel();
if(cfg.getIsImplicit() && (cm.isImplicit || cm.isAutoMode)) {
return authorize(req, res, cfg);
}
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(req, res, cfg);
}
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(req, res, cfg);
}
throw new SaOAuth2Exception("暂未开放的授权模式").setCode(SaOAuth2ErrorCode.CODE_30134);
}
// 默认返回
return SaOAuth2Consts.NOT_HANDLE;
}
/**
* 模式一Code授权码 / 模式二隐藏式
* @param req 请求对象
* @param res 响应对象
* @param cfg 配置对象
* @return 处理结果
*/
public static Object authorize(SaRequest req, SaResponse res, SaOAuth2Config cfg) {
// 1如果尚未登录, 则先去登录
if( ! StpUtil.isLogin()) {
return cfg.getNotLoginView().get();
}
// 2构建请求Model
RequestAuthModel ra = SaOAuth2Util.generateRequestAuth(req, StpUtil.getLoginId());
// 3校验重定向域名是否合法
SaOAuth2Util.checkRightUrl(ra.clientId, ra.redirectUri);
// 4校验此次申请的Scope该Client是否已经签约
SaOAuth2Util.checkContract(ra.clientId, ra.scope);
// 5判断如果此次申请的Scope该用户尚未授权则转到授权页面
boolean isGrant = SaOAuth2Util.isGrant(ra.loginId, ra.clientId, ra.scope);
if( ! isGrant) {
return cfg.getConfirmView().apply(ra.clientId, ra.scope);
}
// 6判断授权类型
// 如果是 授权码式开始重定向授权下放code
if(ResponseType.code.equals(ra.responseType)) {
CodeModel codeModel = SaOAuth2Util.generateCode(ra);
String redirectUri = SaOAuth2Util.buildRedirectUri(ra.redirectUri, codeModel.code, ra.state);
return res.redirect(redirectUri);
}
// 如果是 隐藏式开始重定向授权下放 token
if(ResponseType.token.equals(ra.responseType)) {
AccessTokenModel at = SaOAuth2Util.generateAccessToken(ra, false);
String redirectUri = SaOAuth2Util.buildImplicitRedirectUri(ra.redirectUri, at.accessToken, ra.state);
return res.redirect(redirectUri);
}
// 默认返回
throw new SaOAuth2Exception("无效response_type: " + ra.responseType).setCode(SaOAuth2ErrorCode.CODE_30125);
}
/**
* Code授权码 获取 Access-Token
* @param req 请求对象
* @param res 响应对象
* @param cfg 配置对象
* @return 处理结果
*/
public static Object token(SaRequest req, SaResponse res, SaOAuth2Config cfg) {
// 获取参数
String authorizationValue = SaHttpBasicUtil.getAuthorizationValue();
String clientId;
String clientSecret;
// gitlab回调token接口时,按照的是标准的oauth2协议的basic请求头,basic中会包含client_id和client_secret的信息
if(SaFoxUtil.isEmpty(authorizationValue)){
clientId = req.getParamNotNull(Param.client_id);
clientSecret = req.getParamNotNull(Param.client_secret);
} else {
String[] clientIdAndSecret = authorizationValue.split(":");
clientId = clientIdAndSecret[0];
clientSecret = clientIdAndSecret[1];
}
String code = req.getParamNotNull(Param.code);
String redirectUri = req.getParam(Param.redirect_uri);
// 校验参数
SaOAuth2Util.checkGainTokenParam(code, clientId, clientSecret, redirectUri);
// 构建 Access-Token
AccessTokenModel token = SaOAuth2Util.generateAccessToken(code);
// 返回
return SaResult.data(token.toLineMap());
}
/**
* Refresh-Token 刷新 Access-Token
* @param req 请求对象
* @return 处理结果
*/
public static Object refreshToken(SaRequest req) {
// 获取参数
String clientId = req.getParamNotNull(Param.client_id);
String clientSecret = req.getParamNotNull(Param.client_secret);
String refreshToken = req.getParamNotNull(Param.refresh_token);
// 校验参数
SaOAuth2Util.checkRefreshTokenParam(clientId, clientSecret, refreshToken);
// 获取新Token返回
Object data = SaOAuth2Util.refreshAccessToken(refreshToken).toLineMap();
return SaResult.data(data);
}
/**
* 回收 Access-Token
* @param req 请求对象
* @return 处理结果
*/
public static Object revokeToken(SaRequest req) {
// 获取参数
String clientId = req.getParamNotNull(Param.client_id);
String clientSecret = req.getParamNotNull(Param.client_secret);
String accessToken = req.getParamNotNull(Param.access_token);
// 如果 Access-Token 不存在直接返回
if(SaOAuth2Util.getAccessToken(accessToken) == null) {
return SaResult.ok("access_token不存在" + accessToken);
}
// 校验参数
SaOAuth2Util.checkAccessTokenParam(clientId, clientSecret, accessToken);
// 回收 Access-Token
SaOAuth2Util.revokeAccessToken(accessToken);
return SaResult.ok();
}
/**
* doLogin 登录接口
* @param req 请求对象
* @param res 响应对象
* @param cfg 配置对象
* @return 处理结果
*/
public static Object doLogin(SaRequest req, SaResponse res, SaOAuth2Config cfg) {
return cfg.getDoLoginHandle().apply(req.getParamNotNull(Param.name), req.getParamNotNull(Param.pwd));
}
/**
* doConfirm 确认授权接口
* @param req 请求对象
* @return 处理结果
*/
public static Object doConfirm(SaRequest req) {
String clientId = req.getParamNotNull(Param.client_id);
String scope = req.getParamNotNull(Param.scope);
Object loginId = StpUtil.getLoginId();
SaOAuth2Util.saveGrantScope(clientId, loginId, scope);
return SaResult.ok();
}
/**
* 模式三密码式
* @param req 请求对象
* @param res 响应对象
* @param cfg 配置对象
* @return 处理结果
*/
public static Object password(SaRequest req, SaResponse res, SaOAuth2Config cfg) {
// 1获取请求参数
String username = req.getParamNotNull(Param.username);
String password = req.getParamNotNull(Param.password);
String clientId = req.getParamNotNull(Param.client_id);
String clientSecret = req.getParamNotNull(Param.client_secret);
String scope = req.getParam(Param.scope, "");
// 2校验 ClientScope scope
SaOAuth2Util.checkClientSecretAndScope(clientId, clientSecret, scope);
// 3防止因前端误传token造成逻辑干扰
// SaHolder.getStorage().set(StpUtil.stpLogic.splicingKeyJustCreatedSave(), "no-token");
// 3调用API 开始登录如果没能成功登录则直接退出
Object retObj = cfg.getDoLoginHandle().apply(username, password);
if( ! StpUtil.isLogin()) {
return retObj;
}
// 4构建 ra对象
RequestAuthModel ra = new RequestAuthModel();
ra.clientId = clientId;
ra.loginId = StpUtil.getLoginId();
ra.scope = scope;
// 5生成 Access-Token
AccessTokenModel at = SaOAuth2Util.generateAccessToken(ra, true);
// 6返回 Access-Token
return SaResult.data(at.toLineMap());
}
/**
* 模式四凭证式
* @param req 请求对象
* @param res 响应对象
* @param cfg 配置对象
* @return 处理结果
*/
public static Object clientToken(SaRequest req, SaResponse res, SaOAuth2Config cfg) {
// 获取参数
String clientId = req.getParamNotNull(Param.client_id);
String clientSecret = req.getParamNotNull(Param.client_secret);
String scope = req.getParam(Param.scope);
//校验 ClientScope
SaOAuth2Util.checkContract(clientId, scope);
// 校验 ClientSecret
SaOAuth2Util.checkClientSecret(clientId, clientSecret);
// 返回 Client-Token
ClientTokenModel ct = SaOAuth2Util.generateClientToken(clientId, scope);
// 返回 Client-Token
return SaResult.data(ct.toLineMap());
}
/**
* 根据当前请求提交的 client_id 参数获取 SaClientModel 对象
* @return /
*/
public static SaClientModel currClientModel() {
String clientId = SaHolder.getRequest().getParam(Param.client_id);
return SaOAuth2Util.checkClientModel(clientId);
}
}
/*
* 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.processor;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.context.model.SaRequest;
import cn.dev33.satoken.context.model.SaResponse;
import cn.dev33.satoken.httpauth.basic.SaHttpBasicUtil;
import cn.dev33.satoken.oauth2.SaOAuth2Manager;
import cn.dev33.satoken.oauth2.config.SaOAuth2Config;
import cn.dev33.satoken.oauth2.error.SaOAuth2ErrorCode;
import cn.dev33.satoken.oauth2.exception.SaOAuth2Exception;
import cn.dev33.satoken.oauth2.model.*;
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.template.SaOAuth2Template;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaFoxUtil;
import cn.dev33.satoken.util.SaResult;
/**
* Sa-Token OAuth2 请求处理器
*
* @author click33
* @since 1.23.0
*/
public class SaOAuth2ServerProcessor {
/**
* 全局默认实例
*/
public static SaOAuth2ServerProcessor instance = new SaOAuth2ServerProcessor();
/**
* 底层 SaOAuth2Template 对象
*/
public SaOAuth2Template oauth2Template = new SaOAuth2Template();
/**
* 处理 Server 端请求 路由分发
* @return 处理结果
*/
public Object dister() {
// 获取变量
SaRequest req = SaHolder.getRequest();
SaResponse res = SaHolder.getResponse();
SaOAuth2Config cfg = SaOAuth2Manager.getConfig();
// ------------------ 路由分发 ------------------
// 模式一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(req, res, cfg);
}
throw new SaOAuth2Exception("暂未开放的授权模式").setCode(SaOAuth2ErrorCode.CODE_30131);
}
// Code授权码 获取 Access-Token
if(req.isPath(Api.token) && req.isParam(Param.grant_type, GrantType.authorization_code)) {
return token(req, res, cfg);
}
// Refresh-Token 刷新 Access-Token
if(req.isPath(Api.refresh) && req.isParam(Param.grant_type, GrantType.refresh_token)) {
return refreshToken(req);
}
// 回收 Access-Token
if(req.isPath(Api.revoke)) {
return revokeToken(req);
}
// doLogin 登录接口
if(req.isPath(Api.doLogin)) {
return doLogin(req, res, cfg);
}
// doConfirm 确认授权接口
if(req.isPath(Api.doConfirm)) {
return doConfirm(req);
}
// 模式二隐藏式
if(req.isPath(Api.authorize) && req.isParam(Param.response_type, ResponseType.token)) {
SaClientModel cm = currClientModel();
if(cfg.getIsImplicit() && (cm.isImplicit || cm.isAutoMode)) {
return authorize(req, res, cfg);
}
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(req, res, cfg);
}
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(req, res, cfg);
}
throw new SaOAuth2Exception("暂未开放的授权模式").setCode(SaOAuth2ErrorCode.CODE_30134);
}
// 默认返回
return SaOAuth2Consts.NOT_HANDLE;
}
/**
* 模式一Code授权码 / 模式二隐藏式
* @param req 请求对象
* @param res 响应对象
* @param cfg 配置对象
* @return 处理结果
*/
public Object authorize(SaRequest req, SaResponse res, SaOAuth2Config cfg) {
// 1如果尚未登录, 则先去登录
if( ! StpUtil.isLogin()) {
return cfg.getNotLoginView().get();
}
// 2构建请求Model
RequestAuthModel ra = oauth2Template.generateRequestAuth(req, StpUtil.getLoginId());
// 3校验重定向域名是否合法
oauth2Template.checkRightUrl(ra.clientId, ra.redirectUri);
// 4校验此次申请的Scope该Client是否已经签约
oauth2Template.checkContract(ra.clientId, ra.scope);
// 5判断如果此次申请的Scope该用户尚未授权则转到授权页面
boolean isGrant = oauth2Template.isGrant(ra.loginId, ra.clientId, ra.scope);
if( ! isGrant) {
return cfg.getConfirmView().apply(ra.clientId, ra.scope);
}
// 6判断授权类型
// 如果是 授权码式开始重定向授权下放code
if(ResponseType.code.equals(ra.responseType)) {
CodeModel codeModel = oauth2Template.generateCode(ra);
String redirectUri = oauth2Template.buildRedirectUri(ra.redirectUri, codeModel.code, ra.state);
return res.redirect(redirectUri);
}
// 如果是 隐藏式开始重定向授权下放 token
if(ResponseType.token.equals(ra.responseType)) {
AccessTokenModel at = oauth2Template.generateAccessToken(ra, false);
String redirectUri = oauth2Template.buildImplicitRedirectUri(ra.redirectUri, at.accessToken, ra.state);
return res.redirect(redirectUri);
}
// 默认返回
throw new SaOAuth2Exception("无效response_type: " + ra.responseType).setCode(SaOAuth2ErrorCode.CODE_30125);
}
/**
* Code授权码 获取 Access-Token
* @param req 请求对象
* @param res 响应对象
* @param cfg 配置对象
* @return 处理结果
*/
public Object token(SaRequest req, SaResponse res, SaOAuth2Config cfg) {
// 获取参数
String authorizationValue = SaHttpBasicUtil.getAuthorizationValue();
String clientId;
String clientSecret;
// gitlab回调token接口时,按照的是标准的oauth2协议的basic请求头,basic中会包含client_id和client_secret的信息
if(SaFoxUtil.isEmpty(authorizationValue)){
clientId = req.getParamNotNull(Param.client_id);
clientSecret = req.getParamNotNull(Param.client_secret);
} else {
String[] clientIdAndSecret = authorizationValue.split(":");
clientId = clientIdAndSecret[0];
clientSecret = clientIdAndSecret[1];
}
String code = req.getParamNotNull(Param.code);
String redirectUri = req.getParam(Param.redirect_uri);
// 校验参数
oauth2Template.checkGainTokenParam(code, clientId, clientSecret, redirectUri);
// 构建 Access-Token
AccessTokenModel token = oauth2Template.generateAccessToken(code);
// 返回
return SaResult.data(token.toLineMap());
}
/**
* Refresh-Token 刷新 Access-Token
* @param req 请求对象
* @return 处理结果
*/
public Object refreshToken(SaRequest req) {
// 获取参数
String clientId = req.getParamNotNull(Param.client_id);
String clientSecret = req.getParamNotNull(Param.client_secret);
String refreshToken = req.getParamNotNull(Param.refresh_token);
// 校验参数
oauth2Template.checkRefreshTokenParam(clientId, clientSecret, refreshToken);
// 获取新Token返回
Object data = oauth2Template.refreshAccessToken(refreshToken).toLineMap();
return SaResult.data(data);
}
/**
* 回收 Access-Token
* @param req 请求对象
* @return 处理结果
*/
public Object revokeToken(SaRequest req) {
// 获取参数
String clientId = req.getParamNotNull(Param.client_id);
String clientSecret = req.getParamNotNull(Param.client_secret);
String accessToken = req.getParamNotNull(Param.access_token);
// 如果 Access-Token 不存在直接返回
if(oauth2Template.getAccessToken(accessToken) == null) {
return SaResult.ok("access_token不存在" + accessToken);
}
// 校验参数
oauth2Template.checkAccessTokenParam(clientId, clientSecret, accessToken);
// 回收 Access-Token
oauth2Template.revokeAccessToken(accessToken);
return SaResult.ok();
}
/**
* doLogin 登录接口
* @param req 请求对象
* @param res 响应对象
* @param cfg 配置对象
* @return 处理结果
*/
public Object doLogin(SaRequest req, SaResponse res, SaOAuth2Config cfg) {
return cfg.getDoLoginHandle().apply(req.getParamNotNull(Param.name), req.getParamNotNull(Param.pwd));
}
/**
* doConfirm 确认授权接口
* @param req 请求对象
* @return 处理结果
*/
public Object doConfirm(SaRequest req) {
String clientId = req.getParamNotNull(Param.client_id);
String scope = req.getParamNotNull(Param.scope);
Object loginId = StpUtil.getLoginId();
oauth2Template.saveGrantScope(clientId, loginId, scope);
return SaResult.ok();
}
/**
* 模式三密码式
* @param req 请求对象
* @param res 响应对象
* @param cfg 配置对象
* @return 处理结果
*/
public Object password(SaRequest req, SaResponse res, SaOAuth2Config cfg) {
// 1获取请求参数
String username = req.getParamNotNull(Param.username);
String password = req.getParamNotNull(Param.password);
String clientId = req.getParamNotNull(Param.client_id);
String clientSecret = req.getParamNotNull(Param.client_secret);
String scope = req.getParam(Param.scope, "");
// 2校验 ClientScope scope
oauth2Template.checkClientSecretAndScope(clientId, clientSecret, scope);
// 3防止因前端误传token造成逻辑干扰
// SaHolder.getStorage().set(StpUtil.stpLogic.splicingKeyJustCreatedSave(), "no-token");
// 3调用API 开始登录如果没能成功登录则直接退出
Object retObj = cfg.getDoLoginHandle().apply(username, password);
if( ! StpUtil.isLogin()) {
return retObj;
}
// 4构建 ra对象
RequestAuthModel ra = new RequestAuthModel();
ra.clientId = clientId;
ra.loginId = StpUtil.getLoginId();
ra.scope = scope;
// 5生成 Access-Token
AccessTokenModel at = oauth2Template.generateAccessToken(ra, true);
// 6返回 Access-Token
return SaResult.data(at.toLineMap());
}
/**
* 模式四凭证式
* @param req 请求对象
* @param res 响应对象
* @param cfg 配置对象
* @return 处理结果
*/
public Object clientToken(SaRequest req, SaResponse res, SaOAuth2Config cfg) {
// 获取参数
String clientId = req.getParamNotNull(Param.client_id);
String clientSecret = req.getParamNotNull(Param.client_secret);
String scope = req.getParam(Param.scope);
//校验 ClientScope
oauth2Template.checkContract(clientId, scope);
// 校验 ClientSecret
oauth2Template.checkClientSecret(clientId, clientSecret);
// 返回 Client-Token
ClientTokenModel ct = oauth2Template.generateClientToken(clientId, scope);
// 返回 Client-Token
return SaResult.data(ct.toLineMap());
}
/**
* 根据当前请求提交的 client_id 参数获取 SaClientModel 对象
* @return /
*/
public SaClientModel currClientModel() {
String clientId = SaHolder.getRequest().getParam(Param.client_id);
return oauth2Template.checkClientModel(clientId);
}
}

View File

@ -13,25 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.dev33.satoken.oauth2.logic;
import java.util.List;
package cn.dev33.satoken.oauth2.template;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.context.model.SaRequest;
import cn.dev33.satoken.oauth2.SaOAuth2Manager;
import cn.dev33.satoken.oauth2.consts.SaOAuth2Consts.Param;
import cn.dev33.satoken.oauth2.error.SaOAuth2ErrorCode;
import cn.dev33.satoken.oauth2.exception.SaOAuth2Exception;
import cn.dev33.satoken.oauth2.logic.SaOAuth2Consts.Param;
import cn.dev33.satoken.oauth2.model.AccessTokenModel;
import cn.dev33.satoken.oauth2.model.ClientTokenModel;
import cn.dev33.satoken.oauth2.model.CodeModel;
import cn.dev33.satoken.oauth2.model.RefreshTokenModel;
import cn.dev33.satoken.oauth2.model.RequestAuthModel;
import cn.dev33.satoken.oauth2.model.SaClientModel;
import cn.dev33.satoken.oauth2.model.*;
import cn.dev33.satoken.strategy.SaStrategy;
import cn.dev33.satoken.util.SaFoxUtil;
import java.util.List;
/**
* Sa-Token-OAuth2 模块 代码实现
*
@ -40,15 +35,17 @@ import cn.dev33.satoken.util.SaFoxUtil;
*/
public class SaOAuth2Template {
// ------------------- 获取数据 (开发者必须重写的函数)
// ------------------- 数据加载
/**
* 根据id获取Client信息
* @param clientId 应用id
* @return ClientModel
*/
public SaClientModel getClientModel(String clientId) {
return null;
return SaOAuth2Manager.getDataLoader().getClientModel(clientId);
}
/**
* 根据ClientId LoginId 获取openid
* @param clientId 应用id
@ -56,7 +53,7 @@ public class SaOAuth2Template {
* @return 此账号在此Client下的openid
*/
public String getOpenid(String clientId, Object loginId) {
return null;
return SaOAuth2Manager.getDataLoader().getOpenid(clientId, loginId);
}
// ------------------- 资源校验API

View File

@ -13,15 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.dev33.satoken.oauth2.logic;
package cn.dev33.satoken.oauth2.template;
import cn.dev33.satoken.context.model.SaRequest;
import cn.dev33.satoken.oauth2.model.AccessTokenModel;
import cn.dev33.satoken.oauth2.model.ClientTokenModel;
import cn.dev33.satoken.oauth2.model.CodeModel;
import cn.dev33.satoken.oauth2.model.RefreshTokenModel;
import cn.dev33.satoken.oauth2.model.RequestAuthModel;
import cn.dev33.satoken.oauth2.model.SaClientModel;
import cn.dev33.satoken.oauth2.model.*;
import cn.dev33.satoken.oauth2.processor.SaOAuth2ServerProcessor;
/**
* Sa-Token-OAuth2 模块 工具类
@ -30,12 +26,6 @@ import cn.dev33.satoken.oauth2.model.SaClientModel;
* @since 1.23.0
*/
public class SaOAuth2Util {
/**
* 模板代码对象
*/
public static SaOAuth2Template saOAuth2Template = new SaOAuth2Template();
// ------------------- 资源校验API
@ -45,7 +35,7 @@ public class SaOAuth2Util {
* @return ClientModel
*/
public static SaClientModel checkClientModel(String clientId) {
return saOAuth2Template.checkClientModel(clientId);
return SaOAuth2ServerProcessor.instance.oauth2Template.checkClientModel(clientId);
}
/**
@ -54,7 +44,7 @@ public class SaOAuth2Util {
* @return .
*/
public static AccessTokenModel checkAccessToken(String accessToken) {
return saOAuth2Template.checkAccessToken(accessToken);
return SaOAuth2ServerProcessor.instance.oauth2Template.checkAccessToken(accessToken);
}
/**
@ -63,7 +53,7 @@ public class SaOAuth2Util {
* @return .
*/
public static ClientTokenModel checkClientToken(String clientToken) {
return saOAuth2Template.checkClientToken(clientToken);
return SaOAuth2ServerProcessor.instance.oauth2Template.checkClientToken(clientToken);
}
/**
@ -72,7 +62,7 @@ public class SaOAuth2Util {
* @return LoginId
*/
public static Object getLoginIdByAccessToken(String accessToken) {
return saOAuth2Template.getLoginIdByAccessToken(accessToken);
return SaOAuth2ServerProcessor.instance.oauth2Template.getLoginIdByAccessToken(accessToken);
}
/**
@ -81,7 +71,7 @@ public class SaOAuth2Util {
* @param scopes 需要校验的权限列表
*/
public static void checkScope(String accessToken, String... scopes) {
saOAuth2Template.checkScope(accessToken, scopes);
SaOAuth2ServerProcessor.instance.oauth2Template.checkScope(accessToken, scopes);
}
/**
@ -90,7 +80,7 @@ public class SaOAuth2Util {
* @param scopes 需要校验的权限列表
*/
public static void checkClientTokenScope(String clientToken, String... scopes) {
saOAuth2Template.checkClientTokenScope(clientToken, scopes);
SaOAuth2ServerProcessor.instance.oauth2Template.checkClientTokenScope(clientToken, scopes);
}
// ------------------- generate 构建数据
@ -102,7 +92,7 @@ public class SaOAuth2Util {
* @return RequestAuthModel对象
*/
public static RequestAuthModel generateRequestAuth(SaRequest req, Object loginId) {
return saOAuth2Template.generateRequestAuth(req, loginId);
return SaOAuth2ServerProcessor.instance.oauth2Template.generateRequestAuth(req, loginId);
}
/**
@ -111,7 +101,7 @@ public class SaOAuth2Util {
* @return 授权码Model
*/
public static CodeModel generateCode(RequestAuthModel ra) {
return saOAuth2Template.generateCode(ra);
return SaOAuth2ServerProcessor.instance.oauth2Template.generateCode(ra);
}
/**
@ -120,7 +110,7 @@ public class SaOAuth2Util {
* @return AccessToken Model
*/
public static AccessTokenModel generateAccessToken(String code) {
return saOAuth2Template.generateAccessToken(code);
return SaOAuth2ServerProcessor.instance.oauth2Template.generateAccessToken(code);
}
/**
@ -129,7 +119,7 @@ public class SaOAuth2Util {
* @return 新的 Access-Token
*/
public static AccessTokenModel refreshAccessToken(String refreshToken) {
return saOAuth2Template.refreshAccessToken(refreshToken);
return SaOAuth2ServerProcessor.instance.oauth2Template.refreshAccessToken(refreshToken);
}
/**
@ -139,7 +129,7 @@ public class SaOAuth2Util {
* @return Access-Token Model
*/
public static AccessTokenModel generateAccessToken(RequestAuthModel ra, boolean isCreateRt) {
return saOAuth2Template.generateAccessToken(ra, isCreateRt);
return SaOAuth2ServerProcessor.instance.oauth2Template.generateAccessToken(ra, isCreateRt);
}
/**
@ -149,7 +139,7 @@ public class SaOAuth2Util {
* @return Client-Token Model
*/
public static ClientTokenModel generateClientToken(String clientId, String scope) {
return saOAuth2Template.generateClientToken(clientId, scope);
return SaOAuth2ServerProcessor.instance.oauth2Template.generateClientToken(clientId, scope);
}
/**
@ -160,7 +150,7 @@ public class SaOAuth2Util {
* @return 构建完毕的URL
*/
public static String buildRedirectUri(String redirectUri, String code, String state) {
return saOAuth2Template.buildRedirectUri(redirectUri, code, state);
return SaOAuth2ServerProcessor.instance.oauth2Template.buildRedirectUri(redirectUri, code, state);
}
/**
@ -171,7 +161,7 @@ public class SaOAuth2Util {
* @return 构建完毕的URL
*/
public static String buildImplicitRedirectUri(String redirectUri, String token, String state) {
return saOAuth2Template.buildImplicitRedirectUri(redirectUri, token, state);
return SaOAuth2ServerProcessor.instance.oauth2Template.buildImplicitRedirectUri(redirectUri, token, state);
}
/**
@ -179,7 +169,7 @@ public class SaOAuth2Util {
* @param accessToken Access-Token值
*/
public static void revokeAccessToken(String accessToken) {
saOAuth2Template.revokeAccessToken(accessToken);
SaOAuth2ServerProcessor.instance.oauth2Template.revokeAccessToken(accessToken);
}
// ------------------- 数据校验
@ -192,7 +182,7 @@ public class SaOAuth2Util {
* @return 是否已经授权
*/
public static boolean isGrant(Object loginId, String clientId, String scope) {
return saOAuth2Template.isGrant(loginId, clientId, scope);
return SaOAuth2ServerProcessor.instance.oauth2Template.isGrant(loginId, clientId, scope);
}
/**
@ -201,7 +191,7 @@ public class SaOAuth2Util {
* @param scope 权限(多个用逗号隔开)
*/
public static void checkContract(String clientId, String scope) {
saOAuth2Template.checkContract(clientId, scope);
SaOAuth2ServerProcessor.instance.oauth2Template.checkContract(clientId, scope);
}
/**
@ -210,7 +200,7 @@ public class SaOAuth2Util {
* @param url 指定url
*/
public static void checkRightUrl(String clientId, String url) {
saOAuth2Template.checkRightUrl(clientId, url);
SaOAuth2ServerProcessor.instance.oauth2Template.checkRightUrl(clientId, url);
}
/**
@ -220,7 +210,7 @@ public class SaOAuth2Util {
* @return SaClientModel对象
*/
public static SaClientModel checkClientSecret(String clientId, String clientSecret) {
return saOAuth2Template.checkClientSecret(clientId, clientSecret);
return SaOAuth2ServerProcessor.instance.oauth2Template.checkClientSecret(clientId, clientSecret);
}
/**
@ -231,7 +221,7 @@ public class SaOAuth2Util {
* @return SaClientModel对象
*/
public static SaClientModel checkClientSecretAndScope(String clientId, String clientSecret, String scopes) {
return saOAuth2Template.checkClientSecretAndScope(clientId, clientSecret, scopes);
return SaOAuth2ServerProcessor.instance.oauth2Template.checkClientSecretAndScope(clientId, clientSecret, scopes);
}
/**
@ -243,7 +233,7 @@ public class SaOAuth2Util {
* @return CodeModel对象
*/
public static CodeModel checkGainTokenParam(String code, String clientId, String clientSecret, String redirectUri) {
return saOAuth2Template.checkGainTokenParam(code, clientId, clientSecret, redirectUri);
return SaOAuth2ServerProcessor.instance.oauth2Template.checkGainTokenParam(code, clientId, clientSecret, redirectUri);
}
/**
@ -254,7 +244,7 @@ public class SaOAuth2Util {
* @return CodeModel对象
*/
public static RefreshTokenModel checkRefreshTokenParam(String clientId, String clientSecret, String refreshToken) {
return saOAuth2Template.checkRefreshTokenParam(clientId, clientSecret, refreshToken);
return SaOAuth2ServerProcessor.instance.oauth2Template.checkRefreshTokenParam(clientId, clientSecret, refreshToken);
}
/**
@ -265,7 +255,7 @@ public class SaOAuth2Util {
* @return SaClientModel对象
*/
public static AccessTokenModel checkAccessTokenParam(String clientId, String clientSecret, String accessToken) {
return saOAuth2Template.checkAccessTokenParam(clientId, clientSecret, accessToken);
return SaOAuth2ServerProcessor.instance.oauth2Template.checkAccessTokenParam(clientId, clientSecret, accessToken);
}
// ------------------- save 数据
@ -277,7 +267,7 @@ public class SaOAuth2Util {
* @param scope 权限列表(多个逗号隔开)
*/
public static void saveGrantScope(String clientId, Object loginId, String scope) {
saOAuth2Template.saveGrantScope(clientId, loginId, scope);
SaOAuth2ServerProcessor.instance.oauth2Template.saveGrantScope(clientId, loginId, scope);
}
@ -289,7 +279,7 @@ public class SaOAuth2Util {
* @return .
*/
public static CodeModel getCode(String code) {
return saOAuth2Template.getCode(code);
return SaOAuth2ServerProcessor.instance.oauth2Template.getCode(code);
}
/**
@ -298,7 +288,7 @@ public class SaOAuth2Util {
* @return .
*/
public static AccessTokenModel getAccessToken(String accessToken) {
return saOAuth2Template.getAccessToken(accessToken);
return SaOAuth2ServerProcessor.instance.oauth2Template.getAccessToken(accessToken);
}
/**
@ -307,7 +297,7 @@ public class SaOAuth2Util {
* @return .
*/
public static RefreshTokenModel getRefreshToken(String refreshToken) {
return saOAuth2Template.getRefreshToken(refreshToken);
return SaOAuth2ServerProcessor.instance.oauth2Template.getRefreshToken(refreshToken);
}
/**
@ -316,7 +306,7 @@ public class SaOAuth2Util {
* @return .
*/
public static ClientTokenModel getClientToken(String clientToken) {
return saOAuth2Template.getClientToken(clientToken);
return SaOAuth2ServerProcessor.instance.oauth2Template.getClientToken(clientToken);
}
/**
@ -326,7 +316,7 @@ public class SaOAuth2Util {
* @return 权限
*/
public static String getGrantScope(String clientId, Object loginId) {
return saOAuth2Template.getGrantScope(clientId, loginId);
return SaOAuth2ServerProcessor.instance.oauth2Template.getGrantScope(clientId, loginId);
}
}

View File

@ -17,8 +17,8 @@ package cn.dev33.satoken.solon.oauth2;
import cn.dev33.satoken.oauth2.SaOAuth2Manager;
import cn.dev33.satoken.oauth2.config.SaOAuth2Config;
import cn.dev33.satoken.oauth2.logic.SaOAuth2Template;
import cn.dev33.satoken.oauth2.logic.SaOAuth2Util;
import cn.dev33.satoken.oauth2.template.SaOAuth2Template;
import cn.dev33.satoken.oauth2.template.SaOAuth2Util;
import org.noear.solon.annotation.Bean;
import org.noear.solon.annotation.Condition;
import org.noear.solon.annotation.Configuration;

View File

@ -15,18 +15,18 @@
*/
package cn.dev33.satoken.spring.oauth2;
import cn.dev33.satoken.oauth2.SaOAuth2Manager;
import cn.dev33.satoken.oauth2.config.SaOAuth2Config;
import cn.dev33.satoken.oauth2.dataloader.SaOAuth2DataLoader;
import cn.dev33.satoken.oauth2.processor.SaOAuth2ServerProcessor;
import cn.dev33.satoken.oauth2.template.SaOAuth2Template;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import cn.dev33.satoken.oauth2.SaOAuth2Manager;
import cn.dev33.satoken.oauth2.config.SaOAuth2Config;
import cn.dev33.satoken.oauth2.logic.SaOAuth2Template;
import cn.dev33.satoken.oauth2.logic.SaOAuth2Util;
/*
小提示如果你在 idea 中运行源码时出现异常java: 程序包cn.dev33.satoken.oauth2不存在
在项目根目录进入 cmd执行 mvn package 即可解决
*/
// 小提示如果你在 idea 中运行源码时出现异常java: 程序包cn.dev33.satoken.oauth2不存在
// 在项目根目录进入 cmd执行 mvn package 即可解决
/**
* 注入 Sa-Token-OAuth2 所需要的组件
@ -54,7 +54,17 @@ public class SaOAuth2BeanInject {
*/
@Autowired(required = false)
public void setSaOAuth2Interface(SaOAuth2Template saOAuth2Template) {
SaOAuth2Util.saOAuth2Template = saOAuth2Template;
SaOAuth2ServerProcessor.instance.oauth2Template = saOAuth2Template;
}
/**
* 注入 OAuth2 数据加载器
*
* @param dataLoader /
*/
@Autowired(required = false)
public void setSaOAuth2Interface(SaOAuth2DataLoader dataLoader) {
SaOAuth2Manager.setDataLoader(dataLoader);
}
}

View File

@ -31,7 +31,7 @@ import org.springframework.context.annotation.Bean;
public class SaOAuth2BeanRegister {
/**
* 获取 OAuth2 配置对象
* 获取 OAuth2 配置 Bean
*
* @return 配置对象
*/
@ -40,5 +40,5 @@ public class SaOAuth2BeanRegister {
public SaOAuth2Config getSaOAuth2Config() {
return new SaOAuth2Config();
}
}

View File

@ -17,7 +17,7 @@ package cn.dev33.satoken.integrate.configure.inject;
import org.springframework.stereotype.Component;
import cn.dev33.satoken.oauth2.logic.SaOAuth2Template;
import cn.dev33.satoken.oauth2.template.SaOAuth2Template;
/**
* 自定义 Sa-OAuth2 模板方法