From aa8fb4acf0fda7b71acc2bdcacbdf3bef398a210 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 14 May 2022 20:10:06 +0800 Subject: [PATCH] =?UTF-8?q?=E6=90=AD=E5=BB=BA=20oauth/authorize=20?= =?UTF-8?q?=E7=9A=84=E5=88=9D=E6=AD=A5=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/util/string/StrUtils.java | 31 ++--- .../system/enums/ErrorCodeConstants.java | 4 + .../admin/auth/OAuth2Controller.java | 116 +++++++++++++++--- .../dal/dataobject/auth/OAuth2CodeDO.java | 26 ++-- .../service/auth/AdminOAuth2Service.java | 14 +++ .../service/auth/OAuth2ApproveService.java | 27 ++++ .../auth/OAuth2ApproveServiceImpl.java | 20 +++ .../service/auth/OAuth2ClientService.java | 20 ++- .../service/auth/OAuth2ClientServiceImpl.java | 35 ++++-- .../service/auth/OAuth2CodeService.java | 5 +- yudao-ui-admin/src/api/login.js | 8 ++ 11 files changed, 253 insertions(+), 53 deletions(-) create mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ApproveService.java create mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ApproveServiceImpl.java diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/string/StrUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/string/StrUtils.java index e81a52204b..3f79ceea61 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/string/StrUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/string/StrUtils.java @@ -1,9 +1,9 @@ package cn.iocoder.yudao.framework.common.util.string; -import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.StrUtil; -import java.util.Map; +import java.util.Collection; /** * 字符串工具类 @@ -17,21 +17,24 @@ public class StrUtils { } /** - * 指定字符串的 - * @param str - * @param replaceMap - * @return + * 给定字符串是否以任何一个字符串开始 + * 给定字符串和数组为空都返回 false + * + * @param str 给定字符串 + * @param prefixes 需要检测的开始字符串 + * @since 3.0.6 */ - public static String replace(String str, Map replaceMap) { - assert StrUtil.isNotBlank(str); - if (ObjectUtil.isEmpty(replaceMap)) { - return str; + public static boolean startWithAny(String str, Collection prefixes) { + if (StrUtil.isEmpty(str) || ArrayUtil.isEmpty(prefixes)) { + return false; } - String result = null; - for (String key : replaceMap.keySet()) { - result = str.replace(key, replaceMap.get(key)); + + for (CharSequence suffix : prefixes) { + if (StrUtil.startWith(str, suffix, false)) { + return true; + } } - return result; + return false; } } diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java index b8368200dc..f0ce4e11c4 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java @@ -126,5 +126,9 @@ public interface ErrorCodeConstants { // ========== 系统敏感词 1002020000 ========= ErrorCode OAUTH2_CLIENT_NOT_EXISTS = new ErrorCode(1002020000, "OAuth2 客户端不存在"); ErrorCode OAUTH2_CLIENT_EXISTS = new ErrorCode(1002020001, "OAuth2 客户端编号已存在"); + ErrorCode OAUTH2_CLIENT_DISABLE = new ErrorCode(1002020002, "OAuth2 客户端已禁用"); + ErrorCode OAUTH2_CLIENT_AUTHORIZED_GRANT_TYPE_NOT_EXISTS = new ErrorCode(1002020003, "不支持该授权类型"); + ErrorCode OAUTH2_CLIENT_SCOPE_OVER = new ErrorCode(1002020004, "授权范围过大"); + ErrorCode OAUTH2_CLIENT_REDIRECT_URI_NOT_MATCH = new ErrorCode(1002020004, "重定向地址不匹配"); } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/OAuth2Controller.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/OAuth2Controller.java index 536aecfd4f..772221d4ee 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/OAuth2Controller.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/OAuth2Controller.java @@ -1,21 +1,31 @@ package cn.iocoder.yudao.module.system.controller.admin.auth; +import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; +import cn.iocoder.yudao.module.system.enums.auth.OAuth2GrantTypeEnum; +import cn.iocoder.yudao.module.system.service.auth.OAuth2ApproveService; +import cn.iocoder.yudao.module.system.service.auth.OAuth2ClientService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; +import javax.annotation.Resource; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; @Api(tags = "管理后台 - OAuth2.0 授权") @RestController @@ -32,37 +42,105 @@ public class OAuth2Controller { // GET oauth/authorize AuthorizationEndpoint - @PostMapping("/authorize") - @ApiOperation(value = "申请授权", notes = "适合 code 授权码模式,或者 implicit 简化模式;在 authorize.vue 单点登录界面被调用") + @Resource + private OAuth2ClientService oauth2ClientService; + @Resource + private OAuth2ApproveService oauth2ApproveService; + + @GetMapping("/authorize") + @ApiOperation(value = "获得授权信息", notes = "适合 code 授权码模式,或者 implicit 简化模式;在 authorize.vue 单点登录界面被【获取】调用") @ApiImplicitParams({ @ApiImplicitParam(name = "response_type", required = true, value = "响应类型", example = "code", dataTypeClass = String.class), @ApiImplicitParam(name = "client_id", required = true, value = "客户端编号", example = "tudou", dataTypeClass = String.class), - @ApiImplicitParam(name = "scope", value = "授权范围", example = "userinfo.read", dataTypeClass = String.class), // 多个使用逗号分隔 + @ApiImplicitParam(name = "scope", value = "授权范围", example = "userinfo.read", dataTypeClass = String.class), // 多个使用空格分隔 @ApiImplicitParam(name = "redirect_uri", required = true, value = "重定向 URI", example = "https://www.iocoder.cn", dataTypeClass = String.class), @ApiImplicitParam(name = "state", example = "123321", dataTypeClass = String.class) }) - @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 - // 因为前后端分离,Axios 无法很好的处理 302 重定向,所以和 Spring Security OAuth 略有不同,返回结果是重定向的 URL,剩余交给前端处理 - public CommonResult authorize(@RequestParam("response_type") String responseType, + // 情况一:满足自动授权,则走类似 approveOrDeny 的逻辑,最终返回是 CommonResult + // 情况二:如果不满足自动授权,则返回授权相关的展示信息,最终返回是 CommonResult + public CommonResult authorize(@RequestParam("response_type") String responseType, @RequestParam("client_id") String clientId, @RequestParam(value = "scope", required = false) String scope, @RequestParam("redirect_uri") String redirectUri, @RequestParam(value = "state", required = false) String state) { + List scopes = StrUtil.split(scope, ' '); // 0. 校验用户已经登录。通过 Spring Security 实现 // 1.1 校验 responseType 是否满足 code 或者 token 值 - if (!StrUtil.equalsAny(responseType, "code", "token")) { - throw exception0(GlobalErrorCodeConstants.BAD_REQUEST.getCode(), "response_type 参数值允许 code 和 token"); + OAuth2GrantTypeEnum grantTypeEnum = getGrantTypeEnum(responseType); + // 1.2 校验 redirectUri 重定向域名是否合法 + 校验 scope 是否在 Client 授权范围内 + oauth2ClientService.validOAuthClientFromCache(clientId, grantTypeEnum.getGrantType(), scopes, redirectUri); + + // 2. 判断是否满足自动授权(满足) + boolean approved = oauth2ApproveService.checkForPreApproval(getLoginUserId(), UserTypeEnum.ADMIN.getValue(), clientId, scopes); + if (approved) { + // 2.1 如果是 code 授权码模式,则发放 code 授权码,并重定向 + if (grantTypeEnum == OAuth2GrantTypeEnum.AUTHORIZATION_CODE) { + return success(getAuthorizationCodeRedirect()); + } + return success(getImplicitGrantRedirect()); } - // 1.2 校验 redirectUri 重定向域名是否合法 - // 1.3 校验 scope 是否在 Client 授权范围内 + // 3. 不满足自动授权,则返回授权相关的展示信息 + return null; + } - // 2.1 如果是 code 授权码模式,则发放 code 授权码,并重定向 + @PostMapping("/authorize") + @ApiOperation(value = "申请授权", notes = "适合 code 授权码模式,或者 implicit 简化模式;在 authorize.vue 单点登录界面被【提交】调用") + @ApiImplicitParams({ + @ApiImplicitParam(name = "response_type", required = true, value = "响应类型", example = "code", dataTypeClass = String.class), + @ApiImplicitParam(name = "client_id", required = true, value = "客户端编号", example = "tudou", dataTypeClass = String.class), + @ApiImplicitParam(name = "scope", value = "授权范围", example = "userinfo.read", dataTypeClass = String.class), // 使用 Map 格式,Spring MVC 暂时不支持这么接收参数 + @ApiImplicitParam(name = "redirect_uri", required = true, value = "重定向 URI", example = "https://www.iocoder.cn", dataTypeClass = String.class), + @ApiImplicitParam(name = "approved", value = "用户是否接受", example = "true", dataTypeClass = Boolean.class), // 该参数为 null 时,会基于用户是否已经授权过,进行自动判断 + @ApiImplicitParam(name = "state", example = "123321", dataTypeClass = String.class) + }) + @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 + // 因为前后端分离,Axios 无法很好的处理 302 重定向,所以和 Spring Security OAuth 略有不同,返回结果是重定向的 URL,剩余交给前端处理 + public CommonResult approveOrDeny(@RequestParam("response_type") String responseType, + @RequestParam("client_id") String clientId, + @RequestParam(value = "scope", required = false) String scope, + @RequestParam("redirect_uri") String redirectUri, + @RequestParam("approved") Boolean approved, + @RequestParam(value = "state", required = false) String state) { + @SuppressWarnings("unchecked") + Map scopes = JsonUtils.parseObject(scope, Map.class); + scopes = ObjectUtil.defaultIfNull(scopes, Collections.emptyMap()); + // TODO 芋艿:针对 approved + scopes 在看看 spring security 的实现 + // 0. 校验用户已经登录。通过 Spring Security 实现 - // 2.2 如果是 token 则是 implicit 简化模式,则发送 accessToken 访问令牌,并重定向 - // TODO 需要确认,是否要 refreshToken 生成 - return CommonResult.success(""); + // 1.1 校验 responseType 是否满足 code 或者 token 值 + OAuth2GrantTypeEnum grantTypeEnum = getGrantTypeEnum(responseType); + // 1.2 校验 redirectUri 重定向域名是否合法 + 校验 scope 是否在 Client 授权范围内 + oauth2ClientService.validOAuthClientFromCache(clientId, grantTypeEnum.getGrantType(), scopes.keySet(), redirectUri); + + // 2.1 + // 2.2 + + // 3.1 如果是 code 授权码模式,则发放 code 授权码,并重定向 + if (grantTypeEnum == OAuth2GrantTypeEnum.AUTHORIZATION_CODE) { + return success(getAuthorizationCodeRedirect()); + } + // 3.2 如果是 token 则是 implicit 简化模式,则发送 accessToken 访问令牌,并重定向 + return success(getImplicitGrantRedirect()); + } + + private static OAuth2GrantTypeEnum getGrantTypeEnum(String responseType) { + if (StrUtil.equals(responseType, "code")) { + return OAuth2GrantTypeEnum.AUTHORIZATION_CODE; + } + if (StrUtil.equalsAny(responseType, "token")) { + return OAuth2GrantTypeEnum.IMPLICIT; + } + throw exception0(BAD_REQUEST.getCode(), "response_type 参数值允许 code 和 token"); + } + + private String getImplicitGrantRedirect() { + return ""; + } + + private String getAuthorizationCodeRedirect() { + return ""; } } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2CodeDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2CodeDO.java index ac9bdf2ab7..f7599ffc64 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2CodeDO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2CodeDO.java @@ -3,18 +3,21 @@ package cn.iocoder.yudao.module.system.dal.dataobject.auth; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; import lombok.Data; import lombok.EqualsAndHashCode; import java.util.Date; +import java.util.List; /** * OAuth2 授权码 DO * * @author 芋道源码 */ -@TableName("system_oauth2_code") +@TableName(value = "system_oauth2_code", autoResultMap = true) @KeySequence("system_oauth2_code_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 @Data @EqualsAndHashCode(callSuper = true) @@ -41,22 +44,25 @@ public class OAuth2CodeDO extends BaseDO { /** * 客户端编号 * - * 关联 {@link OAuth2ClientDO#getId()} + * 关联 {@link OAuth2ClientDO#getClientId()} */ private String clientId; /** - * 刷新令牌 - * - * 关联 {@link OAuth2RefreshTokenDO#getRefreshToken()} + * 授权范围 */ - private String refreshToken; + @TableField(typeHandler = JacksonTypeHandler.class) + private List scopes; + /** + * 重定向地址 + */ + private String redirectUri; + /** + * 状态 + */ + private String state; /** * 过期时间 */ private Date expiresTime; - /** - * 创建 IP - */ - private String createIp; } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminOAuth2Service.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminOAuth2Service.java index 5c10cdfb8a..7910e27fd5 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminOAuth2Service.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminOAuth2Service.java @@ -1,5 +1,9 @@ package cn.iocoder.yudao.module.system.service.auth; +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; + +import java.util.Collection; + /** * 管理后台的 OAuth2 Service 接口 * @@ -11,4 +15,14 @@ package cn.iocoder.yudao.module.system.service.auth; * @author 芋道源码 */ public interface AdminOAuth2Service { + + // ImplicitTokenGranter + OAuth2AccessTokenDO grantImplicit(Long userId, Integer userType, + String clientId, Collection scopes); + + // AuthorizationCodeTokenGranter + String grantAuthorizationCode(Long userId, Integer userType, + String clientId, Collection scopes, + String redirectUri, String state); + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ApproveService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ApproveService.java new file mode 100644 index 0000000000..991e81b99e --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ApproveService.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.system.service.auth; + +import java.util.Collection; + +/** + * OAuth2 批准 Service 接口 + * + * 从功能上,和 Spring Security OAuth 的 ApprovalStoreUserApprovalHandler 的功能,记录用户针对指定客户端的授权,减少手动确定。 + * + * @author 芋道源码 + */ +public interface OAuth2ApproveService { + + /** + * 获得指定用户,针对指定客户端的指定授权,是否通过 + * + * 参考 ApprovalStoreUserApprovalHandler 的 checkForPreApproval 方法 + * + * @param userId 用户编号 + * @param userType 用户类型 + * @param clientId 客户端编号 + * @param scopes 授权范围 + * @return 是否授权通过 + */ + boolean checkForPreApproval(Long userId, Integer userType, String clientId, Collection scopes); + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ApproveServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ApproveServiceImpl.java new file mode 100644 index 0000000000..17d38a8b7b --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ApproveServiceImpl.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.system.service.auth; + +import org.springframework.stereotype.Service; + +import java.util.Collection; + +/** + * OAuth2 批准 Service 实现类 + * + * @author 芋道源码 + */ +@Service +public class OAuth2ApproveServiceImpl implements OAuth2ApproveService { + + @Override + public boolean checkForPreApproval(Long userId, Integer userType, String clientId, Collection scopes) { + return false; + } + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ClientService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ClientService.java index 1dd57277e0..5915610b63 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ClientService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ClientService.java @@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2Clie import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO; import javax.validation.Valid; +import java.util.Collection; /** * OAuth2.0 Client Service 接口 @@ -63,9 +64,24 @@ public interface OAuth2ClientService { /** * 从缓存中,校验客户端是否合法 * - * @param clientId 客户端编号 * @return 客户端 */ - OAuth2ClientDO validOAuthClientFromCache(String clientId); + default OAuth2ClientDO validOAuthClientFromCache(String clientId) { + return validOAuthClientFromCache(clientId, null, null, null); + } + + /** + * 从缓存中,校验客户端是否合法 + * + * 非空时,进行校验 + * + * @param clientId 客户端编号 + * @param authorizedGrantType 授权方式 + * @param scopes 授权范围 + * @param redirectUri 重定向地址 + * @return 客户端 + */ + OAuth2ClientDO validOAuthClientFromCache(String clientId, String authorizedGrantType, + Collection scopes, String redirectUri); } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ClientServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ClientServiceImpl.java index a9df22a9e4..c56562ef9f 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ClientServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ClientServiceImpl.java @@ -1,7 +1,10 @@ package cn.iocoder.yudao.module.system.service.auth; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.string.StrUtils; import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientCreateReqVO; import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientPageReqVO; import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientUpdateReqVO; @@ -18,15 +21,12 @@ import org.springframework.validation.annotation.Validated; import javax.annotation.PostConstruct; import javax.annotation.Resource; -import java.util.Date; -import java.util.List; -import java.util.Map; +import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getMaxValue; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.OAUTH2_CLIENT_EXISTS; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.OAUTH2_CLIENT_NOT_EXISTS; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; /** * OAuth2.0 Client Service 实现类 @@ -175,8 +175,29 @@ public class OAuth2ClientServiceImpl implements OAuth2ClientService { } @Override - public OAuth2ClientDO validOAuthClientFromCache(String clientId) { - return clientCache.get(clientId); + public OAuth2ClientDO validOAuthClientFromCache(String clientId, String authorizedGrantType, Collection scopes, String redirectUri) { + // 校验客户端存在、且开启 + OAuth2ClientDO client = clientCache.get(clientId); + if (client == null) { + throw exception(OAUTH2_CLIENT_EXISTS); + } + if (Objects.equals(client.getStatus(), CommonStatusEnum.ENABLE.getStatus())) { + throw exception(OAUTH2_CLIENT_DISABLE); + } + + // 校验授权方式 + if (StrUtil.isNotEmpty(authorizedGrantType) && !CollUtil.contains(client.getAuthorizedGrantTypes(), authorizedGrantType)) { + throw exception(OAUTH2_CLIENT_AUTHORIZED_GRANT_TYPE_NOT_EXISTS); + } + // 校验授权范围 + if (CollUtil.isNotEmpty(scopes) && !CollUtil.containsAll(client.getScopes(), scopes)) { + throw exception(OAUTH2_CLIENT_SCOPE_OVER); + } + // 校验回调地址 + if (StrUtil.isNotEmpty(redirectUri) && !StrUtils.startWithAny(redirectUri, client.getRedirectUris())) { + throw exception(OAUTH2_CLIENT_REDIRECT_URI_NOT_MATCH); + } + return client; } } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2CodeService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2CodeService.java index f8a788eb0d..a4a38b1bcc 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2CodeService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2CodeService.java @@ -7,5 +7,8 @@ package cn.iocoder.yudao.module.system.service.auth; * * @author 芋道源码 */ -public class OAuth2CodeService { +public interface OAuth2CodeService { + + + } diff --git a/yudao-ui-admin/src/api/login.js b/yudao-ui-admin/src/api/login.js index 75846db523..ef667fc6bb 100644 --- a/yudao-ui-admin/src/api/login.js +++ b/yudao-ui-admin/src/api/login.js @@ -122,6 +122,14 @@ export function authorize() { response_type: 'code', client_id: 'test', redirect_uri: 'https://www.iocoder.cn', + // scopes: { + // read: true, + // write: false + // } + scope: { + read: true, + write: false + } }, method: 'post' })