From ce60ec08156f3b84d03c9db62f41abbf97d6f2d6 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 14 May 2022 15:11:58 +0800 Subject: [PATCH 01/13] =?UTF-8?q?=E4=BF=AE=E6=94=B9=20OAuth2ClientDO=20?= =?UTF-8?q?=E5=AE=9E=E4=BD=93=EF=BC=8C=E6=94=AF=E6=8C=81=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E6=8E=88=E6=9D=83=E7=9A=84=E8=8C=83=E5=9B=B4=E7=9A=84=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/auth/OAuth2Controller.java | 44 +++++ .../auth/vo/client/OAuth2ClientBaseVO.java | 7 +- .../dal/dataobject/auth/OAuth2ClientDO.java | 11 +- yudao-ui-admin/src/api/login.js | 17 ++ yudao-ui-admin/src/router/index.js | 5 + yudao-ui-admin/src/views/authorize.vue | 169 ++++++++++++++++++ .../src/views/system/oauth2/client/index.vue | 20 +-- 7 files changed, 251 insertions(+), 22 deletions(-) create mode 100644 yudao-ui-admin/src/views/authorize.vue 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 d82731aab..536aecfd4 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,11 +1,22 @@ package cn.iocoder.yudao.module.system.controller.admin.auth; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; 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 static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0; + @Api(tags = "管理后台 - OAuth2.0 授权") @RestController @RequestMapping("/system/oauth2") @@ -21,4 +32,37 @@ public class OAuth2Controller { // GET oauth/authorize AuthorizationEndpoint + @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), // 多个使用逗号分隔 + @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, + @RequestParam("client_id") String clientId, + @RequestParam(value = "scope", required = false) String scope, + @RequestParam("redirect_uri") String redirectUri, + @RequestParam(value = "state", required = false) String state) { + // 0. 校验用户已经登录。通过 Spring Security 实现 + + // 1.1 校验 responseType 是否满足 code 或者 token 值 + if (!StrUtil.equalsAny(responseType, "code", "token")) { + throw exception0(GlobalErrorCodeConstants.BAD_REQUEST.getCode(), "response_type 参数值允许 code 和 token"); + } + // 1.2 校验 redirectUri 重定向域名是否合法 + + // 1.3 校验 scope 是否在 Client 授权范围内 + + // 2.1 如果是 code 授权码模式,则发放 code 授权码,并重定向 + + // 2.2 如果是 token 则是 implicit 简化模式,则发送 accessToken 访问令牌,并重定向 + // TODO 需要确认,是否要 refreshToken 生成 + return CommonResult.success(""); + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/client/OAuth2ClientBaseVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/client/OAuth2ClientBaseVO.java index 2cd2b7466..4c13263e5 100755 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/client/OAuth2ClientBaseVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/client/OAuth2ClientBaseVO.java @@ -55,10 +55,6 @@ public class OAuth2ClientBaseVO { private List<@NotEmpty(message = "重定向的 URI 不能为空") @URL(message = "重定向的 URI 格式不正确") String> redirectUris; - @ApiModelProperty(value = "是否自动授权", required = true, example = "true") - @NotNull(message = "是否自动授权不能为空") - private Boolean autoApprove; - @ApiModelProperty(value = "授权类型", required = true, example = "password", notes = "参见 OAuth2GrantTypeEnum 枚举") @NotNull(message = "授权类型不能为空") private List authorizedGrantTypes; @@ -66,6 +62,9 @@ public class OAuth2ClientBaseVO { @ApiModelProperty(value = "授权范围", example = "user_info") private List scopes; + @ApiModelProperty(value = "自动通过的授权范围", example = "user_info") + private List autoApproveScopes; + @ApiModelProperty(value = "权限", example = "system:user:query") private List authorities; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2ClientDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2ClientDO.java index 9b6e36b6e..dee649e5e 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2ClientDO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2ClientDO.java @@ -70,10 +70,6 @@ public class OAuth2ClientDO extends BaseDO { */ @TableField(typeHandler = JacksonTypeHandler.class) private List redirectUris; - /** - * 是否自动授权 - */ - private Boolean autoApprove; /** * 授权类型(模式) * @@ -86,6 +82,13 @@ public class OAuth2ClientDO extends BaseDO { */ @TableField(typeHandler = JacksonTypeHandler.class) private List scopes; + /** + * 自动授权的 Scope + * + * code 授权时,如果 scope 在这个范围内,则自动通过 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List autoApproveScopes; /** * 权限 */ diff --git a/yudao-ui-admin/src/api/login.js b/yudao-ui-admin/src/api/login.js index eb643e3a6..75846db52 100644 --- a/yudao-ui-admin/src/api/login.js +++ b/yudao-ui-admin/src/api/login.js @@ -109,3 +109,20 @@ export function refreshToken() { method: 'post' }) } + +// ========== OAUTH 2.0 相关 ========== +export function authorize() { + return service({ + url: '/system/oauth2/authorize', + headers:{ + 'Content-type': 'application/x-www-form-urlencoded', + "Access-Control-Allow-Origin": "*" + }, + params: { + response_type: 'code', + client_id: 'test', + redirect_uri: 'https://www.iocoder.cn', + }, + method: 'post' + }) +} diff --git a/yudao-ui-admin/src/router/index.js b/yudao-ui-admin/src/router/index.js index cc9b374cb..e6b3d6e01 100644 --- a/yudao-ui-admin/src/router/index.js +++ b/yudao-ui-admin/src/router/index.js @@ -42,6 +42,11 @@ export const constantRoutes = [ component: (resolve) => require(['@/views/login'], resolve), hidden: true }, + { + path: '/authorize', + component: (resolve) => require(['@/views/authorize'], resolve), + hidden: true + }, { path: '/social-login', component: (resolve) => require(['@/views/socialLogin'], resolve), diff --git a/yudao-ui-admin/src/views/authorize.vue b/yudao-ui-admin/src/views/authorize.vue new file mode 100644 index 000000000..37bba7aa2 --- /dev/null +++ b/yudao-ui-admin/src/views/authorize.vue @@ -0,0 +1,169 @@ + + + + diff --git a/yudao-ui-admin/src/views/system/oauth2/client/index.vue b/yudao-ui-admin/src/views/system/oauth2/client/index.vue index 37f16635f..72bbef1fe 100755 --- a/yudao-ui-admin/src/views/system/oauth2/client/index.vue +++ b/yudao-ui-admin/src/views/system/oauth2/client/index.vue @@ -108,23 +108,16 @@ - - - 自动登录 - 手动登录 - - - - - - - + + + + + @@ -196,7 +189,6 @@ export default { accessTokenValiditySeconds: [{ required: true, message: "访问令牌的有效期不能为空", trigger: "blur" }], refreshTokenValiditySeconds: [{ required: true, message: "刷新令牌的有效期不能为空", trigger: "blur" }], redirectUris: [{ required: true, message: "可重定向的 URI 地址不能为空", trigger: "blur" }], - autoApprove: [{ required: true, message: "是否自动授权不能为空", trigger: "blur" }], authorizedGrantTypes: [{ required: true, message: "授权类型不能为空", trigger: "blur" }], } }; @@ -235,9 +227,9 @@ export default { accessTokenValiditySeconds: 30 * 60, refreshTokenValiditySeconds: 30 * 24 * 60, redirectUris: [], - autoApprove: true, authorizedGrantTypes: [], scopes: [], + autoApproveScopes: [], authorities: [], resourceIds: [], additionalInformation: undefined, From aa8fb4acf0fda7b71acc2bdcacbdf3bef398a210 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 14 May 2022 20:10:06 +0800 Subject: [PATCH 02/13] =?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 e81a52204..3f79ceea6 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 b8368200d..f0ce4e11c 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 536aecfd4..772221d4e 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 ac9bdf2ab..f7599ffc6 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 5c10cdfb8..7910e27fd 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 000000000..991e81b99 --- /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 000000000..17d38a8b7 --- /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 1dd57277e..5915610b6 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 a9df22a9e..c56562ef9 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 f8a788eb0..a4a38b1bc 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 75846db52..ef667fc6b 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' }) From 7d1deab48bf9e4285d5ef59559cd7d05c4a5d8e8 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 14 May 2022 21:56:28 +0800 Subject: [PATCH 03/13] =?UTF-8?q?=20=E8=B0=83=E6=95=B4=E5=8C=85=E7=BB=93?= =?UTF-8?q?=E6=9E=84=EF=BC=8Coauth2=20=E7=8B=AC=E7=AB=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../system/api/auth/OAuth2TokenApiImpl.java | 2 +- .../controller/admin/auth/AuthController.java | 2 +- .../auth/vo/{auth => }/AuthLoginReqVO.java | 2 +- .../auth/vo/{auth => }/AuthLoginRespVO.java | 2 +- .../auth/vo/{auth => }/AuthMenuRespVO.java | 2 +- .../{auth => }/AuthPermissionInfoRespVO.java | 2 +- .../auth/vo/{auth => }/AuthSmsLoginReqVO.java | 2 +- .../auth/vo/{auth => }/AuthSmsSendReqVO.java | 2 +- .../{auth => }/AuthSocialBindLoginReqVO.java | 2 +- .../{auth => }/AuthSocialQuickLoginReqVO.java | 2 +- .../OAuth2ClientController.http | 0 .../OAuth2ClientController.java | 12 +++---- .../{auth => oauth2}/OAuth2Controller.java | 31 +++++++++++++------ .../OAuth2TokenController.java | 8 ++--- .../vo/client/OAuth2ClientBaseVO.java | 2 +- .../vo/client/OAuth2ClientCreateReqVO.java | 2 +- .../vo/client/OAuth2ClientPageReqVO.java | 2 +- .../vo/client/OAuth2ClientRespVO.java | 2 +- .../vo/client/OAuth2ClientUpdateReqVO.java | 2 +- .../vo/token/OAuth2AccessTokenPageReqVO.java | 2 +- .../vo/token/OAuth2AccessTokenRespVO.java | 2 +- .../system/convert/auth/AuthConvert.java | 2 +- .../convert/auth/OAuth2ClientConvert.java | 6 ++-- .../convert/auth/OAuth2TokenConvert.java | 2 +- .../mysql/auth/OAuth2AccessTokenMapper.java | 2 +- .../dal/mysql/auth/OAuth2ClientMapper.java | 2 +- .../auth/OAuth2ClientRefreshConsumer.java | 2 +- .../system/service/auth/AdminAuthService.java | 2 +- .../service/auth/AdminAuthServiceImpl.java | 3 +- .../OAuth2ApproveService.java | 14 ++++++++- .../OAuth2ApproveServiceImpl.java | 10 ++++-- .../{auth => oauth2}/OAuth2ClientService.java | 8 ++--- .../OAuth2ClientServiceImpl.java | 8 ++--- .../{auth => oauth2}/OAuth2CodeService.java | 2 +- .../OAuth2GrantService.java} | 8 +++-- .../oauth2/OAuth2GrantServiceImpl.java | 29 +++++++++++++++++ .../{auth => oauth2}/OAuth2TokenService.java | 4 +-- .../OAuth2TokenServiceImpl.java | 4 +-- .../service/auth/AuthServiceImplTest.java | 5 +-- .../auth/OAuth2ClientServiceImplTest.java | 7 +++-- 40 files changed, 135 insertions(+), 70 deletions(-) rename yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/{auth => }/AuthLoginReqVO.java (95%) rename yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/{auth => }/AuthLoginRespVO.java (91%) rename yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/{auth => }/AuthMenuRespVO.java (95%) rename yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/{auth => }/AuthPermissionInfoRespVO.java (94%) rename yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/{auth => }/AuthSmsLoginReqVO.java (94%) rename yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/{auth => }/AuthSmsSendReqVO.java (93%) rename yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/{auth => }/AuthSocialBindLoginReqVO.java (96%) rename yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/{auth => }/AuthSocialQuickLoginReqVO.java (94%) rename yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/{auth => oauth2}/OAuth2ClientController.http (100%) rename yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/{auth => oauth2}/OAuth2ClientController.java (85%) rename yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/{auth => oauth2}/OAuth2Controller.java (81%) rename yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/{auth => oauth2}/OAuth2TokenController.java (85%) rename yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/{auth => oauth2}/vo/client/OAuth2ClientBaseVO.java (97%) rename yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/{auth => oauth2}/vo/client/OAuth2ClientCreateReqVO.java (77%) rename yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/{auth => oauth2}/vo/client/OAuth2ClientPageReqVO.java (85%) rename yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/{auth => oauth2}/vo/client/OAuth2ClientRespVO.java (88%) rename yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/{auth => oauth2}/vo/client/OAuth2ClientUpdateReqVO.java (87%) rename yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/{auth => oauth2}/vo/token/OAuth2AccessTokenPageReqVO.java (90%) rename yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/{auth => oauth2}/vo/token/OAuth2AccessTokenRespVO.java (94%) rename yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/{auth => oauth2}/OAuth2ApproveService.java (62%) rename yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/{auth => oauth2}/OAuth2ApproveServiceImpl.java (58%) rename yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/{auth => oauth2}/OAuth2ClientService.java (85%) rename yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/{auth => oauth2}/OAuth2ClientServiceImpl.java (95%) rename yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/{auth => oauth2}/OAuth2CodeService.java (80%) rename yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/{auth/AdminOAuth2Service.java => oauth2/OAuth2GrantService.java} (79%) create mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImpl.java rename yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/{auth => oauth2}/OAuth2TokenService.java (93%) rename yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/{auth => oauth2}/OAuth2TokenServiceImpl.java (97%) diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/auth/OAuth2TokenApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/auth/OAuth2TokenApiImpl.java index d9e873c6d..3c2c29112 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/auth/OAuth2TokenApiImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/auth/OAuth2TokenApiImpl.java @@ -5,7 +5,7 @@ import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCreateReqDTO import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenRespDTO; import cn.iocoder.yudao.module.system.convert.auth.OAuth2TokenConvert; import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; -import cn.iocoder.yudao.module.system.service.auth.OAuth2TokenService; +import cn.iocoder.yudao.module.system.service.oauth2.OAuth2TokenService; import org.springframework.stereotype.Service; import javax.annotation.Resource; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java index 3d38175b5..e42a5dfa5 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java @@ -6,7 +6,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.util.collection.SetUtils; import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.framework.security.config.SecurityProperties; -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.*; +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*; import cn.iocoder.yudao.module.system.convert.auth.AuthConvert; import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/auth/AuthLoginReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthLoginReqVO.java similarity index 95% rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/auth/AuthLoginReqVO.java rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthLoginReqVO.java index 013c0b2e7..0ecd3d670 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/auth/AuthLoginReqVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthLoginReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth; +package cn.iocoder.yudao.module.system.controller.admin.auth.vo; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/auth/AuthLoginRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthLoginRespVO.java similarity index 91% rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/auth/AuthLoginRespVO.java rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthLoginRespVO.java index 915a550c6..ec83a6996 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/auth/AuthLoginRespVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthLoginRespVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth; +package cn.iocoder.yudao.module.system.controller.admin.auth.vo; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/auth/AuthMenuRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthMenuRespVO.java similarity index 95% rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/auth/AuthMenuRespVO.java rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthMenuRespVO.java index 74e8c740d..2ae7eaac8 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/auth/AuthMenuRespVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthMenuRespVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth; +package cn.iocoder.yudao.module.system.controller.admin.auth.vo; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/auth/AuthPermissionInfoRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthPermissionInfoRespVO.java similarity index 94% rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/auth/AuthPermissionInfoRespVO.java rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthPermissionInfoRespVO.java index bada25205..0e809226c 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/auth/AuthPermissionInfoRespVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthPermissionInfoRespVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth; +package cn.iocoder.yudao.module.system.controller.admin.auth.vo; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/auth/AuthSmsLoginReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSmsLoginReqVO.java similarity index 94% rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/auth/AuthSmsLoginReqVO.java rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSmsLoginReqVO.java index 4360b9841..5e4135546 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/auth/AuthSmsLoginReqVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSmsLoginReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth; +package cn.iocoder.yudao.module.system.controller.admin.auth.vo; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/auth/AuthSmsSendReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSmsSendReqVO.java similarity index 93% rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/auth/AuthSmsSendReqVO.java rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSmsSendReqVO.java index adcd0483b..0df9a521e 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/auth/AuthSmsSendReqVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSmsSendReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth; +package cn.iocoder.yudao.module.system.controller.admin.auth.vo; import cn.iocoder.yudao.framework.common.validation.InEnum; import cn.iocoder.yudao.framework.common.validation.Mobile; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/auth/AuthSocialBindLoginReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSocialBindLoginReqVO.java similarity index 96% rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/auth/AuthSocialBindLoginReqVO.java rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSocialBindLoginReqVO.java index 71f5cfe9f..cefe40d14 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/auth/AuthSocialBindLoginReqVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSocialBindLoginReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth; +package cn.iocoder.yudao.module.system.controller.admin.auth.vo; import cn.iocoder.yudao.framework.common.validation.InEnum; import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/auth/AuthSocialQuickLoginReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSocialQuickLoginReqVO.java similarity index 94% rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/auth/AuthSocialQuickLoginReqVO.java rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSocialQuickLoginReqVO.java index 2bec43691..4b7ebb175 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/auth/AuthSocialQuickLoginReqVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSocialQuickLoginReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth; +package cn.iocoder.yudao.module.system.controller.admin.auth.vo; import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; import cn.iocoder.yudao.framework.common.validation.InEnum; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/OAuth2ClientController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2ClientController.http similarity index 100% rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/OAuth2ClientController.http rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2ClientController.http diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/OAuth2ClientController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2ClientController.java similarity index 85% rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/OAuth2ClientController.java rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2ClientController.java index 2912957b8..0e5b109ee 100755 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/OAuth2ClientController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2ClientController.java @@ -1,14 +1,14 @@ -package cn.iocoder.yudao.module.system.controller.admin.auth; +package cn.iocoder.yudao.module.system.controller.admin.oauth2; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; -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.OAuth2ClientRespVO; -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientUpdateReqVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientCreateReqVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientRespVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientUpdateReqVO; import cn.iocoder.yudao.module.system.convert.auth.OAuth2ClientConvert; import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO; -import cn.iocoder.yudao.module.system.service.auth.OAuth2ClientService; +import cn.iocoder.yudao.module.system.service.oauth2.OAuth2ClientService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiOperation; 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/oauth2/OAuth2Controller.java similarity index 81% rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/OAuth2Controller.java rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2Controller.java index 772221d4e..cf5851938 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/oauth2/OAuth2Controller.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.system.controller.admin.auth; +package cn.iocoder.yudao.module.system.controller.admin.oauth2; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; @@ -7,8 +7,9 @@ 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 cn.iocoder.yudao.module.system.service.oauth2.OAuth2ApproveService; +import cn.iocoder.yudao.module.system.service.oauth2.OAuth2ClientService; +import cn.iocoder.yudao.module.system.service.oauth2.OAuth2GrantService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; @@ -42,6 +43,8 @@ public class OAuth2Controller { // GET oauth/authorize AuthorizationEndpoint + @Resource + private OAuth2GrantService oAuth2GrantService; @Resource private OAuth2ClientService oauth2ClientService; @Resource @@ -56,8 +59,6 @@ public class OAuth2Controller { @ApiImplicitParam(name = "redirect_uri", required = true, value = "重定向 URI", example = "https://www.iocoder.cn", dataTypeClass = String.class), @ApiImplicitParam(name = "state", example = "123321", dataTypeClass = String.class) }) - // 情况一:满足自动授权,则走类似 approveOrDeny 的逻辑,最终返回是 CommonResult - // 情况二:如果不满足自动授权,则返回授权相关的展示信息,最终返回是 CommonResult public CommonResult authorize(@RequestParam("response_type") String responseType, @RequestParam("client_id") String clientId, @RequestParam(value = "scope", required = false) String scope, @@ -92,16 +93,18 @@ public class OAuth2Controller { @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 = "autoApprove", required = true, value = "用户是否接受", example = "true", dataTypeClass = Boolean.class), @ApiImplicitParam(name = "state", example = "123321", dataTypeClass = String.class) }) @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 + // 场景一:【自动授权 autoApprove = true】刚进入 authorize.vue 界面,调用该接口,用户历史已经给该应用做过对应的授权,或者 OAuth2Client 支持该 scope 的自动授权 + // 场景二:【手动授权 autoApprove = false】在 authorize.vue 界面,用户选择好 scope 授权范围,调用该接口,进行授权。此时,approved 为 true 或者 false // 因为前后端分离,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 = "autoApprove") Boolean autoApprove, @RequestParam(value = "state", required = false) String state) { @SuppressWarnings("unchecked") Map scopes = JsonUtils.parseObject(scope, Map.class); @@ -114,8 +117,18 @@ public class OAuth2Controller { // 1.2 校验 redirectUri 重定向域名是否合法 + 校验 scope 是否在 Client 授权范围内 oauth2ClientService.validOAuthClientFromCache(clientId, grantTypeEnum.getGrantType(), scopes.keySet(), redirectUri); - // 2.1 - // 2.2 + // 2.1 假设 approved 为 null,说明是场景一 + if (Boolean.TRUE.equals(autoApprove)) { + // 如果无法自动授权通过,则返回空 url,前端不进行跳转 + if (!oauth2ApproveService.checkForPreApproval(getLoginUserId(), UserTypeEnum.ADMIN.getValue(), clientId, scopes.keySet())) { + return success(null); + } + } else { // 2.2 假设 approved 非 null,说明是场景二 + // 如果计算后不通过,则跳转一个错误链接 + if (!oauth2ApproveService.updateAfterApproval(getLoginUserId(), UserTypeEnum.ADMIN.getValue(), clientId, scopes)) { + return success("TODO"); + } + } // 3.1 如果是 code 授权码模式,则发放 code 授权码,并重定向 if (grantTypeEnum == OAuth2GrantTypeEnum.AUTHORIZATION_CODE) { diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/OAuth2TokenController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2TokenController.java similarity index 85% rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/OAuth2TokenController.java rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2TokenController.java index 042a873bd..3adacf4e2 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/OAuth2TokenController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2TokenController.java @@ -1,14 +1,14 @@ -package cn.iocoder.yudao.module.system.controller.admin.auth; +package cn.iocoder.yudao.module.system.controller.admin.oauth2; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.token.OAuth2AccessTokenPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.token.OAuth2AccessTokenRespVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token.OAuth2AccessTokenPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token.OAuth2AccessTokenRespVO; import cn.iocoder.yudao.module.system.convert.auth.OAuth2TokenConvert; import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; import cn.iocoder.yudao.module.system.service.auth.AdminAuthService; -import cn.iocoder.yudao.module.system.service.auth.OAuth2TokenService; +import cn.iocoder.yudao.module.system.service.oauth2.OAuth2TokenService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiOperation; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/client/OAuth2ClientBaseVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientBaseVO.java similarity index 97% rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/client/OAuth2ClientBaseVO.java rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientBaseVO.java index 4c13263e5..786bc5267 100755 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/client/OAuth2ClientBaseVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientBaseVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.system.controller.admin.auth.vo.client; +package cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/client/OAuth2ClientCreateReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientCreateReqVO.java similarity index 77% rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/client/OAuth2ClientCreateReqVO.java rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientCreateReqVO.java index f49735ec5..b241277ac 100755 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/client/OAuth2ClientCreateReqVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientCreateReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.system.controller.admin.auth.vo.client; +package cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client; import lombok.*; import io.swagger.annotations.*; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/client/OAuth2ClientPageReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientPageReqVO.java similarity index 85% rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/client/OAuth2ClientPageReqVO.java rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientPageReqVO.java index aa6434a28..b6d2a2172 100755 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/client/OAuth2ClientPageReqVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientPageReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.system.controller.admin.auth.vo.client; +package cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client; import lombok.*; import io.swagger.annotations.*; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/client/OAuth2ClientRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientRespVO.java similarity index 88% rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/client/OAuth2ClientRespVO.java rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientRespVO.java index 6416d7438..e0df90f3d 100755 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/client/OAuth2ClientRespVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientRespVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.system.controller.admin.auth.vo.client; +package cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/client/OAuth2ClientUpdateReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientUpdateReqVO.java similarity index 87% rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/client/OAuth2ClientUpdateReqVO.java rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientUpdateReqVO.java index 27479050c..3b1d2a0f3 100755 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/client/OAuth2ClientUpdateReqVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientUpdateReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.system.controller.admin.auth.vo.client; +package cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/token/OAuth2AccessTokenPageReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/token/OAuth2AccessTokenPageReqVO.java similarity index 90% rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/token/OAuth2AccessTokenPageReqVO.java rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/token/OAuth2AccessTokenPageReqVO.java index 41dff4af3..65103adc6 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/token/OAuth2AccessTokenPageReqVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/token/OAuth2AccessTokenPageReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.system.controller.admin.auth.vo.token; +package cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token; import cn.iocoder.yudao.framework.common.pojo.PageParam; import io.swagger.annotations.ApiModel; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/token/OAuth2AccessTokenRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/token/OAuth2AccessTokenRespVO.java similarity index 94% rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/token/OAuth2AccessTokenRespVO.java rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/token/OAuth2AccessTokenRespVO.java index 678a2156d..b1d5a48ad 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/token/OAuth2AccessTokenRespVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/token/OAuth2AccessTokenRespVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.system.controller.admin.auth.vo.token; +package cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java index 9f8a32225..d03cf7960 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO; import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.*; +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*; import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/OAuth2ClientConvert.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/OAuth2ClientConvert.java index 91ccb4ea8..021eea271 100755 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/OAuth2ClientConvert.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/OAuth2ClientConvert.java @@ -1,9 +1,9 @@ package cn.iocoder.yudao.module.system.convert.auth; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientCreateReqVO; -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientRespVO; -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientUpdateReqVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientCreateReqVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientRespVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientUpdateReqVO; import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO; import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/OAuth2TokenConvert.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/OAuth2TokenConvert.java index 882f69723..0530ebe04 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/OAuth2TokenConvert.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/OAuth2TokenConvert.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.system.convert.auth; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCheckRespDTO; import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenRespDTO; -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.token.OAuth2AccessTokenRespVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token.OAuth2AccessTokenRespVO; import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2AccessTokenMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2AccessTokenMapper.java index 6fadaf527..b3d8e4e0b 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2AccessTokenMapper.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2AccessTokenMapper.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.system.dal.mysql.auth; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.token.OAuth2AccessTokenPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token.OAuth2AccessTokenPageReqVO; import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; import org.apache.ibatis.annotations.Mapper; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2ClientMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2ClientMapper.java index ce128e164..a3f9093d7 100755 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2ClientMapper.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2ClientMapper.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.system.dal.mysql.auth; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientPageReqVO; import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/auth/OAuth2ClientRefreshConsumer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/auth/OAuth2ClientRefreshConsumer.java index f41dd3632..fc765c425 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/auth/OAuth2ClientRefreshConsumer.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/auth/OAuth2ClientRefreshConsumer.java @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.system.mq.consumer.auth; import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener; import cn.iocoder.yudao.module.system.mq.message.auth.OAuth2ClientRefreshMessage; -import cn.iocoder.yudao.module.system.service.auth.OAuth2ClientService; +import cn.iocoder.yudao.module.system.service.oauth2.OAuth2ClientService; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java index 5f67f9696..ec8b31bd5 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.system.service.auth; -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.*; +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*; import javax.validation.Valid; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java index 66c00a233..55ffaae81 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java @@ -8,7 +8,7 @@ import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO; import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.*; +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*; import cn.iocoder.yudao.module.system.convert.auth.AuthConvert; import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; @@ -19,6 +19,7 @@ import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; import cn.iocoder.yudao.module.system.service.common.CaptchaService; import cn.iocoder.yudao.module.system.service.logger.LoginLogService; import cn.iocoder.yudao.module.system.service.member.MemberService; +import cn.iocoder.yudao.module.system.service.oauth2.OAuth2TokenService; import cn.iocoder.yudao.module.system.service.social.SocialUserService; import cn.iocoder.yudao.module.system.service.user.AdminUserService; import com.google.common.annotations.VisibleForTesting; 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/oauth2/OAuth2ApproveService.java similarity index 62% rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ApproveService.java rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ApproveService.java index 991e81b99..bd1f1685c 100644 --- 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/oauth2/OAuth2ApproveService.java @@ -1,6 +1,7 @@ -package cn.iocoder.yudao.module.system.service.auth; +package cn.iocoder.yudao.module.system.service.oauth2; import java.util.Collection; +import java.util.Map; /** * OAuth2 批准 Service 接口 @@ -24,4 +25,15 @@ public interface OAuth2ApproveService { */ boolean checkForPreApproval(Long userId, Integer userType, String clientId, Collection scopes); + /** + * 在用户发起批准时,基于 scopes 的选项,计算最终是否通过 + * + * @param userId 用户编号 + * @param userType 用户类型 + * @param clientId 客户端编号 + * @param scopes 授权范围 + * @return 是否授权通过 + */ + boolean updateAfterApproval(Long userId, Integer userType, String clientId, Map 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/oauth2/OAuth2ApproveServiceImpl.java similarity index 58% rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ApproveServiceImpl.java rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ApproveServiceImpl.java index 17d38a8b7..76e9af6c3 100644 --- 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/oauth2/OAuth2ApproveServiceImpl.java @@ -1,8 +1,9 @@ -package cn.iocoder.yudao.module.system.service.auth; +package cn.iocoder.yudao.module.system.service.oauth2; import org.springframework.stereotype.Service; import java.util.Collection; +import java.util.Map; /** * OAuth2 批准 Service 实现类 @@ -14,7 +15,12 @@ public class OAuth2ApproveServiceImpl implements OAuth2ApproveService { @Override public boolean checkForPreApproval(Long userId, Integer userType, String clientId, Collection scopes) { - return false; + return true; + } + + @Override + public boolean updateAfterApproval(Long userId, Integer userType, String clientId, Map scopes) { + return true; } } 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/oauth2/OAuth2ClientService.java similarity index 85% rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ClientService.java rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientService.java index 5915610b6..3c1408c40 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/oauth2/OAuth2ClientService.java @@ -1,9 +1,9 @@ -package cn.iocoder.yudao.module.system.service.auth; +package cn.iocoder.yudao.module.system.service.oauth2; import cn.iocoder.yudao.framework.common.pojo.PageResult; -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; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientCreateReqVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientUpdateReqVO; import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO; import javax.validation.Valid; 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/oauth2/OAuth2ClientServiceImpl.java similarity index 95% rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ClientServiceImpl.java rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImpl.java index c56562ef9..501c9eefe 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/oauth2/OAuth2ClientServiceImpl.java @@ -1,13 +1,13 @@ -package cn.iocoder.yudao.module.system.service.auth; +package cn.iocoder.yudao.module.system.service.oauth2; 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; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientCreateReqVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientUpdateReqVO; import cn.iocoder.yudao.module.system.convert.auth.OAuth2ClientConvert; import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO; import cn.iocoder.yudao.module.system.dal.mysql.auth.OAuth2ClientMapper; 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/oauth2/OAuth2CodeService.java similarity index 80% rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2CodeService.java rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeService.java index a4a38b1bc..37b5f54d5 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/oauth2/OAuth2CodeService.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.system.service.auth; +package cn.iocoder.yudao.module.system.service.oauth2; /** * OAuth2.0 授权码 Service 接口 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/oauth2/OAuth2GrantService.java similarity index 79% rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminOAuth2Service.java rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantService.java index 7910e27fd..35e1a26e7 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/oauth2/OAuth2GrantService.java @@ -1,11 +1,13 @@ -package cn.iocoder.yudao.module.system.service.auth; +package cn.iocoder.yudao.module.system.service.oauth2; import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; import java.util.Collection; /** - * 管理后台的 OAuth2 Service 接口 + * OAuth2 授予 Service 接口 + * + * 从功能上,和 Spring Security OAuth 的 TokenGranter 的功能,提供访问令牌、刷新令牌的操作 * * 将自身的 AdminUser 用户,授权给第三方应用,采用 OAuth2.0 的协议。 * @@ -14,7 +16,7 @@ import java.util.Collection; * * @author 芋道源码 */ -public interface AdminOAuth2Service { +public interface OAuth2GrantService { // ImplicitTokenGranter OAuth2AccessTokenDO grantImplicit(Long userId, Integer userType, diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImpl.java new file mode 100644 index 000000000..29b658a4e --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImpl.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.system.service.oauth2; + +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; +import org.springframework.stereotype.Service; + +import java.util.Collection; + +/** + * OAuth2 授予 Service 实现类 + * + * @author 芋道源码 + */ +@Service +public class OAuth2GrantServiceImpl implements OAuth2GrantService { + + @Override + public OAuth2AccessTokenDO grantImplicit(Long userId, Integer userType, + String clientId, Collection scopes) { + return null; + } + + @Override + public String grantAuthorizationCode(Long userId, Integer userType, + String clientId, Collection scopes, + String redirectUri, String state) { + return null; + } + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2TokenService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenService.java similarity index 93% rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2TokenService.java rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenService.java index ad373bcfb..d8fbf7bd2 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2TokenService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenService.java @@ -1,7 +1,7 @@ -package cn.iocoder.yudao.module.system.service.auth; +package cn.iocoder.yudao.module.system.service.oauth2; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.token.OAuth2AccessTokenPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token.OAuth2AccessTokenPageReqVO; import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; /** diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2TokenServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImpl.java similarity index 97% rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2TokenServiceImpl.java rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImpl.java index 31b17be16..410dc37e9 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2TokenServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImpl.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.system.service.auth; +package cn.iocoder.yudao.module.system.service.oauth2; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.IdUtil; @@ -7,7 +7,7 @@ import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstant import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.token.OAuth2AccessTokenPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token.OAuth2AccessTokenPageReqVO; import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO; import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2RefreshTokenDO; diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AuthServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AuthServiceImplTest.java index b68ef207b..cd2e5f019 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AuthServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AuthServiceImplTest.java @@ -5,8 +5,8 @@ import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; import cn.iocoder.yudao.framework.test.core.util.AssertUtils; import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.AuthLoginReqVO; -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.AuthLoginRespVO; +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthLoginReqVO; +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthLoginRespVO; import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; @@ -14,6 +14,7 @@ import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum; import cn.iocoder.yudao.module.system.service.common.CaptchaService; import cn.iocoder.yudao.module.system.service.logger.LoginLogService; import cn.iocoder.yudao.module.system.service.member.MemberService; +import cn.iocoder.yudao.module.system.service.oauth2.OAuth2TokenService; import cn.iocoder.yudao.module.system.service.social.SocialUserService; import cn.iocoder.yudao.module.system.service.user.AdminUserService; import org.junit.jupiter.api.BeforeEach; diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ClientServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ClientServiceImplTest.java index 1fb688d6a..bc22d23d4 100755 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ClientServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ClientServiceImplTest.java @@ -3,12 +3,13 @@ package cn.iocoder.yudao.module.system.service.auth; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -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; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientCreateReqVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientUpdateReqVO; import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO; import cn.iocoder.yudao.module.system.dal.mysql.auth.OAuth2ClientMapper; import cn.iocoder.yudao.module.system.mq.producer.auth.OAuth2ClientProducer; +import cn.iocoder.yudao.module.system.service.oauth2.OAuth2ClientServiceImpl; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.boot.test.mock.mockito.MockBean; From 6ca88277d80a6f13ec69698bc3735bfe98538645 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 14 May 2022 23:47:34 +0800 Subject: [PATCH 04/13] =?UTF-8?q?=E5=AE=8C=E6=88=90=20oauth2=20implicit=20?= =?UTF-8?q?=E7=AE=80=E5=8C=96=E6=A8=A1=E5=BC=8F=E7=9A=84=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../framework/common/util/http/HttpUtils.java | 70 +++++++++++++++++++ .../core/util/SecurityFrameworkUtils.java | 4 +- .../dto/OAuth2AccessTokenCreateReqDTO.java | 5 ++ .../system/enums/ErrorCodeConstants.java | 2 +- .../system/api/auth/OAuth2TokenApiImpl.java | 2 +- .../admin/oauth2/OAuth2Controller.http | 7 ++ .../admin/oauth2/OAuth2Controller.java | 40 ++++++----- .../dataobject/auth/OAuth2AccessTokenDO.java | 8 +++ .../dataobject/auth/OAuth2RefreshTokenDO.java | 8 +++ .../service/auth/AdminAuthServiceImpl.java | 2 +- .../oauth2/OAuth2ClientServiceImpl.java | 3 +- .../service/oauth2/OAuth2GrantService.java | 6 +- .../oauth2/OAuth2GrantServiceImpl.java | 11 ++- .../service/oauth2/OAuth2TokenService.java | 5 +- .../oauth2/OAuth2TokenServiceImpl.java | 12 ++-- .../system/util/oauth2/OAuth2Utils.java | 56 +++++++++++++++ .../service/auth/AuthServiceImplTest.java | 2 +- 17 files changed, 208 insertions(+), 35 deletions(-) create mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2Controller.http create mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/util/oauth2/OAuth2Utils.java diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/http/HttpUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/http/HttpUtils.java index 2eb4f34eb..7435cc76a 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/http/HttpUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/http/HttpUtils.java @@ -3,8 +3,12 @@ package cn.iocoder.yudao.framework.common.util.http; import cn.hutool.core.map.TableMap; import cn.hutool.core.net.url.UrlBuilder; import cn.hutool.core.util.ReflectUtil; +import org.springframework.web.util.UriComponents; +import org.springframework.web.util.UriComponentsBuilder; +import java.net.URI; import java.nio.charset.Charset; +import java.util.Map; /** * HTTP 工具类 @@ -25,4 +29,70 @@ public class HttpUtils { return builder.build(); } + private String append(String base, Map query, boolean fragment) { + return append(base, query, null, fragment); + } + + /** + * 拼接 URL + * + * copy from Spring Security OAuth2 的 AuthorizationEndpoint 类的 append 方法 + * + * @param base 基础 URL + * @param query 查询参数 + * @param keys query 的 key,对应的原本的 key 的映射。例如说 query 里有个 key 是 xx,实际它的 key 是 extra_xx,则通过 keys 里添加这个映射 + * @param fragment URL 的 fragment,即拼接到 # 中 + * @return 拼接后的 URL + */ + public static String append(String base, Map query, Map keys, boolean fragment) { + UriComponentsBuilder template = UriComponentsBuilder.newInstance(); + UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(base); + URI redirectUri; + try { + // assume it's encoded to start with (if it came in over the wire) + redirectUri = builder.build(true).toUri(); + } catch (Exception e) { + // ... but allow client registrations to contain hard-coded non-encoded values + redirectUri = builder.build().toUri(); + builder = UriComponentsBuilder.fromUri(redirectUri); + } + template.scheme(redirectUri.getScheme()).port(redirectUri.getPort()).host(redirectUri.getHost()) + .userInfo(redirectUri.getUserInfo()).path(redirectUri.getPath()); + + if (fragment) { + StringBuilder values = new StringBuilder(); + if (redirectUri.getFragment() != null) { + String append = redirectUri.getFragment(); + values.append(append); + } + for (String key : query.keySet()) { + if (values.length() > 0) { + values.append("&"); + } + String name = key; + if (keys != null && keys.containsKey(key)) { + name = keys.get(key); + } + values.append(name).append("={").append(key).append("}"); + } + if (values.length() > 0) { + template.fragment(values.toString()); + } + UriComponents encoded = template.build().expand(query).encode(); + builder.fragment(encoded.getFragment()); + } else { + for (String key : query.keySet()) { + String name = key; + if (keys != null && keys.containsKey(key)) { + name = keys.get(key); + } + template.queryParam(name, "{" + key + "}"); + } + template.fragment(redirectUri.getFragment()); + UriComponents encoded = template.build().expand(query).encode(); + builder.query(encoded.getQuery()); + } + return builder.build().toUriString(); + } + } diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/util/SecurityFrameworkUtils.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/util/SecurityFrameworkUtils.java index 253974539..ba56e7ad4 100644 --- a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/util/SecurityFrameworkUtils.java +++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/util/SecurityFrameworkUtils.java @@ -20,6 +20,8 @@ import java.util.Collections; */ public class SecurityFrameworkUtils { + public static final String TOKEN_TYPE = "Bearer"; + private SecurityFrameworkUtils() {} /** @@ -34,7 +36,7 @@ public class SecurityFrameworkUtils { if (!StringUtils.hasText(authorization)) { return null; } - int index = authorization.indexOf("Bearer "); + int index = authorization.indexOf(TOKEN_TYPE + " "); if (index == -1) { // 未找到 return null; } diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/dto/OAuth2AccessTokenCreateReqDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/dto/OAuth2AccessTokenCreateReqDTO.java index 09bd6d8c9..1d9b793d3 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/dto/OAuth2AccessTokenCreateReqDTO.java +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/dto/OAuth2AccessTokenCreateReqDTO.java @@ -6,6 +6,7 @@ import lombok.Data; import javax.validation.constraints.NotNull; import java.io.Serializable; +import java.util.List; /** * OAuth2.0 访问令牌创建 Request DTO @@ -31,5 +32,9 @@ public class OAuth2AccessTokenCreateReqDTO implements Serializable { */ @NotNull(message = "客户端编号不能为空") private String clientId; + /** + * 授权范围 + */ + private List scopes; } 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 f0ce4e11c..a0d4ed009 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 @@ -129,6 +129,6 @@ public interface ErrorCodeConstants { 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, "重定向地址不匹配"); + ErrorCode OAUTH2_CLIENT_REDIRECT_URI_NOT_MATCH = new ErrorCode(1002020005, "重定向地址不匹配"); } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/auth/OAuth2TokenApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/auth/OAuth2TokenApiImpl.java index 3c2c29112..41a587f11 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/auth/OAuth2TokenApiImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/auth/OAuth2TokenApiImpl.java @@ -24,7 +24,7 @@ public class OAuth2TokenApiImpl implements OAuth2TokenApi { @Override public OAuth2AccessTokenRespDTO createAccessToken(OAuth2AccessTokenCreateReqDTO reqDTO) { OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken( - reqDTO.getUserId(), reqDTO.getUserType(), reqDTO.getClientId()); + reqDTO.getUserId(), reqDTO.getUserType(), reqDTO.getClientId(), reqDTO.getScopes()); return OAuth2TokenConvert.INSTANCE.convert2(accessTokenDO); } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2Controller.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2Controller.http new file mode 100644 index 000000000..eece85e5c --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2Controller.http @@ -0,0 +1,7 @@ +### 请求 /system/oauth2/authorize 接口 => 成功 +POST {{baseUrl}}/system/oauth2/authorize +Content-Type: application/x-www-form-urlencoded +Authorization: Bearer {{token}} +tenant-id: {{adminTenentId}} + +response_type=token&client_id=default&scope={"user_info": true}&redirect_uri=https://www.iocoder.cn&auto_approve=true diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2Controller.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2Controller.java index cf5851938..875ce722c 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2Controller.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2Controller.java @@ -1,15 +1,19 @@ package cn.iocoder.yudao.module.system.controller.admin.oauth2; +import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; 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.dal.dataobject.auth.OAuth2AccessTokenDO; +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO; import cn.iocoder.yudao.module.system.enums.auth.OAuth2GrantTypeEnum; import cn.iocoder.yudao.module.system.service.oauth2.OAuth2ApproveService; import cn.iocoder.yudao.module.system.service.oauth2.OAuth2ClientService; import cn.iocoder.yudao.module.system.service.oauth2.OAuth2GrantService; +import cn.iocoder.yudao.module.system.util.oauth2.OAuth2Utils; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; @@ -26,6 +30,7 @@ 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.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; @Api(tags = "管理后台 - OAuth2.0 授权") @@ -72,16 +77,6 @@ public class OAuth2Controller { // 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()); - } - // 3. 不满足自动授权,则返回授权相关的展示信息 return null; } @@ -104,7 +99,7 @@ public class OAuth2Controller { @RequestParam("client_id") String clientId, @RequestParam(value = "scope", required = false) String scope, @RequestParam("redirect_uri") String redirectUri, - @RequestParam(value = "autoApprove") Boolean autoApprove, + @RequestParam(value = "auto_approve") Boolean autoApprove, @RequestParam(value = "state", required = false) String state) { @SuppressWarnings("unchecked") Map scopes = JsonUtils.parseObject(scope, Map.class); @@ -115,27 +110,28 @@ public class OAuth2Controller { // 1.1 校验 responseType 是否满足 code 或者 token 值 OAuth2GrantTypeEnum grantTypeEnum = getGrantTypeEnum(responseType); // 1.2 校验 redirectUri 重定向域名是否合法 + 校验 scope 是否在 Client 授权范围内 - oauth2ClientService.validOAuthClientFromCache(clientId, grantTypeEnum.getGrantType(), scopes.keySet(), redirectUri); + OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientId, grantTypeEnum.getGrantType(), scopes.keySet(), redirectUri); // 2.1 假设 approved 为 null,说明是场景一 if (Boolean.TRUE.equals(autoApprove)) { // 如果无法自动授权通过,则返回空 url,前端不进行跳转 - if (!oauth2ApproveService.checkForPreApproval(getLoginUserId(), UserTypeEnum.ADMIN.getValue(), clientId, scopes.keySet())) { + if (!oauth2ApproveService.checkForPreApproval(getLoginUserId(), getUserType(), clientId, scopes.keySet())) { return success(null); } } else { // 2.2 假设 approved 非 null,说明是场景二 // 如果计算后不通过,则跳转一个错误链接 - if (!oauth2ApproveService.updateAfterApproval(getLoginUserId(), UserTypeEnum.ADMIN.getValue(), clientId, scopes)) { + if (!oauth2ApproveService.updateAfterApproval(getLoginUserId(), getUserType(), clientId, scopes)) { return success("TODO"); } } // 3.1 如果是 code 授权码模式,则发放 code 授权码,并重定向 + List approveScopes = convertList(scopes.entrySet(), Map.Entry::getKey, Map.Entry::getValue); if (grantTypeEnum == OAuth2GrantTypeEnum.AUTHORIZATION_CODE) { return success(getAuthorizationCodeRedirect()); } // 3.2 如果是 token 则是 implicit 简化模式,则发送 accessToken 访问令牌,并重定向 - return success(getImplicitGrantRedirect()); + return success(getImplicitGrantRedirect(getLoginUserId(), client, redirectUri, state, approveScopes)); } private static OAuth2GrantTypeEnum getGrantTypeEnum(String responseType) { @@ -148,12 +144,22 @@ public class OAuth2Controller { throw exception0(BAD_REQUEST.getCode(), "response_type 参数值允许 code 和 token"); } - private String getImplicitGrantRedirect() { - return ""; + private String getImplicitGrantRedirect(Long userId, OAuth2ClientDO client, + String redirectUri, String state, List scopes) { + OAuth2AccessTokenDO accessTokenDO = oAuth2GrantService.grantImplicit(userId, getUserType(), client.getClientId(), scopes); + Assert.notNull(accessTokenDO, "访问令牌不能为空"); // 防御性检查 + // 拼接 URL + // noinspection unchecked + return OAuth2Utils.buildImplicitRedirectUri(redirectUri, accessTokenDO.getAccessToken(), state, accessTokenDO.getExpiresTime(), + scopes, JsonUtils.parseObject(client.getAdditionalInformation(), Map.class)); } private String getAuthorizationCodeRedirect() { return ""; } + private Integer getUserType() { + return UserTypeEnum.ADMIN.getValue(); + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2AccessTokenDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2AccessTokenDO.java index edcc166a6..e08f2b7db 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2AccessTokenDO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2AccessTokenDO.java @@ -3,13 +3,16 @@ package cn.iocoder.yudao.module.system.dal.dataobject.auth; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors; import java.util.Date; +import java.util.List; /** * OAuth2 访问令牌 DO @@ -55,6 +58,11 @@ public class OAuth2AccessTokenDO extends TenantBaseDO { * 关联 {@link OAuth2ClientDO#getId()} */ private String clientId; + /** + * 授权范围 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List scopes; /** * 过期时间 */ diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2RefreshTokenDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2RefreshTokenDO.java index d13ee258f..6575a7153 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2RefreshTokenDO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2RefreshTokenDO.java @@ -3,12 +3,15 @@ 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 lombok.experimental.Accessors; import java.util.Date; +import java.util.List; /** * OAuth2 刷新令牌 @@ -47,6 +50,11 @@ public class OAuth2RefreshTokenDO extends BaseDO { * 关联 {@link OAuth2ClientDO#getId()} */ private String clientId; + /** + * 授权范围 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List scopes; /** * 过期时间 */ diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java index 55ffaae81..60584f0ca 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java @@ -207,7 +207,7 @@ public class AdminAuthServiceImpl implements AdminAuthService { createLoginLog(userId, username, logType, LoginResultEnum.SUCCESS); // 创建访问令牌 OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken(userId, getUserType().getValue(), - OAuth2ClientConstants.CLIENT_ID_DEFAULT); + OAuth2ClientConstants.CLIENT_ID_DEFAULT, null); // 构建返回结果 return AuthConvert.INSTANCE.convert(accessTokenDO); } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImpl.java index 501c9eefe..194d6364e 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImpl.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.system.service.oauth2; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -181,7 +182,7 @@ public class OAuth2ClientServiceImpl implements OAuth2ClientService { if (client == null) { throw exception(OAUTH2_CLIENT_EXISTS); } - if (Objects.equals(client.getStatus(), CommonStatusEnum.ENABLE.getStatus())) { + if (ObjectUtil.notEqual(client.getStatus(), CommonStatusEnum.ENABLE.getStatus())) { throw exception(OAUTH2_CLIENT_DISABLE); } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantService.java index 35e1a26e7..11c743d2e 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantService.java @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.system.service.oauth2; import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; -import java.util.Collection; +import java.util.List; /** * OAuth2 授予 Service 接口 @@ -20,11 +20,11 @@ public interface OAuth2GrantService { // ImplicitTokenGranter OAuth2AccessTokenDO grantImplicit(Long userId, Integer userType, - String clientId, Collection scopes); + String clientId, List scopes); // AuthorizationCodeTokenGranter String grantAuthorizationCode(Long userId, Integer userType, - String clientId, Collection scopes, + String clientId, List scopes, String redirectUri, String state); } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImpl.java index 29b658a4e..3af885fc9 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImpl.java @@ -3,7 +3,9 @@ package cn.iocoder.yudao.module.system.service.oauth2; import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; import org.springframework.stereotype.Service; +import javax.annotation.Resource; import java.util.Collection; +import java.util.List; /** * OAuth2 授予 Service 实现类 @@ -13,15 +15,18 @@ import java.util.Collection; @Service public class OAuth2GrantServiceImpl implements OAuth2GrantService { + @Resource + private OAuth2TokenService oauth2TokenService; + @Override public OAuth2AccessTokenDO grantImplicit(Long userId, Integer userType, - String clientId, Collection scopes) { - return null; + String clientId, List scopes) { + return oauth2TokenService.createAccessToken(userId, userType, clientId, scopes); } @Override public String grantAuthorizationCode(Long userId, Integer userType, - String clientId, Collection scopes, + String clientId, List scopes, String redirectUri, String state) { return null; } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenService.java index d8fbf7bd2..1fdce710d 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenService.java @@ -4,6 +4,8 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token.OAuth2AccessTokenPageReqVO; import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; +import java.util.List; + /** * OAuth2.0 Token Service 接口 * @@ -22,9 +24,10 @@ public interface OAuth2TokenService { * @param userId 用户编号 * @param userType 用户类型 * @param clientId 客户端编号 + * @param scopes 授权范围 * @return 访问令牌的信息 */ - OAuth2AccessTokenDO createAccessToken(Long userId, Integer userType, String clientId); + OAuth2AccessTokenDO createAccessToken(Long userId, Integer userType, String clientId, List scopes); /** * 刷新访问令牌 diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImpl.java index 410dc37e9..a476f04f5 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImpl.java @@ -45,10 +45,10 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService { @Override @Transactional - public OAuth2AccessTokenDO createAccessToken(Long userId, Integer userType, String clientId) { + public OAuth2AccessTokenDO createAccessToken(Long userId, Integer userType, String clientId, List scopes) { OAuth2ClientDO clientDO = oauth2ClientService.validOAuthClientFromCache(clientId); // 创建刷新令牌 - OAuth2RefreshTokenDO refreshTokenDO = createOAuth2RefreshToken(userId, userType, clientDO); + OAuth2RefreshTokenDO refreshTokenDO = createOAuth2RefreshToken(userId, userType, clientDO, scopes); // 创建访问令牌 return createOAuth2AccessToken(refreshTokenDO, clientDO); } @@ -134,7 +134,8 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService { private OAuth2AccessTokenDO createOAuth2AccessToken(OAuth2RefreshTokenDO refreshTokenDO, OAuth2ClientDO clientDO) { OAuth2AccessTokenDO accessTokenDO = new OAuth2AccessTokenDO().setAccessToken(generateAccessToken()) - .setUserId(refreshTokenDO.getUserId()).setUserType(refreshTokenDO.getUserType()).setClientId(clientDO.getClientId()) + .setUserId(refreshTokenDO.getUserId()).setUserType(refreshTokenDO.getUserType()) + .setClientId(clientDO.getClientId()).setScopes(refreshTokenDO.getScopes()) .setRefreshToken(refreshTokenDO.getRefreshToken()) .setExpiresTime(DateUtils.addDate(Calendar.SECOND, clientDO.getAccessTokenValiditySeconds())); accessTokenDO.setTenantId(TenantContextHolder.getTenantId()); // 手动设置租户编号,避免缓存到 Redis 的时候,无对应的租户编号 @@ -144,9 +145,10 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService { return accessTokenDO; } - private OAuth2RefreshTokenDO createOAuth2RefreshToken(Long userId, Integer userType, OAuth2ClientDO clientDO) { + private OAuth2RefreshTokenDO createOAuth2RefreshToken(Long userId, Integer userType, OAuth2ClientDO clientDO, List scopes) { OAuth2RefreshTokenDO refreshToken = new OAuth2RefreshTokenDO().setRefreshToken(generateRefreshToken()) - .setUserId(userId).setUserType(userType).setClientId(clientDO.getClientId()) + .setUserId(userId).setUserType(userType) + .setClientId(clientDO.getClientId()).setScopes(scopes) .setExpiresTime(DateUtils.addDate(Calendar.SECOND, clientDO.getRefreshTokenValiditySeconds())); oauth2RefreshTokenMapper.insert(refreshToken); return refreshToken; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/util/oauth2/OAuth2Utils.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/util/oauth2/OAuth2Utils.java new file mode 100644 index 000000000..c4321e536 --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/util/oauth2/OAuth2Utils.java @@ -0,0 +1,56 @@ +package cn.iocoder.yudao.module.system.util.oauth2; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.util.http.HttpUtils; +import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; + +import java.util.*; + +/** + * OAuth2 相关的工具类 + * + * @author 芋道源码 + */ +public class OAuth2Utils { + + /** + * 构建简化模式下,重定向的 URI + * + * copy from Spring Security OAuth2 的 AuthorizationEndpoint 类的 appendAccessToken 方法 + * + * @param redirectUri 重定向 URI + * @param accessToken 访问令牌 + * @param state 状态 + * @param expireTime 过期时间 + * @param scopes 授权范围 + * @param additionalInformation 附加信息 + * @return 简化授权模式下的重定向 URI + */ + public static String buildImplicitRedirectUri(String redirectUri, String accessToken, String state, Date expireTime, + Collection scopes, Map additionalInformation) { + Map vars = new LinkedHashMap(); + Map keys = new HashMap(); + vars.put("access_token", accessToken); + vars.put("token_type", SecurityFrameworkUtils.TOKEN_TYPE.toLowerCase()); + if (state != null) { + vars.put("state", state); + } + if (expireTime != null) { + long expires_in = (expireTime.getTime() - System.currentTimeMillis()) / 1000; + vars.put("expires_in", expires_in); + } + if (CollUtil.isNotEmpty(scopes)) { + vars.put("scope", CollUtil.join(scopes, " ")); + } + for (String key : additionalInformation.keySet()) { + Object value = additionalInformation.get(key); + if (value != null) { + keys.put("extra_" + key, key); + vars.put("extra_" + key, value); + } + } + // Do not include the refresh token (even if there is one) + return HttpUtils.append(redirectUri, vars, keys, true); + } + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AuthServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AuthServiceImplTest.java index cd2e5f019..49f378730 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AuthServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AuthServiceImplTest.java @@ -201,7 +201,7 @@ public class AuthServiceImplTest extends BaseDbUnitTest { // mock 缓存登录用户到 Redis OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class, o -> o.setUserId(1L) .setUserType(UserTypeEnum.ADMIN.getValue())); - when(oauth2TokenService.createAccessToken(eq(1L), eq(UserTypeEnum.ADMIN.getValue()), eq("default"))) + when(oauth2TokenService.createAccessToken(eq(1L), eq(UserTypeEnum.ADMIN.getValue()), eq("default"), isNull())) .thenReturn(accessTokenDO); // 调用, 并断言异常 From 66034d26c06cbb7ad37e2e6579299260de9b728e Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 15 May 2022 00:20:52 +0800 Subject: [PATCH 05/13] =?UTF-8?q?=E5=AE=8C=E6=88=90=20oauth2=20code=20?= =?UTF-8?q?=E6=8E=88=E6=9D=83=E7=A0=81=E6=A8=A1=E5=BC=8F=E7=9A=84=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/oauth2/OAuth2Controller.http | 10 ++++++- .../admin/oauth2/OAuth2Controller.java | 25 ++++++++++------ .../oauth2/OAuth2ApproveServiceImpl.java | 2 +- .../oauth2/OAuth2GrantServiceImpl.java | 3 +- .../system/util/oauth2/OAuth2Utils.java | 30 +++++++++++++++++++ 5 files changed, 57 insertions(+), 13 deletions(-) diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2Controller.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2Controller.http index eece85e5c..cd4d0f3b2 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2Controller.http +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2Controller.http @@ -1,7 +1,15 @@ -### 请求 /system/oauth2/authorize 接口 => 成功 +### 请求 /system/oauth2/authorize + token 接口 => 成功 POST {{baseUrl}}/system/oauth2/authorize Content-Type: application/x-www-form-urlencoded Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} response_type=token&client_id=default&scope={"user_info": true}&redirect_uri=https://www.iocoder.cn&auto_approve=true + +### 请求 /system/oauth2/authorize + code 接口 => 成功 +POST {{baseUrl}}/system/oauth2/authorize +Content-Type: application/x-www-form-urlencoded +Authorization: Bearer {{token}} +tenant-id: {{adminTenentId}} + +response_type=code&client_id=default&scope={"user_info": true}&redirect_uri=https://www.iocoder.cn&auto_approve=true diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2Controller.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2Controller.java index 875ce722c..e528890a1 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2Controller.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2Controller.java @@ -49,7 +49,7 @@ public class OAuth2Controller { // GET oauth/authorize AuthorizationEndpoint @Resource - private OAuth2GrantService oAuth2GrantService; + private OAuth2GrantService oauth2GrantService; @Resource private OAuth2ClientService oauth2ClientService; @Resource @@ -121,17 +121,18 @@ public class OAuth2Controller { } else { // 2.2 假设 approved 非 null,说明是场景二 // 如果计算后不通过,则跳转一个错误链接 if (!oauth2ApproveService.updateAfterApproval(getLoginUserId(), getUserType(), clientId, scopes)) { - return success("TODO"); + return success(OAuth2Utils.buildUnsuccessfulRedirect(redirectUri, responseType, state, + "access_denied", "User denied access")); } } // 3.1 如果是 code 授权码模式,则发放 code 授权码,并重定向 List approveScopes = convertList(scopes.entrySet(), Map.Entry::getKey, Map.Entry::getValue); if (grantTypeEnum == OAuth2GrantTypeEnum.AUTHORIZATION_CODE) { - return success(getAuthorizationCodeRedirect()); + return success(getAuthorizationCodeRedirect(getLoginUserId(), client, approveScopes, redirectUri, state)); } // 3.2 如果是 token 则是 implicit 简化模式,则发送 accessToken 访问令牌,并重定向 - return success(getImplicitGrantRedirect(getLoginUserId(), client, redirectUri, state, approveScopes)); + return success(getImplicitGrantRedirect(getLoginUserId(), client, approveScopes, redirectUri, state)); } private static OAuth2GrantTypeEnum getGrantTypeEnum(String responseType) { @@ -145,17 +146,23 @@ public class OAuth2Controller { } private String getImplicitGrantRedirect(Long userId, OAuth2ClientDO client, - String redirectUri, String state, List scopes) { - OAuth2AccessTokenDO accessTokenDO = oAuth2GrantService.grantImplicit(userId, getUserType(), client.getClientId(), scopes); + List scopes, String redirectUri, String state) { + // 1. 创建 access token 访问令牌 + OAuth2AccessTokenDO accessTokenDO = oauth2GrantService.grantImplicit(userId, getUserType(), client.getClientId(), scopes); Assert.notNull(accessTokenDO, "访问令牌不能为空"); // 防御性检查 - // 拼接 URL + // 2. 拼接重定向的 URL // noinspection unchecked return OAuth2Utils.buildImplicitRedirectUri(redirectUri, accessTokenDO.getAccessToken(), state, accessTokenDO.getExpiresTime(), scopes, JsonUtils.parseObject(client.getAdditionalInformation(), Map.class)); } - private String getAuthorizationCodeRedirect() { - return ""; + private String getAuthorizationCodeRedirect(Long userId, OAuth2ClientDO client, + List scopes, String redirectUri, String state) { + // 1. 创建 code 授权码 + String authorizationCode = oauth2GrantService.grantAuthorizationCode(userId,getUserType(), client.getClientId(), scopes, + redirectUri, state); + // 2. 拼接重定向的 URL + return OAuth2Utils.buildAuthorizationCodeRedirectUri(redirectUri, authorizationCode, state); } private Integer getUserType() { diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ApproveServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ApproveServiceImpl.java index 76e9af6c3..18191caac 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ApproveServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ApproveServiceImpl.java @@ -20,7 +20,7 @@ public class OAuth2ApproveServiceImpl implements OAuth2ApproveService { @Override public boolean updateAfterApproval(Long userId, Integer userType, String clientId, Map scopes) { - return true; + return false; } } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImpl.java index 3af885fc9..22dad3c88 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImpl.java @@ -4,7 +4,6 @@ import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; import org.springframework.stereotype.Service; import javax.annotation.Resource; -import java.util.Collection; import java.util.List; /** @@ -28,7 +27,7 @@ public class OAuth2GrantServiceImpl implements OAuth2GrantService { public String grantAuthorizationCode(Long userId, Integer userType, String clientId, List scopes, String redirectUri, String state) { - return null; + return "test"; } } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/util/oauth2/OAuth2Utils.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/util/oauth2/OAuth2Utils.java index c4321e536..0cdc88c5f 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/util/oauth2/OAuth2Utils.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/util/oauth2/OAuth2Utils.java @@ -13,6 +13,25 @@ import java.util.*; */ public class OAuth2Utils { + /** + * 构建授权码模式下,重定向的 URI + * + * copy from Spring Security OAuth2 的 AuthorizationEndpoint 类的 getSuccessfulRedirect 方法 + * + * @param redirectUri 重定向 URI + * @param authorizationCode 授权码 + * @param state 状态 + * @return 授权码模式下的重定向 URI + */ + public static String buildAuthorizationCodeRedirectUri(String redirectUri, String authorizationCode, String state) { + Map query = new LinkedHashMap<>(); + query.put("code", authorizationCode); + if (state != null) { + query.put("state", state); + } + return HttpUtils.append(redirectUri, query, null, false); + } + /** * 构建简化模式下,重定向的 URI * @@ -53,4 +72,15 @@ public class OAuth2Utils { return HttpUtils.append(redirectUri, vars, keys, true); } + public static String buildUnsuccessfulRedirect(String redirectUri, String responseType, String state, + String error, String description) { + Map query = new LinkedHashMap(); + query.put("error", error); + query.put("error_description", description); + if (state != null) { + query.put("state", state); + } + return HttpUtils.append(redirectUri, query, null, !responseType.contains("code")); + } + } From 99ba7ccee8a59b52614ee01649c535272b8ff734 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 15 May 2022 15:59:49 +0800 Subject: [PATCH 06/13] =?UTF-8?q?=E5=AE=8C=E6=88=90=20oauth2=20code=20?= =?UTF-8?q?=E6=8E=88=E6=9D=83=E7=A0=81=E6=A8=A1=E5=BC=8F=E7=9A=84=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../framework/common/util/http/HttpUtils.java | 28 +++++++ .../core/util/SecurityFrameworkUtils.java | 4 +- .../system/enums/ErrorCodeConstants.java | 11 ++- .../enums/auth/OAuth2GrantTypeEnum.java | 5 ++ .../admin/oauth2/OAuth2Controller.http | 8 ++ ...troller.java => OAuth2OpenController.java} | 84 ++++++++++++++++--- .../vo/open/OAuth2OpenAccessTokenRespVO.java | 32 +++++++ .../convert/oauth2/OAuth2OpenConvert.java | 24 ++++++ .../config/SecurityConfiguration.java | 2 + .../service/oauth2/OAuth2ClientService.java | 7 +- .../oauth2/OAuth2ClientServiceImpl.java | 9 +- .../service/oauth2/OAuth2GrantService.java | 34 +++++++- .../oauth2/OAuth2GrantServiceImpl.java | 41 ++++++++- .../system/util/oauth2/OAuth2Utils.java | 9 +- 14 files changed, 267 insertions(+), 31 deletions(-) rename yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/{OAuth2Controller.java => OAuth2OpenController.java} (68%) create mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenAccessTokenRespVO.java create mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/oauth2/OAuth2OpenConvert.java diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/http/HttpUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/http/HttpUtils.java index 7435cc76a..d36b2c308 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/http/HttpUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/http/HttpUtils.java @@ -1,11 +1,15 @@ package cn.iocoder.yudao.framework.common.util.http; +import cn.hutool.core.codec.Base64; import cn.hutool.core.map.TableMap; import cn.hutool.core.net.url.UrlBuilder; import cn.hutool.core.util.ReflectUtil; +import cn.hutool.core.util.StrUtil; +import org.springframework.util.StringUtils; import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; +import javax.servlet.http.HttpServletRequest; import java.net.URI; import java.nio.charset.Charset; import java.util.Map; @@ -95,4 +99,28 @@ public class HttpUtils { return builder.build().toUriString(); } + public static String[] obtainBasicAuthorization(HttpServletRequest request) { + String clientId; + String clientSecret; + // 先从 Header 中获取 + String authorization = request.getHeader("Authorization"); + authorization = StrUtil.subAfter(authorization, "Basic ", true); + if (StringUtils.hasText(authorization)) { + authorization = Base64.decodeStr(authorization); + clientId = StrUtil.subBefore(authorization, ":", false); + clientSecret = StrUtil.subAfter(authorization, ":", false); + // 再从 Param 中获取 + } else { + clientId = request.getParameter("client_id"); + clientSecret = request.getParameter("client_secret"); + } + + // 如果两者非空,则返回 + if (StrUtil.isNotEmpty(clientId) && StrUtil.isNotEmpty(clientSecret)) { + return new String[]{clientId, clientSecret}; + } + return null; + } + + } diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/util/SecurityFrameworkUtils.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/util/SecurityFrameworkUtils.java index ba56e7ad4..5dc17b626 100644 --- a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/util/SecurityFrameworkUtils.java +++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/util/SecurityFrameworkUtils.java @@ -20,7 +20,7 @@ import java.util.Collections; */ public class SecurityFrameworkUtils { - public static final String TOKEN_TYPE = "Bearer"; + public static final String AUTHORIZATION_BEARER = "Bearer"; private SecurityFrameworkUtils() {} @@ -36,7 +36,7 @@ public class SecurityFrameworkUtils { if (!StringUtils.hasText(authorization)) { return null; } - int index = authorization.indexOf(TOKEN_TYPE + " "); + int index = authorization.indexOf(AUTHORIZATION_BEARER + " "); if (index == -1) { // 未找到 return null; } 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 a0d4ed009..24aa5e127 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 @@ -123,12 +123,19 @@ public interface ErrorCodeConstants { ErrorCode SENSITIVE_WORD_NOT_EXISTS = new ErrorCode(1002019000, "系统敏感词在所有标签中都不存在"); ErrorCode SENSITIVE_WORD_EXISTS = new ErrorCode(1002019001, "系统敏感词已在标签中存在"); - // ========== 系统敏感词 1002020000 ========= + // ========== OAuth2 客户端 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(1002020005, "重定向地址不匹配"); + ErrorCode OAUTH2_CLIENT_REDIRECT_URI_NOT_MATCH = new ErrorCode(1002020005, "无效 redirect_uri: {}"); + ErrorCode OAUTH2_CLIENT_CLIENT_SECRET_ERROR = new ErrorCode(1002020006, "无效 client_secret: {}"); + + // ========== OAuth2 授权 1002021000 ========= + ErrorCode OAUTH2_GRANT_CLIENT_ID_MISMATCH = new ErrorCode(1002020000, "client_id 不匹配"); + ErrorCode OAUTH2_GRANT_REDIRECT_URI_MISMATCH = new ErrorCode(1002020001, "redirect_uri 不匹配"); + ErrorCode OAUTH2_GRANT_STATE_MISMATCH = new ErrorCode(1002020002, "state 不匹配"); + ErrorCode OAUTH2_GRANT_CODE_NOT_EXISTS = new ErrorCode(1002020003, "code 不存在"); } diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/auth/OAuth2GrantTypeEnum.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/auth/OAuth2GrantTypeEnum.java index f2394b7af..20eb8fce3 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/auth/OAuth2GrantTypeEnum.java +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/auth/OAuth2GrantTypeEnum.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.system.enums.auth; +import cn.hutool.core.util.ArrayUtil; import lombok.AllArgsConstructor; import lombok.Getter; @@ -21,4 +22,8 @@ public enum OAuth2GrantTypeEnum { private final String grantType; + public static OAuth2GrantTypeEnum getByGranType(String grantType) { + return ArrayUtil.firstMatch(o -> o.getGrantType().equals(grantType), values()); + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2Controller.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2Controller.http index cd4d0f3b2..ce7cf83d8 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2Controller.http +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2Controller.http @@ -13,3 +13,11 @@ Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} response_type=code&client_id=default&scope={"user_info": true}&redirect_uri=https://www.iocoder.cn&auto_approve=true + +### 请求 /system/oauth2/token + code 接口 => 成功 +POST {{baseUrl}}/system/oauth2/token +Content-Type: application/x-www-form-urlencoded +Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw== +tenant-id: {{adminTenentId}} + +grant_type=authorization_code&redirect_uri=https://www.iocoder.cn diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2Controller.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.java similarity index 68% rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2Controller.java rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.java index e528890a1..5d7e0bbd2 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2Controller.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.java @@ -1,12 +1,16 @@ package cn.iocoder.yudao.module.system.controller.admin.oauth2; import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.http.HttpUtils; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenAccessTokenRespVO; +import cn.iocoder.yudao.module.system.convert.oauth2.OAuth2OpenConvert; import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO; import cn.iocoder.yudao.module.system.enums.auth.OAuth2GrantTypeEnum; @@ -23,6 +27,7 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; import java.util.Collections; import java.util.List; import java.util.Map; @@ -33,21 +38,17 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; -@Api(tags = "管理后台 - OAuth2.0 授权") +@Api(tags = "管理后台 - OAuth2.0 授权") // 提供给外部应用调用为主 @RestController @RequestMapping("/system/oauth2") @Validated @Slf4j -public class OAuth2Controller { - -// POST oauth/token TokenEndpoint:Password、Implicit、Code、Refresh Token +public class OAuth2OpenController { // POST oauth/check_token CheckTokenEndpoint // DELETE oauth/token ConsumerTokenServices#revokeToken -// GET oauth/authorize AuthorizationEndpoint - @Resource private OAuth2GrantService oauth2GrantService; @Resource @@ -55,6 +56,56 @@ public class OAuth2Controller { @Resource private OAuth2ApproveService oauth2ApproveService; + @PostMapping("/token") + @ApiOperation(value = "获得访问令牌", notes = "适合 code 授权码模式,或者 implicit 简化模式;在 authorize.vue 单点登录界面被【获取】调用") + @ApiImplicitParams({ + @ApiImplicitParam(name = "grant_type", required = true, value = "授权类型", example = "code", dataTypeClass = String.class), + @ApiImplicitParam(name = "code", value = "授权范围", example = "userinfo.read", dataTypeClass = String.class), + @ApiImplicitParam(name = "redirect_uri", value = "重定向 URI", example = "https://www.iocoder.cn", dataTypeClass = String.class), + @ApiImplicitParam(name = "state", example = "123321", dataTypeClass = String.class) + }) + @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 + public CommonResult postAccessToken(HttpServletRequest request, + @RequestParam("grant_type") String grantType, + @RequestParam(value = "code", required = false) String code, // 授权码模式 + @RequestParam(value = "redirect_uri", required = false) String redirectUri, // 授权码模式 + @RequestParam(value = "state", required = false) String state) { // 授权码模式 + // 授权类型 + OAuth2GrantTypeEnum grantTypeEnum = OAuth2GrantTypeEnum.getByGranType(grantType); + if (grantTypeEnum == null) { + throw exception0(BAD_REQUEST.getCode(), StrUtil.format("未知授权类型({})", grantType)); + } + if (grantTypeEnum == OAuth2GrantTypeEnum.IMPLICIT) { + throw exception0(BAD_REQUEST.getCode(), "Token 接口不支持 implicit 授权模式"); + } + + // 校验客户端 + String[] clientIdAndSecret = HttpUtils.obtainBasicAuthorization(request); + if (ArrayUtil.isEmpty(clientIdAndSecret) || clientIdAndSecret.length != 2) { + throw exception0(BAD_REQUEST.getCode(), "client_id 或 client_secret 未正确传递"); + } + OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientIdAndSecret[0], clientIdAndSecret[1], grantType, null, null); + + // 根据授权模式,获取访问令牌 + OAuth2AccessTokenDO accessTokenDO = null; + switch (grantTypeEnum) { + case AUTHORIZATION_CODE: + accessTokenDO = oauth2GrantService.grantAuthorizationCodeForAccessToken(client.getClientId(), code, redirectUri, state); + break; + case PASSWORD: + break; + case CLIENT_CREDENTIALS: + break; + case REFRESH_TOKEN: + break; + default: + throw new IllegalArgumentException("未知授权类型:" + grantType); + } + Assert.notNull(accessTokenDO, "访问令牌不能为空"); // 防御性检查 + return success(OAuth2OpenConvert.INSTANCE.convert(accessTokenDO)); + } + + // GET oauth/authorize AuthorizationEndpoint TODO @GetMapping("/authorize") @ApiOperation(value = "获得授权信息", notes = "适合 code 授权码模式,或者 implicit 简化模式;在 authorize.vue 单点登录界面被【获取】调用") @ApiImplicitParams({ @@ -75,12 +126,23 @@ public class OAuth2Controller { // 1.1 校验 responseType 是否满足 code 或者 token 值 OAuth2GrantTypeEnum grantTypeEnum = getGrantTypeEnum(responseType); // 1.2 校验 redirectUri 重定向域名是否合法 + 校验 scope 是否在 Client 授权范围内 - oauth2ClientService.validOAuthClientFromCache(clientId, grantTypeEnum.getGrantType(), scopes, redirectUri); + oauth2ClientService.validOAuthClientFromCache(clientId, null, + grantTypeEnum.getGrantType(), scopes, redirectUri); // 3. 不满足自动授权,则返回授权相关的展示信息 return null; } + /** + * 对应 Spring Security OAuth 的 AuthorizationEndpoint 类的 approveOrDeny 方法 + * + * 场景一:【自动授权 autoApprove = true】 + * 刚进入 authorize.vue 界面,调用该接口,用户历史已经给该应用做过对应的授权,或者 OAuth2Client 支持该 scope 的自动授权 + * 场景二:【手动授权 autoApprove = false】 + * 在 authorize.vue 界面,用户选择好 scope 授权范围,调用该接口,进行授权。此时,approved 为 true 或者 false + * + * 因为前后端分离,Axios 无法很好的处理 302 重定向,所以和 Spring Security OAuth 略有不同,返回结果是重定向的 URL,剩余交给前端处理 + */ @PostMapping("/authorize") @ApiOperation(value = "申请授权", notes = "适合 code 授权码模式,或者 implicit 简化模式;在 authorize.vue 单点登录界面被【提交】调用") @ApiImplicitParams({ @@ -92,9 +154,6 @@ public class OAuth2Controller { @ApiImplicitParam(name = "state", example = "123321", dataTypeClass = String.class) }) @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 - // 场景一:【自动授权 autoApprove = true】刚进入 authorize.vue 界面,调用该接口,用户历史已经给该应用做过对应的授权,或者 OAuth2Client 支持该 scope 的自动授权 - // 场景二:【手动授权 autoApprove = false】在 authorize.vue 界面,用户选择好 scope 授权范围,调用该接口,进行授权。此时,approved 为 true 或者 false - // 因为前后端分离,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, @@ -110,7 +169,8 @@ public class OAuth2Controller { // 1.1 校验 responseType 是否满足 code 或者 token 值 OAuth2GrantTypeEnum grantTypeEnum = getGrantTypeEnum(responseType); // 1.2 校验 redirectUri 重定向域名是否合法 + 校验 scope 是否在 Client 授权范围内 - OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientId, grantTypeEnum.getGrantType(), scopes.keySet(), redirectUri); + OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientId, null, + grantTypeEnum.getGrantType(), scopes.keySet(), redirectUri); // 2.1 假设 approved 为 null,说明是场景一 if (Boolean.TRUE.equals(autoApprove)) { @@ -159,7 +219,7 @@ public class OAuth2Controller { private String getAuthorizationCodeRedirect(Long userId, OAuth2ClientDO client, List scopes, String redirectUri, String state) { // 1. 创建 code 授权码 - String authorizationCode = oauth2GrantService.grantAuthorizationCode(userId,getUserType(), client.getClientId(), scopes, + String authorizationCode = oauth2GrantService.grantAuthorizationCodeForCode(userId,getUserType(), client.getClientId(), scopes, redirectUri, state); // 2. 拼接重定向的 URL return OAuth2Utils.buildAuthorizationCodeRedirectUri(redirectUri, authorizationCode, state); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenAccessTokenRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenAccessTokenRespVO.java new file mode 100644 index 000000000..b9c697a85 --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenAccessTokenRespVO.java @@ -0,0 +1,32 @@ +package cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@ApiModel("管理后台 - 访问令牌 Response VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class OAuth2OpenAccessTokenRespVO { + + @ApiModelProperty(value = "访问令牌", required = true, example = "tudou") + @JsonProperty("access_token") + private String accessToken; + + @ApiModelProperty(value = "刷新令牌", required = true, example = "nice") + @JsonProperty("refresh_token") + private String refreshToken; + + @ApiModelProperty(value = "令牌类型", required = true, example = "bearer") + @JsonProperty("token_type") + private String tokenType; + + @ApiModelProperty(value = "过期时间", required = true, example = "42430", notes = "单位:秒") + @JsonProperty("expires_in") + private Long expiresIn; + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/oauth2/OAuth2OpenConvert.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/oauth2/OAuth2OpenConvert.java new file mode 100644 index 000000000..a873c7f28 --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/oauth2/OAuth2OpenConvert.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.system.convert.oauth2; + +import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenAccessTokenRespVO; +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; +import cn.iocoder.yudao.module.system.util.oauth2.OAuth2Utils; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface OAuth2OpenConvert { + + OAuth2OpenConvert INSTANCE = Mappers.getMapper(OAuth2OpenConvert.class); + + default OAuth2OpenAccessTokenRespVO convert(OAuth2AccessTokenDO bean) { + OAuth2OpenAccessTokenRespVO respVO = convert0(bean); + respVO.setTokenType(SecurityFrameworkUtils.AUTHORIZATION_BEARER.toLowerCase()); + respVO.setExpiresIn(OAuth2Utils.getExpiresIn(bean.getExpiresTime())); + return respVO; + } + + OAuth2OpenAccessTokenRespVO convert0(OAuth2AccessTokenDO bean); + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/security/config/SecurityConfiguration.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/security/config/SecurityConfiguration.java index 1233a2d81..94be75462 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/security/config/SecurityConfiguration.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/security/config/SecurityConfiguration.java @@ -35,6 +35,8 @@ public class SecurityConfiguration { registry.antMatchers(buildAdminApi("/system/tenant/get-id-by-name")).permitAll(); // 短信回调 API registry.antMatchers(buildAdminApi("/system/sms/callback/**")).permitAll(); + // OAuth2 API + registry.antMatchers(buildAdminApi("/system/oauth2/token")).permitAll(); } }; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientService.java index 3c1408c40..a147fcc14 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientService.java @@ -67,7 +67,7 @@ public interface OAuth2ClientService { * @return 客户端 */ default OAuth2ClientDO validOAuthClientFromCache(String clientId) { - return validOAuthClientFromCache(clientId, null, null, null); + return validOAuthClientFromCache(clientId, null, null, null, null); } /** @@ -76,12 +76,13 @@ public interface OAuth2ClientService { * 非空时,进行校验 * * @param clientId 客户端编号 + * @param clientSecret 客户端密钥 * @param authorizedGrantType 授权方式 * @param scopes 授权范围 * @param redirectUri 重定向地址 * @return 客户端 */ - OAuth2ClientDO validOAuthClientFromCache(String clientId, String authorizedGrantType, - Collection scopes, String redirectUri); + OAuth2ClientDO validOAuthClientFromCache(String clientId, String clientSecret, + 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/oauth2/OAuth2ClientServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImpl.java index 194d6364e..627197256 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImpl.java @@ -176,7 +176,8 @@ public class OAuth2ClientServiceImpl implements OAuth2ClientService { } @Override - public OAuth2ClientDO validOAuthClientFromCache(String clientId, String authorizedGrantType, Collection scopes, String redirectUri) { + public OAuth2ClientDO validOAuthClientFromCache(String clientId, String clientSecret, + String authorizedGrantType, Collection scopes, String redirectUri) { // 校验客户端存在、且开启 OAuth2ClientDO client = clientCache.get(clientId); if (client == null) { @@ -186,6 +187,10 @@ public class OAuth2ClientServiceImpl implements OAuth2ClientService { throw exception(OAUTH2_CLIENT_DISABLE); } + // 校验客户端密钥 + if (StrUtil.isNotEmpty(clientSecret) && ObjectUtil.notEqual(client.getSecret(), clientSecret)) { + throw exception(OAUTH2_CLIENT_CLIENT_SECRET_ERROR, clientSecret); + } // 校验授权方式 if (StrUtil.isNotEmpty(authorizedGrantType) && !CollUtil.contains(client.getAuthorizedGrantTypes(), authorizedGrantType)) { throw exception(OAUTH2_CLIENT_AUTHORIZED_GRANT_TYPE_NOT_EXISTS); @@ -196,7 +201,7 @@ public class OAuth2ClientServiceImpl implements OAuth2ClientService { } // 校验回调地址 if (StrUtil.isNotEmpty(redirectUri) && !StrUtils.startWithAny(redirectUri, client.getRedirectUris())) { - throw exception(OAUTH2_CLIENT_REDIRECT_URI_NOT_MATCH); + throw exception(OAUTH2_CLIENT_REDIRECT_URI_NOT_MATCH, redirectUri); } return client; } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantService.java index 11c743d2e..92d1da337 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantService.java @@ -22,9 +22,35 @@ public interface OAuth2GrantService { OAuth2AccessTokenDO grantImplicit(Long userId, Integer userType, String clientId, List scopes); - // AuthorizationCodeTokenGranter - String grantAuthorizationCode(Long userId, Integer userType, - String clientId, List scopes, - String redirectUri, String state); + /** + * 授权码模式,第一阶段,获得 code 授权码 + * + * 对应 Spring Security OAuth2 的 AuthorizationEndpoint 的 generateCode 方法 + * + * @param userId 用户编号 + * @param userType 用户类型 + * @param clientId 客户端编号 + * @param scopes 授权范围 + * @param redirectUri 重定向 URI + * @param state 状态 + * @return 授权码 + */ + String grantAuthorizationCodeForCode(Long userId, Integer userType, + String clientId, List scopes, + String redirectUri, String state); + + /** + * 授权码模式,第二阶段,获得 accessToken 访问令牌 + * + * 对应 Spring Security OAuth2 的 AuthorizationCodeTokenGranter 功能 + * + * @param clientId 客户端编号 + * @param code 授权码 + * @param redirectUri 重定向 URI + * @param state 状态 + * @return 访问令牌 + */ + OAuth2AccessTokenDO grantAuthorizationCodeForAccessToken(String clientId, String code, + String redirectUri, String state); } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImpl.java index 22dad3c88..377dde2fa 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImpl.java @@ -1,11 +1,18 @@ package cn.iocoder.yudao.module.system.service.oauth2; +import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2CodeDO; +import cn.iocoder.yudao.module.system.enums.ErrorCodeConstants; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.List; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.OAUTH2_GRANT_CODE_NOT_EXISTS; +import static java.util.Collections.singletonList; + /** * OAuth2 授予 Service 实现类 * @@ -24,10 +31,38 @@ public class OAuth2GrantServiceImpl implements OAuth2GrantService { } @Override - public String grantAuthorizationCode(Long userId, Integer userType, - String clientId, List scopes, - String redirectUri, String state) { + public String grantAuthorizationCodeForCode(Long userId, Integer userType, + String clientId, List scopes, + String redirectUri, String state) { return "test"; } + @Override + public OAuth2AccessTokenDO grantAuthorizationCodeForAccessToken(String clientId, String code, + String redirectUri, String state) { + // TODO 消费 code + OAuth2CodeDO codeDO = new OAuth2CodeDO().setClientId("default").setRedirectUri("https://www.iocoder.cn").setState("") + .setUserId(1L).setUserType(2).setScopes(singletonList("user_info")); + if (codeDO == null) { + throw exception(OAUTH2_GRANT_CODE_NOT_EXISTS); + } + // 校验 clientId 是否匹配 + if (!StrUtil.equals(clientId, codeDO.getClientId())) { + throw exception(ErrorCodeConstants.OAUTH2_GRANT_CLIENT_ID_MISMATCH); + } + // 校验 redirectUri 是否匹配 + if (!StrUtil.equals(redirectUri, codeDO.getRedirectUri())) { + throw exception(ErrorCodeConstants.OAUTH2_GRANT_REDIRECT_URI_MISMATCH); + } + // 校验 state 是否匹配 + state = StrUtil.nullToDefault(state, ""); // 数据库 state 为 null 时,会设置为 "" 空串 + if (!StrUtil.equals(state, codeDO.getState())) { + throw exception(ErrorCodeConstants.OAUTH2_GRANT_STATE_MISMATCH); + } + + // 创建访问令牌 + return oauth2TokenService.createAccessToken(codeDO.getUserId(), codeDO.getUserType(), + codeDO.getClientId(), codeDO.getScopes()); + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/util/oauth2/OAuth2Utils.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/util/oauth2/OAuth2Utils.java index 0cdc88c5f..e3246505b 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/util/oauth2/OAuth2Utils.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/util/oauth2/OAuth2Utils.java @@ -50,13 +50,12 @@ public class OAuth2Utils { Map vars = new LinkedHashMap(); Map keys = new HashMap(); vars.put("access_token", accessToken); - vars.put("token_type", SecurityFrameworkUtils.TOKEN_TYPE.toLowerCase()); + vars.put("token_type", SecurityFrameworkUtils.AUTHORIZATION_BEARER.toLowerCase()); if (state != null) { vars.put("state", state); } if (expireTime != null) { - long expires_in = (expireTime.getTime() - System.currentTimeMillis()) / 1000; - vars.put("expires_in", expires_in); + vars.put("expires_in", getExpiresIn(expireTime)); } if (CollUtil.isNotEmpty(scopes)) { vars.put("scope", CollUtil.join(scopes, " ")); @@ -83,4 +82,8 @@ public class OAuth2Utils { return HttpUtils.append(redirectUri, query, null, !responseType.contains("code")); } + public static long getExpiresIn(Date expireTime) { + return (expireTime.getTime() - System.currentTimeMillis()) / 1000; + } + } From 0900b9e111ec110df765d5c3f7789a0abb3b8785 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 15 May 2022 17:36:22 +0800 Subject: [PATCH 07/13] =?UTF-8?q?=E5=AE=8C=E6=88=90=20oauth2=20password=20?= =?UTF-8?q?=E5=AF=86=E7=A0=81=E6=A8=A1=E5=BC=8F=E7=9A=84=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=20=E5=AE=8C=E6=88=90=20oauth2=20revokeToken=20=E7=9A=84?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/oauth2/OAuth2Controller.http | 21 +++++++ .../admin/oauth2/OAuth2OpenController.java | 63 ++++++++++++++++--- .../vo/open/OAuth2OpenAccessTokenRespVO.java | 3 + .../convert/oauth2/OAuth2OpenConvert.java | 1 + .../config/SecurityConfiguration.java | 1 + .../system/service/auth/AdminAuthService.java | 10 +++ .../service/auth/AdminAuthServiceImpl.java | 46 +++++++------- .../service/oauth2/OAuth2GrantService.java | 59 ++++++++++++++++- .../oauth2/OAuth2GrantServiceImpl.java | 39 ++++++++++++ .../system/util/oauth2/OAuth2Utils.java | 11 +++- ...est.java => AdminAuthServiceImplTest.java} | 18 +++--- 11 files changed, 228 insertions(+), 44 deletions(-) rename yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/{AuthServiceImplTest.java => AdminAuthServiceImplTest.java} (94%) diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2Controller.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2Controller.http index ce7cf83d8..691ed9e39 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2Controller.http +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2Controller.http @@ -21,3 +21,24 @@ Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw== tenant-id: {{adminTenentId}} grant_type=authorization_code&redirect_uri=https://www.iocoder.cn + +### 请求 /system/oauth2/token + password 接口 => 成功 +POST {{baseUrl}}/system/oauth2/token +Content-Type: application/x-www-form-urlencoded +Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw== +tenant-id: {{adminTenentId}} + +grant_type=password&username=admin&password=admin123&scope=user_info + +### 请求 /system/oauth2/token + refresh_token 接口 => 成功 +POST {{baseUrl}}/system/oauth2/token +Content-Type: application/x-www-form-urlencoded +Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw== +tenant-id: {{adminTenentId}} + +grant_type=refresh_token&refresh_token=00895465d6994f72a9d926ceeed0f588 + +### 请求 /system/oauth2/token + DELETE 接口 => 成功 +DELETE {{baseUrl}}/system/oauth2/token?token=ca8a188f464441d6949c51493a2b7596 +Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw== +tenant-id: {{adminTenentId}} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.java index 5d7e0bbd2..0edd0f827 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.java @@ -47,8 +47,6 @@ public class OAuth2OpenController { // POST oauth/check_token CheckTokenEndpoint -// DELETE oauth/token ConsumerTokenServices#revokeToken - @Resource private OAuth2GrantService oauth2GrantService; @Resource @@ -56,20 +54,38 @@ public class OAuth2OpenController { @Resource private OAuth2ApproveService oauth2ApproveService; + /** + * 对应 Spring Security OAuth 的 TokenEndpoint 类的 postAccessToken 方法 + * + * 授权码 authorization_code 模式时:code + redirectUri + state 参数 + * 密码 password 模式时:username + password + scope 参数 + * 刷新 refresh_token 模式时:refreshToken 参数 + * 客户端 client_credentials 模式:scope 参数 + * 简化 implicit 模式时:不支持 + * + * 注意,默认需要传递 client_id + client_secret 参数 + */ @PostMapping("/token") @ApiOperation(value = "获得访问令牌", notes = "适合 code 授权码模式,或者 implicit 简化模式;在 authorize.vue 单点登录界面被【获取】调用") @ApiImplicitParams({ @ApiImplicitParam(name = "grant_type", required = true, value = "授权类型", example = "code", dataTypeClass = String.class), @ApiImplicitParam(name = "code", value = "授权范围", example = "userinfo.read", dataTypeClass = String.class), @ApiImplicitParam(name = "redirect_uri", value = "重定向 URI", example = "https://www.iocoder.cn", dataTypeClass = String.class), - @ApiImplicitParam(name = "state", example = "123321", dataTypeClass = String.class) + @ApiImplicitParam(name = "username", example = "tudou", dataTypeClass = String.class), + @ApiImplicitParam(name = "password", example = "cai", dataTypeClass = String.class), // 多个使用空格分隔 + @ApiImplicitParam(name = "scope", example = "user_info", dataTypeClass = String.class) }) @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 public CommonResult postAccessToken(HttpServletRequest request, @RequestParam("grant_type") String grantType, @RequestParam(value = "code", required = false) String code, // 授权码模式 @RequestParam(value = "redirect_uri", required = false) String redirectUri, // 授权码模式 - @RequestParam(value = "state", required = false) String state) { // 授权码模式 + @RequestParam(value = "state", required = false) String state, // 授权码模式 + @RequestParam(value = "username", required = false) String username, // 密码模式 + @RequestParam(value = "password", required = false) String password, // 密码模式 + @RequestParam(value = "scope", required = false) String scope, // 密码模式 + @RequestParam(value = "refresh_token", required = false) String refreshToken) { // 刷新模式 + List scopes = OAuth2Utils.buildScopes(scope); // 授权类型 OAuth2GrantTypeEnum grantTypeEnum = OAuth2GrantTypeEnum.getByGranType(grantType); if (grantTypeEnum == null) { @@ -80,23 +96,24 @@ public class OAuth2OpenController { } // 校验客户端 - String[] clientIdAndSecret = HttpUtils.obtainBasicAuthorization(request); - if (ArrayUtil.isEmpty(clientIdAndSecret) || clientIdAndSecret.length != 2) { - throw exception0(BAD_REQUEST.getCode(), "client_id 或 client_secret 未正确传递"); - } - OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientIdAndSecret[0], clientIdAndSecret[1], grantType, null, null); + String[] clientIdAndSecret = obtainBasicAuthorization(request); + OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientIdAndSecret[0], clientIdAndSecret[1], + grantType, scopes, redirectUri); // 根据授权模式,获取访问令牌 - OAuth2AccessTokenDO accessTokenDO = null; + OAuth2AccessTokenDO accessTokenDO; switch (grantTypeEnum) { case AUTHORIZATION_CODE: accessTokenDO = oauth2GrantService.grantAuthorizationCodeForAccessToken(client.getClientId(), code, redirectUri, state); break; case PASSWORD: + accessTokenDO = oauth2GrantService.grantPassword(username, password, client.getClientId(), scopes); break; case CLIENT_CREDENTIALS: + accessTokenDO = oauth2GrantService.grantClientCredentials(client.getClientId(), scopes); break; case REFRESH_TOKEN: + accessTokenDO = oauth2GrantService.grantRefreshToken(refreshToken, client.getClientId()); break; default: throw new IllegalArgumentException("未知授权类型:" + grantType); @@ -105,6 +122,24 @@ public class OAuth2OpenController { return success(OAuth2OpenConvert.INSTANCE.convert(accessTokenDO)); } + @DeleteMapping("/token") + @ApiOperation(value = "删除访问令牌") + @ApiImplicitParam(name = "token", required = true, value = "访问令牌", example = "biu", dataTypeClass = String.class) + @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 + public CommonResult revokeToken(HttpServletRequest request, + @RequestParam("token") String token) { + // 校验客户端 + String[] clientIdAndSecret = obtainBasicAuthorization(request); + if (ArrayUtil.isEmpty(clientIdAndSecret) || clientIdAndSecret.length != 2) { + throw exception0(BAD_REQUEST.getCode(), "client_id 或 client_secret 未正确传递"); + } + OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientIdAndSecret[0], clientIdAndSecret[1], + null, null, null); + + // 删除访问令牌 + return success(oauth2GrantService.revokeToken(client.getClientId(), token)); + } + // GET oauth/authorize AuthorizationEndpoint TODO @GetMapping("/authorize") @ApiOperation(value = "获得授权信息", notes = "适合 code 授权码模式,或者 implicit 简化模式;在 authorize.vue 单点登录界面被【获取】调用") @@ -229,4 +264,12 @@ public class OAuth2OpenController { return UserTypeEnum.ADMIN.getValue(); } + private String[] obtainBasicAuthorization(HttpServletRequest request) { + String[] clientIdAndSecret = HttpUtils.obtainBasicAuthorization(request); + if (ArrayUtil.isEmpty(clientIdAndSecret) || clientIdAndSecret.length != 2) { + throw exception0(BAD_REQUEST.getCode(), "client_id 或 client_secret 未正确传递"); + } + return clientIdAndSecret; + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenAccessTokenRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenAccessTokenRespVO.java index b9c697a85..cf9848ad3 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenAccessTokenRespVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenAccessTokenRespVO.java @@ -29,4 +29,7 @@ public class OAuth2OpenAccessTokenRespVO { @JsonProperty("expires_in") private Long expiresIn; + @ApiModelProperty(value = "授权范围", example = "user_info", notes = "如果多个授权范围,使用空格分隔") + private String scope; + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/oauth2/OAuth2OpenConvert.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/oauth2/OAuth2OpenConvert.java index a873c7f28..61fbee152 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/oauth2/OAuth2OpenConvert.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/oauth2/OAuth2OpenConvert.java @@ -16,6 +16,7 @@ public interface OAuth2OpenConvert { OAuth2OpenAccessTokenRespVO respVO = convert0(bean); respVO.setTokenType(SecurityFrameworkUtils.AUTHORIZATION_BEARER.toLowerCase()); respVO.setExpiresIn(OAuth2Utils.getExpiresIn(bean.getExpiresTime())); + respVO.setScope(OAuth2Utils.buildScopeStr(bean.getScopes())); return respVO; } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/security/config/SecurityConfiguration.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/security/config/SecurityConfiguration.java index 94be75462..b43af78e0 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/security/config/SecurityConfiguration.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/security/config/SecurityConfiguration.java @@ -37,6 +37,7 @@ public class SecurityConfiguration { registry.antMatchers(buildAdminApi("/system/sms/callback/**")).permitAll(); // OAuth2 API registry.antMatchers(buildAdminApi("/system/oauth2/token")).permitAll(); + registry.antMatchers(buildAdminApi("/system/oauth2/check_token")).permitAll(); } }; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java index ec8b31bd5..3a53c1aa3 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.system.service.auth; import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*; +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; import javax.validation.Valid; @@ -13,6 +14,15 @@ import javax.validation.Valid; */ public interface AdminAuthService { + /** + * 验证账号 + 密码。如果通过,则返回用户 + * + * @param username 账号 + * @param password 密码 + * @return 用户 + */ + AdminUserDO authenticate(String username, String password); + /** * 账号登录 * diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java index 60584f0ca..898b08abf 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java @@ -62,13 +62,34 @@ public class AdminAuthServiceImpl implements AdminAuthService { @Resource private SmsCodeApi smsCodeApi; + @Override + public AdminUserDO authenticate(String username, String password) { + final LoginLogTypeEnum logTypeEnum = LoginLogTypeEnum.LOGIN_USERNAME; + // 校验账号是否存在 + AdminUserDO user = userService.getUserByUsername(username); + if (user == null) { + createLoginLog(null, username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS); + throw exception(AUTH_LOGIN_BAD_CREDENTIALS); + } + if (!userService.isPasswordMatch(password, user.getPassword())) { + createLoginLog(user.getId(), username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS); + throw exception(AUTH_LOGIN_BAD_CREDENTIALS); + } + // 校验是否禁用 + if (ObjectUtil.notEqual(user.getStatus(), CommonStatusEnum.ENABLE.getStatus())) { + createLoginLog(user.getId(), username, logTypeEnum, LoginResultEnum.USER_DISABLED); + throw exception(AUTH_LOGIN_USER_DISABLED); + } + return user; + } + @Override public AuthLoginRespVO login(AuthLoginReqVO reqVO) { // 判断验证码是否正确 verifyCaptcha(reqVO); // 使用账号密码,进行登录 - AdminUserDO user = login0(reqVO.getUsername(), reqVO.getPassword()); + AdminUserDO user = authenticate(reqVO.getUsername(), reqVO.getPassword()); // 创建 Token 令牌,记录登录日志 return createTokenAfterLoginSuccess(user.getId(), reqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME); @@ -125,27 +146,6 @@ public class AdminAuthServiceImpl implements AdminAuthService { captchaService.deleteCaptchaCode(reqVO.getUuid()); } - @VisibleForTesting - AdminUserDO login0(String username, String password) { - final LoginLogTypeEnum logTypeEnum = LoginLogTypeEnum.LOGIN_USERNAME; - // 校验账号是否存在 - AdminUserDO user = userService.getUserByUsername(username); - if (user == null) { - createLoginLog(null, username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS); - throw exception(AUTH_LOGIN_BAD_CREDENTIALS); - } - if (!userService.isPasswordMatch(password, user.getPassword())) { - createLoginLog(user.getId(), username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS); - throw exception(AUTH_LOGIN_BAD_CREDENTIALS); - } - // 校验是否禁用 - if (ObjectUtil.notEqual(user.getStatus(), CommonStatusEnum.ENABLE.getStatus())) { - createLoginLog(user.getId(), username, logTypeEnum, LoginResultEnum.USER_DISABLED); - throw exception(AUTH_LOGIN_USER_DISABLED); - } - return user; - } - private void createLoginLog(Long userId, String username, LoginLogTypeEnum logTypeEnum, LoginResultEnum loginResult) { // 插入登录日志 @@ -187,7 +187,7 @@ public class AdminAuthServiceImpl implements AdminAuthService { @Override public AuthLoginRespVO socialBindLogin(AuthSocialBindLoginReqVO reqVO) { // 使用账号密码,进行登录。 - AdminUserDO user = login0(reqVO.getUsername(), reqVO.getPassword()); + AdminUserDO user = authenticate(reqVO.getUsername(), reqVO.getPassword()); // 绑定社交用户 socialUserService.bindSocialUser(AuthConvert.INSTANCE.convert(user.getId(), getUserType().getValue(), reqVO)); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantService.java index 92d1da337..f87a18cc1 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantService.java @@ -18,7 +18,17 @@ import java.util.List; */ public interface OAuth2GrantService { - // ImplicitTokenGranter + /** + * 简化模式 + * + * 对应 Spring Security OAuth2 的 ImplicitTokenGranter 功能 + * + * @param userId 用户编号 + * @param userType 用户类型 + * @param clientId 客户端编号 + * @param scopes 授权范围 + * @return 访问令牌 + */ OAuth2AccessTokenDO grantImplicit(Long userId, Integer userType, String clientId, List scopes); @@ -53,4 +63,51 @@ public interface OAuth2GrantService { OAuth2AccessTokenDO grantAuthorizationCodeForAccessToken(String clientId, String code, String redirectUri, String state); + /** + * 密码模式 + * + * 对应 Spring Security OAuth2 的 ResourceOwnerPasswordTokenGranter 功能 + * + * @param username 账号 + * @param password 密码 + * @param clientId 客户端编号 + * @param scopes 授权范围 + * @return 访问令牌 + */ + OAuth2AccessTokenDO grantPassword(String username, String password, + String clientId, List scopes); + + /** + * 刷新模式 + * + * 对应 Spring Security OAuth2 的 ResourceOwnerPasswordTokenGranter 功能 + * + * @param refreshToken 刷新令牌 + * @param clientId 客户端编号 + * @return 访问令牌 + */ + OAuth2AccessTokenDO grantRefreshToken(String refreshToken, String clientId); + + /** + * 客户端模式 + * + * 对应 Spring Security OAuth2 的 ClientCredentialsTokenGranter 功能 + * + * @param clientId 客户端编号 + * @param scopes 授权范围 + * @return 访问令牌 + */ + OAuth2AccessTokenDO grantClientCredentials(String clientId, List scopes); + + /** + * 移除访问令牌 + * + * 对应 Spring Security OAuth2 的 ConsumerTokenServices 的 revokeToken 方法 + * + * @param accessToken 访问令牌 + * @param clientId 客户端编号 + * @return 是否移除到 + */ + boolean revokeToken(String clientId, String accessToken); + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImpl.java index 377dde2fa..3d50b3956 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImpl.java @@ -1,9 +1,14 @@ package cn.iocoder.yudao.module.system.service.oauth2; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2CodeDO; +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; import cn.iocoder.yudao.module.system.enums.ErrorCodeConstants; +import cn.iocoder.yudao.module.system.service.auth.AdminAuthService; import org.springframework.stereotype.Service; import javax.annotation.Resource; @@ -23,6 +28,8 @@ public class OAuth2GrantServiceImpl implements OAuth2GrantService { @Resource private OAuth2TokenService oauth2TokenService; + @Resource + private AdminAuthService adminAuthService; @Override public OAuth2AccessTokenDO grantImplicit(Long userId, Integer userType, @@ -65,4 +72,36 @@ public class OAuth2GrantServiceImpl implements OAuth2GrantService { codeDO.getClientId(), codeDO.getScopes()); } + @Override + public OAuth2AccessTokenDO grantPassword(String username, String password, String clientId, List scopes) { + // 使用账号 + 密码进行登录 + AdminUserDO user = adminAuthService.authenticate(username, password); + Assert.notNull(user, "用户不能为空!"); // 防御性编程 + + // 创建访问令牌 + return oauth2TokenService.createAccessToken(user.getId(), UserTypeEnum.ADMIN.getValue(), clientId, scopes); + } + + @Override + public OAuth2AccessTokenDO grantRefreshToken(String refreshToken, String clientId) { + return oauth2TokenService.refreshAccessToken(refreshToken, clientId); + } + + @Override + public OAuth2AccessTokenDO grantClientCredentials(String clientId, List scopes) { + // TODO 芋艿:项目中使用 OAuth2 解决的是三方应用的授权,内部的 SSO 等问题,所以暂时不考虑 client_credentials 这个场景 + throw new UnsupportedOperationException("暂时不支持 client_credentials 授权模式"); + } + + @Override + public boolean revokeToken(String clientId, String accessToken) { + // 先查询,保证 clientId 时匹配的 + OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.getAccessToken(accessToken); + if (accessTokenDO == null || ObjectUtil.notEqual(clientId, accessTokenDO.getClientId())) { + return false; + } + // 再删除 + return oauth2TokenService.removeAccessToken(accessToken) != null; + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/util/oauth2/OAuth2Utils.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/util/oauth2/OAuth2Utils.java index e3246505b..43617ddbe 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/util/oauth2/OAuth2Utils.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/util/oauth2/OAuth2Utils.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.system.util.oauth2; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.util.http.HttpUtils; import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; @@ -58,7 +59,7 @@ public class OAuth2Utils { vars.put("expires_in", getExpiresIn(expireTime)); } if (CollUtil.isNotEmpty(scopes)) { - vars.put("scope", CollUtil.join(scopes, " ")); + vars.put("scope", buildScopeStr(scopes)); } for (String key : additionalInformation.keySet()) { Object value = additionalInformation.get(key); @@ -86,4 +87,12 @@ public class OAuth2Utils { return (expireTime.getTime() - System.currentTimeMillis()) / 1000; } + public static String buildScopeStr(Collection scopes) { + return CollUtil.join(scopes, " "); + } + + public static List buildScopes(String scope) { + return StrUtil.split(scope, ' '); + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AuthServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImplTest.java similarity index 94% rename from yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AuthServiceImplTest.java rename to yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImplTest.java index 49f378730..44141a3b2 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AuthServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImplTest.java @@ -34,7 +34,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.*; @Import(AdminAuthServiceImpl.class) -public class AuthServiceImplTest extends BaseDbUnitTest { +public class AdminAuthServiceImplTest extends BaseDbUnitTest { @Resource private AdminAuthServiceImpl authService; @@ -63,7 +63,7 @@ public class AuthServiceImplTest extends BaseDbUnitTest { } @Test - public void testLogin0_success() { + public void testAuthenticate_success() { // 准备参数 String username = randomString(); String password = randomString(); @@ -75,19 +75,19 @@ public class AuthServiceImplTest extends BaseDbUnitTest { when(userService.isPasswordMatch(eq(password), eq(user.getPassword()))).thenReturn(true); // 调用 - AdminUserDO loginUser = authService.login0(username, password); + AdminUserDO loginUser = authService.authenticate(username, password); // 校验 assertPojoEquals(user, loginUser); } @Test - public void testLogin0_userNotFound() { + public void testAuthenticate_userNotFound() { // 准备参数 String username = randomString(); String password = randomString(); // 调用, 并断言异常 - AssertUtils.assertServiceException(() -> authService.login0(username, password), + AssertUtils.assertServiceException(() -> authService.authenticate(username, password), AUTH_LOGIN_BAD_CREDENTIALS); verify(loginLogService).createLoginLog( argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType()) @@ -97,7 +97,7 @@ public class AuthServiceImplTest extends BaseDbUnitTest { } @Test - public void testLogin0_badCredentials() { + public void testAuthenticate_badCredentials() { // 准备参数 String username = randomString(); String password = randomString(); @@ -107,7 +107,7 @@ public class AuthServiceImplTest extends BaseDbUnitTest { when(userService.getUserByUsername(eq(username))).thenReturn(user); // 调用, 并断言异常 - AssertUtils.assertServiceException(() -> authService.login0(username, password), + AssertUtils.assertServiceException(() -> authService.authenticate(username, password), AUTH_LOGIN_BAD_CREDENTIALS); verify(loginLogService).createLoginLog( argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType()) @@ -117,7 +117,7 @@ public class AuthServiceImplTest extends BaseDbUnitTest { } @Test - public void testLogin0_userDisabled() { + public void testAuthenticate_userDisabled() { // 准备参数 String username = randomString(); String password = randomString(); @@ -129,7 +129,7 @@ public class AuthServiceImplTest extends BaseDbUnitTest { when(userService.isPasswordMatch(eq(password), eq(user.getPassword()))).thenReturn(true); // 调用, 并断言异常 - AssertUtils.assertServiceException(() -> authService.login0(username, password), + AssertUtils.assertServiceException(() -> authService.authenticate(username, password), AUTH_LOGIN_USER_DISABLED); verify(loginLogService).createLoginLog( argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType()) From 65ee56c81194209d827a592a9224c3c2b7ec3a2b Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 15 May 2022 18:32:34 +0800 Subject: [PATCH 08/13] =?UTF-8?q?=E5=AE=8C=E6=88=90=20oauth2=20check-token?= =?UTF-8?q?=20=E6=A0=A1=E9=AA=8C=20token=20=E7=9A=84=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...troller.http => OAuth2OpenController.http} | 5 +++ .../admin/oauth2/OAuth2OpenController.java | 29 +++++++++++--- .../vo/open/OAuth2OpenAccessTokenRespVO.java | 2 +- .../vo/open/OAuth2OpenCheckTokenRespVO.java | 40 +++++++++++++++++++ .../convert/oauth2/OAuth2OpenConvert.java | 11 ++++- .../config/SecurityConfiguration.java | 2 +- 6 files changed, 81 insertions(+), 8 deletions(-) rename yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/{OAuth2Controller.http => OAuth2OpenController.http} (89%) create mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenCheckTokenRespVO.java diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2Controller.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.http similarity index 89% rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2Controller.http rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.http index 691ed9e39..f1a494605 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2Controller.http +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.http @@ -42,3 +42,8 @@ grant_type=refresh_token&refresh_token=00895465d6994f72a9d926ceeed0f588 DELETE {{baseUrl}}/system/oauth2/token?token=ca8a188f464441d6949c51493a2b7596 Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw== tenant-id: {{adminTenentId}} + +### 请求 /system/oauth2/check-token 接口 => 成功 +POST {{baseUrl}}/system/oauth2/check-token?token=620d307c5b4148df8a98dd6c6c547106 +Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw== +tenant-id: {{adminTenentId}} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.java index 0edd0f827..6c510b47d 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.java @@ -10,6 +10,7 @@ import cn.iocoder.yudao.framework.common.util.http.HttpUtils; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenAccessTokenRespVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenCheckTokenRespVO; import cn.iocoder.yudao.module.system.convert.oauth2.OAuth2OpenConvert; import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO; @@ -17,6 +18,7 @@ import cn.iocoder.yudao.module.system.enums.auth.OAuth2GrantTypeEnum; import cn.iocoder.yudao.module.system.service.oauth2.OAuth2ApproveService; import cn.iocoder.yudao.module.system.service.oauth2.OAuth2ClientService; import cn.iocoder.yudao.module.system.service.oauth2.OAuth2GrantService; +import cn.iocoder.yudao.module.system.service.oauth2.OAuth2TokenService; import cn.iocoder.yudao.module.system.util.oauth2.OAuth2Utils; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; @@ -45,14 +47,14 @@ import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUti @Slf4j public class OAuth2OpenController { -// POST oauth/check_token CheckTokenEndpoint - @Resource private OAuth2GrantService oauth2GrantService; @Resource private OAuth2ClientService oauth2ClientService; @Resource private OAuth2ApproveService oauth2ApproveService; + @Resource + private OAuth2TokenService oauth2TokenService; /** * 对应 Spring Security OAuth 的 TokenEndpoint 类的 postAccessToken 方法 @@ -130,9 +132,6 @@ public class OAuth2OpenController { @RequestParam("token") String token) { // 校验客户端 String[] clientIdAndSecret = obtainBasicAuthorization(request); - if (ArrayUtil.isEmpty(clientIdAndSecret) || clientIdAndSecret.length != 2) { - throw exception0(BAD_REQUEST.getCode(), "client_id 或 client_secret 未正确传递"); - } OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientIdAndSecret[0], clientIdAndSecret[1], null, null, null); @@ -140,6 +139,26 @@ public class OAuth2OpenController { return success(oauth2GrantService.revokeToken(client.getClientId(), token)); } + /** + * 对应 Spring Security OAuth 的 CheckTokenEndpoint 类的 checkToken 方法 + */ + @PostMapping("/check-token") + @ApiOperation(value = "校验访问令牌") + @ApiImplicitParam(name = "token", required = true, value = "访问令牌", example = "biu", dataTypeClass = String.class) + @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 + public CommonResult checkToken(HttpServletRequest request, + @RequestParam("token") String token) { + // 校验客户端 + String[] clientIdAndSecret = obtainBasicAuthorization(request); + OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientIdAndSecret[0], clientIdAndSecret[1], + null, null, null); + + // 校验令牌 + OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.checkAccessToken(token); + Assert.notNull(accessTokenDO, "访问令牌不能为空"); // 防御性检查 + return success(OAuth2OpenConvert.INSTANCE.convert2(accessTokenDO)); + } + // GET oauth/authorize AuthorizationEndpoint TODO @GetMapping("/authorize") @ApiOperation(value = "获得授权信息", notes = "适合 code 授权码模式,或者 implicit 简化模式;在 authorize.vue 单点登录界面被【获取】调用") diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenAccessTokenRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenAccessTokenRespVO.java index cf9848ad3..4fdb4e539 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenAccessTokenRespVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenAccessTokenRespVO.java @@ -7,7 +7,7 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -@ApiModel("管理后台 - 访问令牌 Response VO") +@ApiModel("管理后台 - 【开放接口】访问令牌 Response VO") @Data @NoArgsConstructor @AllArgsConstructor diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenCheckTokenRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenCheckTokenRespVO.java new file mode 100644 index 000000000..b905e302b --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenCheckTokenRespVO.java @@ -0,0 +1,40 @@ +package cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Set; + +@ApiModel("管理后台 - 【开放接口】校验令牌 Response VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class OAuth2OpenCheckTokenRespVO { + + @ApiModelProperty(value = "用户编号", required = true, example = "666") + @JsonProperty("user_id") + private Long userId; + @ApiModelProperty(value = "用户类型", required = true, example = "2", notes = "参见 UserTypeEnum 枚举") + @JsonProperty("user_type") + private Integer userType; + @ApiModelProperty(value = "租户编号", required = true, example = "1024") + @JsonProperty("tenant_id") + private Long tenantId; + + @ApiModelProperty(value = "客户端编号", required = true, example = "car") + private String clientId; + @ApiModelProperty(value = "授权范围", required = true, example = "user_info") + private Set scopes; + + @ApiModelProperty(value = "访问令牌", required = true, example = "tudou") + @JsonProperty("access_token") + private String accessToken; + + @ApiModelProperty(value = "过期时间", required = true, example = "1593092157", notes = "时间戳 / 1000,即单位:秒") + @JsonProperty("exp") + private Long exp; +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/oauth2/OAuth2OpenConvert.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/oauth2/OAuth2OpenConvert.java index 61fbee152..cf0017d9e 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/oauth2/OAuth2OpenConvert.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/oauth2/OAuth2OpenConvert.java @@ -1,7 +1,9 @@ package cn.iocoder.yudao.module.system.convert.oauth2; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenAccessTokenRespVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenCheckTokenRespVO; import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; import cn.iocoder.yudao.module.system.util.oauth2.OAuth2Utils; import org.mapstruct.Mapper; @@ -19,7 +21,14 @@ public interface OAuth2OpenConvert { respVO.setScope(OAuth2Utils.buildScopeStr(bean.getScopes())); return respVO; } - OAuth2OpenAccessTokenRespVO convert0(OAuth2AccessTokenDO bean); + default OAuth2OpenCheckTokenRespVO convert2(OAuth2AccessTokenDO bean) { + OAuth2OpenCheckTokenRespVO respVO = convert3(bean); + respVO.setExp(bean.getExpiresTime().getTime() / 1000L); + respVO.setUserType(UserTypeEnum.ADMIN.getValue()); + return respVO; + } + OAuth2OpenCheckTokenRespVO convert3(OAuth2AccessTokenDO bean); + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/security/config/SecurityConfiguration.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/security/config/SecurityConfiguration.java index b43af78e0..c81809b2e 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/security/config/SecurityConfiguration.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/security/config/SecurityConfiguration.java @@ -37,7 +37,7 @@ public class SecurityConfiguration { registry.antMatchers(buildAdminApi("/system/sms/callback/**")).permitAll(); // OAuth2 API registry.antMatchers(buildAdminApi("/system/oauth2/token")).permitAll(); - registry.antMatchers(buildAdminApi("/system/oauth2/check_token")).permitAll(); + registry.antMatchers(buildAdminApi("/system/oauth2/check-token")).permitAll(); } }; From feff5aba07a1d6110e0d63b98a75cb560336da4a Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 15 May 2022 21:30:12 +0800 Subject: [PATCH 09/13] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20oauth2=20=E7=9A=84?= =?UTF-8?q?=20scope=20=E7=9A=84=E6=A0=A1=E9=AA=8C=E6=96=B9=E6=B3=95?= =?UTF-8?q?=EF=BC=8C=E4=B8=8E=E4=BD=BF=E7=94=A8=E7=A4=BA=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../framework/security/core/LoginUser.java | 5 ++ .../filter/TokenAuthenticationFilter.java | 2 +- .../service/SecurityFrameworkService.java | 15 ++++ .../service/SecurityFrameworkServiceImpl.java | 19 +++++ .../dto/OAuth2AccessTokenCheckRespDTO.java | 5 ++ .../admin/oauth2/OAuth2OpenController.http | 17 ++++- .../admin/oauth2/OAuth2OpenController.java | 64 ++++++++++++++++- .../open/user/OAuth2OpenUserInfoRespVO.java | 71 +++++++++++++++++++ .../open/user/OAuth2OpenUserUpdateReqVO.java | 35 +++++++++ .../admin/user/UserProfileController.java | 1 - .../vo/profile/UserProfileUpdateReqVO.java | 2 +- .../convert/oauth2/OAuth2OpenConvert.java | 15 ++++ 12 files changed, 246 insertions(+), 5 deletions(-) create mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/user/OAuth2OpenUserInfoRespVO.java create mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/user/OAuth2OpenUserUpdateReqVO.java diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/LoginUser.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/LoginUser.java index 20d567963..50523ca0a 100644 --- a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/LoginUser.java +++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/LoginUser.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Data; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -30,6 +31,10 @@ public class LoginUser { * 租户编号 */ private Long tenantId; + /** + * 授权范围 + */ + private List scopes; // ========== 上下文 ========== /** diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/filter/TokenAuthenticationFilter.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/filter/TokenAuthenticationFilter.java index 204b03fa0..e59324004 100644 --- a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/filter/TokenAuthenticationFilter.java +++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/filter/TokenAuthenticationFilter.java @@ -79,7 +79,7 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter { } // 构建登录用户 return new LoginUser().setId(accessToken.getUserId()).setUserType(accessToken.getUserType()) - .setTenantId(accessToken.getTenantId()); + .setTenantId(accessToken.getTenantId()).setScopes(accessToken.getScopes()); } catch (ServiceException serviceException) { // 校验 Token 不通过时,考虑到一些接口是无需登录的,所以直接返回 null 即可 return null; diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/service/SecurityFrameworkService.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/service/SecurityFrameworkService.java index 49cc9f972..bf2f7f31f 100644 --- a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/service/SecurityFrameworkService.java +++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/service/SecurityFrameworkService.java @@ -41,4 +41,19 @@ public interface SecurityFrameworkService { */ boolean hasAnyRoles(String... roles); + /** + * 判断是否有授权 + * + * @param scope 授权 + * @return 是否 + */ + boolean hasScope(String scope); + + /** + * 判断是否有授权范围,任一一个即可 + * + * @param scope 授权范围数组 + * @return 是否 + */ + boolean hasAnyScopes(String... scope); } diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/service/SecurityFrameworkServiceImpl.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/service/SecurityFrameworkServiceImpl.java index 2227ad3c2..78caadea2 100644 --- a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/service/SecurityFrameworkServiceImpl.java +++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/service/SecurityFrameworkServiceImpl.java @@ -1,8 +1,13 @@ package cn.iocoder.yudao.framework.security.core.service; +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.security.core.LoginUser; +import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; import cn.iocoder.yudao.module.system.api.permission.PermissionApi; import lombok.AllArgsConstructor; +import java.util.Arrays; + import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; /** @@ -35,4 +40,18 @@ public class SecurityFrameworkServiceImpl implements SecurityFrameworkService { return permissionApi.hasAnyRoles(getLoginUserId(), roles); } + @Override + public boolean hasScope(String scope) { + return hasAnyScopes(scope); + } + + @Override + public boolean hasAnyScopes(String... scope) { + LoginUser user = SecurityFrameworkUtils.getLoginUser(); + if (user == null) { + return false; + } + return CollUtil.containsAny(user.getScopes(), Arrays.asList(scope)); + } + } diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/dto/OAuth2AccessTokenCheckRespDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/dto/OAuth2AccessTokenCheckRespDTO.java index 5acdc1ea8..5b708ff66 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/dto/OAuth2AccessTokenCheckRespDTO.java +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/dto/OAuth2AccessTokenCheckRespDTO.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.system.api.auth.dto; import lombok.Data; import java.io.Serializable; +import java.util.List; /** * OAuth2.0 访问令牌的校验 Response DTO @@ -24,5 +25,9 @@ public class OAuth2AccessTokenCheckRespDTO implements Serializable { * 租户编号 */ private Long tenantId; + /** + * 授权范围的数组 + */ + private List scopes; } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.http index f1a494605..16a7025c0 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.http +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.http @@ -28,7 +28,7 @@ Content-Type: application/x-www-form-urlencoded Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw== tenant-id: {{adminTenentId}} -grant_type=password&username=admin&password=admin123&scope=user_info +grant_type=password&username=admin&password=admin123&scope=user.read ### 请求 /system/oauth2/token + refresh_token 接口 => 成功 POST {{baseUrl}}/system/oauth2/token @@ -47,3 +47,18 @@ tenant-id: {{adminTenentId}} POST {{baseUrl}}/system/oauth2/check-token?token=620d307c5b4148df8a98dd6c6c547106 Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw== tenant-id: {{adminTenentId}} + +### 请求 /system/oauth2/user/get 接口 => 成功 +GET {{baseUrl}}/system/oauth2/user/get +Authorization: Bearer 9502bd7a768a4ade920b90f41e2efd5c +tenant-id: {{adminTenentId}} + +### 请求 /system/oauth2/user/update 接口 => 成功 +PUT {{baseUrl}}/system/oauth2/user/update +Content-Type: application/json +Authorization: Bearer 9502bd7a768a4ade920b90f41e2efd5c +tenant-id: {{adminTenentId}} + +{ + "nickname": "芋道源码" +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.java index 6c510b47d..f6b588c90 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.system.controller.admin.oauth2; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ObjectUtil; @@ -11,25 +12,35 @@ import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenAccessTokenRespVO; import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenCheckTokenRespVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.user.OAuth2OpenUserInfoRespVO; +import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdateReqVO; import cn.iocoder.yudao.module.system.convert.oauth2.OAuth2OpenConvert; import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO; +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; import cn.iocoder.yudao.module.system.enums.auth.OAuth2GrantTypeEnum; +import cn.iocoder.yudao.module.system.service.dept.DeptService; +import cn.iocoder.yudao.module.system.service.dept.PostService; import cn.iocoder.yudao.module.system.service.oauth2.OAuth2ApproveService; import cn.iocoder.yudao.module.system.service.oauth2.OAuth2ClientService; import cn.iocoder.yudao.module.system.service.oauth2.OAuth2GrantService; import cn.iocoder.yudao.module.system.service.oauth2.OAuth2TokenService; +import cn.iocoder.yudao.module.system.service.user.AdminUserService; import cn.iocoder.yudao.module.system.util.oauth2.OAuth2Utils; 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.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; +import javax.validation.Valid; import java.util.Collections; import java.util.List; import java.util.Map; @@ -40,7 +51,19 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; -@Api(tags = "管理后台 - OAuth2.0 授权") // 提供给外部应用调用为主 +/** + * 提供给外部应用调用为主 + * + * 一般来说,管理后台的 /system-api/* 是不直接提供给外部应用使用,主要是外部应用能够访问的数据与接口是有限的,而管理后台的 RBAC 无法很好的控制。 + * 参考大量的开放平台,都是独立的一套 OpenAPI,对应到【本系统】就是在 Controller 下新建 open 包,实现 /open-api/* 接口,然后通过 scope 进行控制。 + * 另外,一个公司如果有多个管理后台,它们 client_id 产生的 access token 相互之间是无法互通的,即无法访问它们系统的 API 接口,直到两个 client_id 产生信任授权。 + * + * 考虑到【本系统】暂时不想做的过于复杂,默认只有获取到 access token 之后,可以访问【本系统】管理后台的 /system-api/* 所有接口,除非手动添加 scope 控制。 + * scope 的使用示例,可见当前类的 getUserInfo 和 updateUserInfo 方法,上面有 @PreAuthorize("@ss.hasScope('user.read')") 和 @PreAuthorize("@ss.hasScope('user.write')") 注解 + * + * @author 芋道源码 + */ +@Api(tags = "管理后台 - OAuth2.0 授权") @RestController @RequestMapping("/system/oauth2") @Validated @@ -291,4 +314,43 @@ public class OAuth2OpenController { return clientIdAndSecret; } + // ============ 用户操作的示例,展示 scope 的使用 ============ + + @Resource + private AdminUserService userService; + @Resource + private DeptService deptService; + @Resource + private PostService postService; + + @GetMapping("/user/get") + @ApiOperation("获得用户基本信息") + @PreAuthorize("@ss.hasScope('user.read')") + public CommonResult getUserInfo() { + // 获得用户基本信息 + AdminUserDO user = userService.getUser(getLoginUserId()); + OAuth2OpenUserInfoRespVO resp = OAuth2OpenConvert.INSTANCE.convert(user); + // 获得部门信息 + if (user.getDeptId() != null) { + DeptDO dept = deptService.getDept(user.getDeptId()); + resp.setDept(OAuth2OpenConvert.INSTANCE.convert(dept)); + } + // 获得岗位信息 + if (CollUtil.isNotEmpty(user.getPostIds())) { + List posts = postService.getPosts(user.getPostIds()); + resp.setPosts(OAuth2OpenConvert.INSTANCE.convertList(posts)); + } + return success(resp); + } + + @PutMapping("/user/update") + @ApiOperation("更新用户基本信息") + @PreAuthorize("@ss.hasScope('user.write')") + public CommonResult updateUserInfo(@Valid @RequestBody UserProfileUpdateReqVO reqVO) { + // 这里将 UserProfileUpdateReqVO =》UserProfileUpdateReqVO 对象,实现接口的复用。 + // 主要是,AdminUserService 没有自己的 BO 对象,所以复用只能这么做 + userService.updateUserProfile(getLoginUserId(), OAuth2OpenConvert.INSTANCE.convert(reqVO)); + return success(true); + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/user/OAuth2OpenUserInfoRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/user/OAuth2OpenUserInfoRespVO.java new file mode 100644 index 000000000..00df1f11c --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/user/OAuth2OpenUserInfoRespVO.java @@ -0,0 +1,71 @@ +package cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.user; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@ApiModel("管理后台 - 【开放接口】获得用户基本信息 Response VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class OAuth2OpenUserInfoRespVO { + + @ApiModelProperty(value = "用户编号", required = true, example = "1") + private Long id; + + @ApiModelProperty(value = "用户昵称", required = true, example = "芋艿") + private String username; + + @ApiModelProperty(value = "用户昵称", required = true, example = "芋道") + private String nickname; + + @ApiModelProperty(value = "用户邮箱", example = "yudao@iocoder.cn") + private String email; + @ApiModelProperty(value = "手机号码", example = "15601691300") + private String mobile; + + @ApiModelProperty(value = "用户性别", example = "1", notes = "参见 SexEnum 枚举类") + private Integer sex; + + @ApiModelProperty(value = "用户头像", example = "https://www.iocoder.cn/xxx.png") + private String avatar; + + /** + * 所在部门 + */ + private Dept dept; + + /** + * 所属岗位数组 + */ + private List posts; + + @ApiModel("部门") + @Data + public static class Dept { + + @ApiModelProperty(value = "部门编号", required = true, example = "1") + private Long id; + + @ApiModelProperty(value = "部门名称", required = true, example = "研发部") + private String name; + + } + + @ApiModel("岗位") + @Data + public static class Post { + + @ApiModelProperty(value = "岗位编号", required = true, example = "1") + private Long id; + + @ApiModelProperty(value = "岗位名称", required = true, example = "开发") + private String name; + + } + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/user/OAuth2OpenUserUpdateReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/user/OAuth2OpenUserUpdateReqVO.java new file mode 100644 index 000000000..4c7b784d9 --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/user/OAuth2OpenUserUpdateReqVO.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.user; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.Email; +import javax.validation.constraints.Size; + +@ApiModel("管理后台 - 【开放接口】更新用户基本信息 Request VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class OAuth2OpenUserUpdateReqVO { + + @ApiModelProperty(value = "用户昵称", required = true, example = "芋艿") + @Size(max = 30, message = "用户昵称长度不能超过 30 个字符") + private String nickname; + + @ApiModelProperty(value = "用户邮箱", example = "yudao@iocoder.cn") + @Email(message = "邮箱格式不正确") + @Size(max = 50, message = "邮箱长度不能超过 50 个字符") + private String email; + + @ApiModelProperty(value = "手机号码", example = "15601691300") + @Length(min = 11, max = 11, message = "手机号长度必须 11 位") + private String mobile; + + @ApiModelProperty(value = "用户性别", example = "1", notes = "参见 SexEnum 枚举类") + private Integer sex; + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserProfileController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserProfileController.java index 9c6c24092..3cc3a44dd 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserProfileController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserProfileController.java @@ -46,7 +46,6 @@ public class UserProfileController { private AdminUserService userService; @Resource private DeptService deptService; - @Resource private PostService postService; @Resource diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/profile/UserProfileUpdateReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/profile/UserProfileUpdateReqVO.java index 3716f4727..5ab28d1e7 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/profile/UserProfileUpdateReqVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/profile/UserProfileUpdateReqVO.java @@ -13,7 +13,7 @@ import javax.validation.constraints.Size; public class UserProfileUpdateReqVO { @ApiModelProperty(value = "用户昵称", required = true, example = "芋艿") - @Size(max = 30, message = "用户昵称长度不能超过30个字符") + @Size(max = 30, message = "用户昵称长度不能超过 30 个字符") private String nickname; @ApiModelProperty(value = "用户邮箱", example = "yudao@iocoder.cn") diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/oauth2/OAuth2OpenConvert.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/oauth2/OAuth2OpenConvert.java index cf0017d9e..af9158019 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/oauth2/OAuth2OpenConvert.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/oauth2/OAuth2OpenConvert.java @@ -4,11 +4,18 @@ import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenAccessTokenRespVO; import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenCheckTokenRespVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.user.OAuth2OpenUserInfoRespVO; +import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdateReqVO; import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO; +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; import cn.iocoder.yudao.module.system.util.oauth2.OAuth2Utils; import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; +import java.util.List; + @Mapper public interface OAuth2OpenConvert { @@ -31,4 +38,12 @@ public interface OAuth2OpenConvert { } OAuth2OpenCheckTokenRespVO convert3(OAuth2AccessTokenDO bean); + // ============ 用户操作的示例 ============ + + OAuth2OpenUserInfoRespVO convert(AdminUserDO bean); + OAuth2OpenUserInfoRespVO.Dept convert(DeptDO dept); + List convertList(List list); + + UserProfileUpdateReqVO convert(UserProfileUpdateReqVO bean); + } From 60bb8dd29ca970cfed61765812ff372e313ad080 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 15 May 2022 22:23:28 +0800 Subject: [PATCH 10/13] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20oauth2=20=E7=9A=84?= =?UTF-8?q?=20code=20=E7=9A=84=E7=94=9F=E6=88=90=E4=B8=8E=E6=B6=88?= =?UTF-8?q?=E8=B4=B9=E7=9A=84=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../system/enums/ErrorCodeConstants.java | 12 ++-- .../admin/oauth2/OAuth2OpenController.http | 4 +- .../OAuth2AccessTokenMapper.java | 2 +- .../{auth => oauth2}/OAuth2ClientMapper.java | 2 +- .../dal/mysql/oauth2/OAuth2CodeMapper.java | 14 ++++ .../OAuth2RefreshTokenMapper.java | 2 +- .../OAuth2AccessTokenRedisDAO.java | 2 +- .../oauth2/OAuth2ClientServiceImpl.java | 2 +- .../service/oauth2/OAuth2CodeService.java | 25 ++++++++ .../service/oauth2/OAuth2CodeServiceImpl.java | 64 +++++++++++++++++++ .../oauth2/OAuth2GrantServiceImpl.java | 15 ++--- .../oauth2/OAuth2TokenServiceImpl.java | 6 +- .../auth/OAuth2ClientServiceImplTest.java | 2 +- 13 files changed, 128 insertions(+), 24 deletions(-) rename yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/{auth => oauth2}/OAuth2AccessTokenMapper.java (96%) rename yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/{auth => oauth2}/OAuth2ClientMapper.java (95%) create mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2CodeMapper.java rename yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/{auth => oauth2}/OAuth2RefreshTokenMapper.java (92%) rename yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/{auth => oauth2}/OAuth2AccessTokenRedisDAO.java (97%) create mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeServiceImpl.java 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 24aa5e127..2f39519a3 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 @@ -133,9 +133,13 @@ public interface ErrorCodeConstants { ErrorCode OAUTH2_CLIENT_CLIENT_SECRET_ERROR = new ErrorCode(1002020006, "无效 client_secret: {}"); // ========== OAuth2 授权 1002021000 ========= - ErrorCode OAUTH2_GRANT_CLIENT_ID_MISMATCH = new ErrorCode(1002020000, "client_id 不匹配"); - ErrorCode OAUTH2_GRANT_REDIRECT_URI_MISMATCH = new ErrorCode(1002020001, "redirect_uri 不匹配"); - ErrorCode OAUTH2_GRANT_STATE_MISMATCH = new ErrorCode(1002020002, "state 不匹配"); - ErrorCode OAUTH2_GRANT_CODE_NOT_EXISTS = new ErrorCode(1002020003, "code 不存在"); + ErrorCode OAUTH2_GRANT_CLIENT_ID_MISMATCH = new ErrorCode(1002021000, "client_id 不匹配"); + ErrorCode OAUTH2_GRANT_REDIRECT_URI_MISMATCH = new ErrorCode(1002021001, "redirect_uri 不匹配"); + ErrorCode OAUTH2_GRANT_STATE_MISMATCH = new ErrorCode(1002021002, "state 不匹配"); + ErrorCode OAUTH2_GRANT_CODE_NOT_EXISTS = new ErrorCode(1002021003, "code 不存在"); + + // ========== OAuth2 授权 1002022000 ========= + ErrorCode OAUTH2_CODE_NOT_EXISTS = new ErrorCode(1002022000, "code 不存在"); + ErrorCode OAUTH2_CODE_EXPIRE = new ErrorCode(1002022000, "code 已过期"); } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.http index 16a7025c0..288f0f672 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.http +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.http @@ -12,7 +12,7 @@ Content-Type: application/x-www-form-urlencoded Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} -response_type=code&client_id=default&scope={"user_info": true}&redirect_uri=https://www.iocoder.cn&auto_approve=true +response_type=code&client_id=default&scope={"user.read": true}&redirect_uri=https://www.iocoder.cn&auto_approve=true ### 请求 /system/oauth2/token + code 接口 => 成功 POST {{baseUrl}}/system/oauth2/token @@ -20,7 +20,7 @@ Content-Type: application/x-www-form-urlencoded Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw== tenant-id: {{adminTenentId}} -grant_type=authorization_code&redirect_uri=https://www.iocoder.cn +grant_type=authorization_code&redirect_uri=https://www.iocoder.cn&code=189956c07a174588a97157eabef2f93a ### 请求 /system/oauth2/token + password 接口 => 成功 POST {{baseUrl}}/system/oauth2/token diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2AccessTokenMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2AccessTokenMapper.java similarity index 96% rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2AccessTokenMapper.java rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2AccessTokenMapper.java index b3d8e4e0b..12ac6ddba 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2AccessTokenMapper.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2AccessTokenMapper.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.system.dal.mysql.auth; +package cn.iocoder.yudao.module.system.dal.mysql.oauth2; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2ClientMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2ClientMapper.java similarity index 95% rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2ClientMapper.java rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2ClientMapper.java index a3f9093d7..e7d4c5cd6 100755 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2ClientMapper.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2ClientMapper.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.system.dal.mysql.auth; +package cn.iocoder.yudao.module.system.dal.mysql.oauth2; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2CodeMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2CodeMapper.java new file mode 100644 index 000000000..d5fdd7589 --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2CodeMapper.java @@ -0,0 +1,14 @@ +package cn.iocoder.yudao.module.system.dal.mysql.oauth2; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2CodeDO; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface OAuth2CodeMapper extends BaseMapperX { + + default OAuth2CodeDO selectByCode(String code) { + return selectOne(OAuth2CodeDO::getCode, code); + } + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2RefreshTokenMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2RefreshTokenMapper.java similarity index 92% rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2RefreshTokenMapper.java rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2RefreshTokenMapper.java index 0e46e4767..304aa4433 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2RefreshTokenMapper.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2RefreshTokenMapper.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.system.dal.mysql.auth; +package cn.iocoder.yudao.module.system.dal.mysql.oauth2; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/auth/OAuth2AccessTokenRedisDAO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/oauth2/OAuth2AccessTokenRedisDAO.java similarity index 97% rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/auth/OAuth2AccessTokenRedisDAO.java rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/oauth2/OAuth2AccessTokenRedisDAO.java index 3afa69300..3ca0aec8a 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/auth/OAuth2AccessTokenRedisDAO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/oauth2/OAuth2AccessTokenRedisDAO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.system.dal.redis.auth; +package cn.iocoder.yudao.module.system.dal.redis.oauth2; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImpl.java index 627197256..1701db28d 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImpl.java @@ -11,7 +11,7 @@ import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2Cl import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientUpdateReqVO; import cn.iocoder.yudao.module.system.convert.auth.OAuth2ClientConvert; import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO; -import cn.iocoder.yudao.module.system.dal.mysql.auth.OAuth2ClientMapper; +import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2ClientMapper; import cn.iocoder.yudao.module.system.mq.producer.auth.OAuth2ClientProducer; import com.google.common.annotations.VisibleForTesting; import lombok.Getter; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeService.java index 37b5f54d5..31ed9bcf8 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeService.java @@ -1,5 +1,9 @@ package cn.iocoder.yudao.module.system.service.oauth2; +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2CodeDO; + +import java.util.List; + /** * OAuth2.0 授权码 Service 接口 * @@ -9,6 +13,27 @@ package cn.iocoder.yudao.module.system.service.oauth2; */ public interface OAuth2CodeService { + /** + * 创建授权码 + * + * 参考 JdbcAuthorizationCodeServices 的 createAuthorizationCode 方法 + * + * @param userId 用户编号 + * @param userType 用户类型 + * @param clientId 客户端编号 + * @param scopes 授权范围 + * @param redirectUri 重定向 URI + * @param state 状态 + * @return 授权码的信息 + */ + OAuth2CodeDO createAuthorizationCode(Long userId, Integer userType, String clientId, List scopes, + String redirectUri, String state); + /** + * 使用授权码 + * + * @param code 授权码 + */ + OAuth2CodeDO consumeAuthorizationCode(String code); } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeServiceImpl.java new file mode 100644 index 000000000..0181d8aa0 --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeServiceImpl.java @@ -0,0 +1,64 @@ +package cn.iocoder.yudao.module.system.service.oauth2; + +import cn.hutool.core.util.IdUtil; +import cn.iocoder.yudao.framework.common.util.date.DateUtils; +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2CodeDO; +import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2CodeMapper; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.util.Calendar; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.OAUTH2_CODE_EXPIRE; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.OAUTH2_CODE_NOT_EXISTS; + +/** + * OAuth2.0 授权码 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class OAuth2CodeServiceImpl implements OAuth2CodeService { + + /** + * 授权码的过期时间,默认 5 分钟 + */ + private static final Integer TIMEOUT = 5 * 60; + + @Resource + private OAuth2CodeMapper oauth2CodeMapper; + + @Override + public OAuth2CodeDO createAuthorizationCode(Long userId, Integer userType, String clientId, List scopes, + String redirectUri, String state) { + OAuth2CodeDO codeDO = new OAuth2CodeDO().setCode(generateCode()) + .setUserId(userId).setUserType(userType) + .setClientId(clientId).setScopes(scopes) + .setExpiresTime(DateUtils.addDate(Calendar.SECOND, TIMEOUT)) + .setRedirectUri(redirectUri).setState(state); + oauth2CodeMapper.insert(codeDO); + return codeDO; + } + + @Override + public OAuth2CodeDO consumeAuthorizationCode(String code) { + OAuth2CodeDO codeDO = oauth2CodeMapper.selectByCode(code); + if (codeDO == null) { + throw exception(OAUTH2_CODE_NOT_EXISTS); + } + if (DateUtils.isExpired(codeDO.getExpiresTime())) { + throw exception(OAUTH2_CODE_EXPIRE); + } + oauth2CodeMapper.deleteById(codeDO.getId()); + return codeDO; + } + + private static String generateCode() { + return IdUtil.fastSimpleUUID(); + } + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImpl.java index 3d50b3956..3390cd236 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImpl.java @@ -15,8 +15,6 @@ import javax.annotation.Resource; import java.util.List; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.OAUTH2_GRANT_CODE_NOT_EXISTS; -import static java.util.Collections.singletonList; /** * OAuth2 授予 Service 实现类 @@ -29,6 +27,8 @@ public class OAuth2GrantServiceImpl implements OAuth2GrantService { @Resource private OAuth2TokenService oauth2TokenService; @Resource + private OAuth2CodeService oauth2CodeService; + @Resource private AdminAuthService adminAuthService; @Override @@ -41,18 +41,15 @@ public class OAuth2GrantServiceImpl implements OAuth2GrantService { public String grantAuthorizationCodeForCode(Long userId, Integer userType, String clientId, List scopes, String redirectUri, String state) { - return "test"; + return oauth2CodeService.createAuthorizationCode(userId, userType, clientId, scopes, + redirectUri, state).getCode(); } @Override public OAuth2AccessTokenDO grantAuthorizationCodeForAccessToken(String clientId, String code, String redirectUri, String state) { - // TODO 消费 code - OAuth2CodeDO codeDO = new OAuth2CodeDO().setClientId("default").setRedirectUri("https://www.iocoder.cn").setState("") - .setUserId(1L).setUserType(2).setScopes(singletonList("user_info")); - if (codeDO == null) { - throw exception(OAUTH2_GRANT_CODE_NOT_EXISTS); - } + OAuth2CodeDO codeDO = oauth2CodeService.consumeAuthorizationCode(code); + Assert.notNull(codeDO, "授权码不能为空"); // 防御性编程 // 校验 clientId 是否匹配 if (!StrUtil.equals(clientId, codeDO.getClientId())) { throw exception(ErrorCodeConstants.OAUTH2_GRANT_CLIENT_ID_MISMATCH); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImpl.java index a476f04f5..37e1c6369 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImpl.java @@ -11,9 +11,9 @@ import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token.OAuth2Acc import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO; import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2RefreshTokenDO; -import cn.iocoder.yudao.module.system.dal.mysql.auth.OAuth2AccessTokenMapper; -import cn.iocoder.yudao.module.system.dal.mysql.auth.OAuth2RefreshTokenMapper; -import cn.iocoder.yudao.module.system.dal.redis.auth.OAuth2AccessTokenRedisDAO; +import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2AccessTokenMapper; +import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2RefreshTokenMapper; +import cn.iocoder.yudao.module.system.dal.redis.oauth2.OAuth2AccessTokenRedisDAO; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ClientServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ClientServiceImplTest.java index bc22d23d4..859cc5a4c 100755 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ClientServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ClientServiceImplTest.java @@ -7,7 +7,7 @@ import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2Cl import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientPageReqVO; import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientUpdateReqVO; import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO; -import cn.iocoder.yudao.module.system.dal.mysql.auth.OAuth2ClientMapper; +import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2ClientMapper; import cn.iocoder.yudao.module.system.mq.producer.auth.OAuth2ClientProducer; import cn.iocoder.yudao.module.system.service.oauth2.OAuth2ClientServiceImpl; import org.junit.jupiter.api.Disabled; From c7a29167480bf2f5c0d5b27600ce12feab9379f7 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 16 May 2022 00:00:36 +0800 Subject: [PATCH 11/13] =?UTF-8?q?=E5=AE=8C=E6=88=90=20system=5Foauth2=5Fap?= =?UTF-8?q?prove=20=E7=9B=B8=E5=85=B3=E7=9A=84=E6=89=B9=E5=87=86=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../util/collection/CollectionUtils.java | 7 ++ .../system/api/auth/OAuth2TokenApiImpl.java | 2 +- .../admin/oauth2/OAuth2ClientController.java | 2 +- .../admin/oauth2/OAuth2OpenController.java | 4 +- .../admin/oauth2/OAuth2TokenController.java | 2 +- .../system/convert/auth/AuthConvert.java | 2 +- .../convert/auth/OAuth2ClientConvert.java | 2 +- .../convert/auth/OAuth2TokenConvert.java | 2 +- .../convert/oauth2/OAuth2OpenConvert.java | 2 +- .../{auth => oauth2}/OAuth2AccessTokenDO.java | 2 +- .../dataobject/oauth2/OAuth2ApproveDO.java | 63 ++++++++++++++ .../{auth => oauth2}/OAuth2ClientDO.java | 2 +- .../{auth => oauth2}/OAuth2CodeDO.java | 2 +- .../OAuth2RefreshTokenDO.java | 2 +- .../mysql/oauth2/OAuth2AccessTokenMapper.java | 2 +- .../dal/mysql/oauth2/OAuth2ApproveMapper.java | 27 ++++++ .../dal/mysql/oauth2/OAuth2ClientMapper.java | 2 +- .../dal/mysql/oauth2/OAuth2CodeMapper.java | 2 +- .../oauth2/OAuth2RefreshTokenMapper.java | 2 +- .../system/dal/redis/RedisKeyConstants.java | 2 +- .../oauth2/OAuth2AccessTokenRedisDAO.java | 2 +- .../service/auth/AdminAuthServiceImpl.java | 2 +- .../service/oauth2/OAuth2ApproveService.java | 21 ++++- .../oauth2/OAuth2ApproveServiceImpl.java | 84 +++++++++++++++++-- .../service/oauth2/OAuth2ClientService.java | 2 +- .../oauth2/OAuth2ClientServiceImpl.java | 2 +- .../service/oauth2/OAuth2CodeService.java | 2 +- .../service/oauth2/OAuth2CodeServiceImpl.java | 2 +- .../service/oauth2/OAuth2GrantService.java | 2 +- .../oauth2/OAuth2GrantServiceImpl.java | 4 +- .../service/oauth2/OAuth2TokenService.java | 2 +- .../oauth2/OAuth2TokenServiceImpl.java | 6 +- .../auth/AdminAuthServiceImplTest.java | 2 +- .../auth/OAuth2ClientServiceImplTest.java | 2 +- 34 files changed, 225 insertions(+), 43 deletions(-) rename yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/{auth => oauth2}/OAuth2AccessTokenDO.java (96%) create mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2ApproveDO.java rename yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/{auth => oauth2}/OAuth2ClientDO.java (97%) rename yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/{auth => oauth2}/OAuth2CodeDO.java (96%) rename yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/{auth => oauth2}/OAuth2RefreshTokenDO.java (96%) create mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2ApproveMapper.java diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java index a6466ab44..6b83bb3d6 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java @@ -68,6 +68,13 @@ public class CollectionUtils { return from.stream().map(func).filter(Objects::nonNull).collect(Collectors.toSet()); } + public static Set convertSet(Collection from, Function func, Predicate filter) { + if (CollUtil.isEmpty(from)) { + return new HashSet<>(); + } + return from.stream().filter(filter).map(func).filter(Objects::nonNull).collect(Collectors.toSet()); + } + public static Map convertMap(Collection from, Function keyFunc) { if (CollUtil.isEmpty(from)) { return new HashMap<>(); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/auth/OAuth2TokenApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/auth/OAuth2TokenApiImpl.java index 41a587f11..438cf9463 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/auth/OAuth2TokenApiImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/auth/OAuth2TokenApiImpl.java @@ -4,7 +4,7 @@ import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCheckRespDTO import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCreateReqDTO; import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenRespDTO; import cn.iocoder.yudao.module.system.convert.auth.OAuth2TokenConvert; -import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; import cn.iocoder.yudao.module.system.service.oauth2.OAuth2TokenService; import org.springframework.stereotype.Service; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2ClientController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2ClientController.java index 0e5b109ee..d6b927857 100755 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2ClientController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2ClientController.java @@ -7,7 +7,7 @@ import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2Cl import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientRespVO; import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientUpdateReqVO; import cn.iocoder.yudao.module.system.convert.auth.OAuth2ClientConvert; -import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; import cn.iocoder.yudao.module.system.service.oauth2.OAuth2ClientService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.java index f6b588c90..dfceb6320 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.java @@ -15,8 +15,8 @@ import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2Open import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.user.OAuth2OpenUserInfoRespVO; import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdateReqVO; import cn.iocoder.yudao.module.system.convert.oauth2.OAuth2OpenConvert; -import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; -import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO; import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2TokenController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2TokenController.java index 3adacf4e2..c055a19df 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2TokenController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2TokenController.java @@ -5,7 +5,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token.OAuth2AccessTokenPageReqVO; import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token.OAuth2AccessTokenRespVO; import cn.iocoder.yudao.module.system.convert.auth.OAuth2TokenConvert; -import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; import cn.iocoder.yudao.module.system.service.auth.AdminAuthService; import cn.iocoder.yudao.module.system.service.oauth2.OAuth2TokenService; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java index d03cf7960..62784defa 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java @@ -5,7 +5,7 @@ import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO; import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*; -import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/OAuth2ClientConvert.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/OAuth2ClientConvert.java index 021eea271..87f408aac 100755 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/OAuth2ClientConvert.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/OAuth2ClientConvert.java @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientCreateReqVO; import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientRespVO; import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientUpdateReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/OAuth2TokenConvert.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/OAuth2TokenConvert.java index 0530ebe04..50a6b977f 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/OAuth2TokenConvert.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/OAuth2TokenConvert.java @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCheckRespDTO; import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenRespDTO; import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token.OAuth2AccessTokenRespVO; -import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/oauth2/OAuth2OpenConvert.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/oauth2/OAuth2OpenConvert.java index af9158019..0972670a2 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/oauth2/OAuth2OpenConvert.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/oauth2/OAuth2OpenConvert.java @@ -6,7 +6,7 @@ import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2Open import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenCheckTokenRespVO; import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.user.OAuth2OpenUserInfoRespVO; import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdateReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO; import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2AccessTokenDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2AccessTokenDO.java similarity index 96% rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2AccessTokenDO.java rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2AccessTokenDO.java index e08f2b7db..8cecb8f1a 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2AccessTokenDO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2AccessTokenDO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.system.dal.dataobject.auth; +package cn.iocoder.yudao.module.system.dal.dataobject.oauth2; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2ApproveDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2ApproveDO.java new file mode 100644 index 000000000..ac1bed923 --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2ApproveDO.java @@ -0,0 +1,63 @@ +package cn.iocoder.yudao.module.system.dal.dataobject.oauth2; + +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.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * OAuth2 批准 DO + * + * 用户在 authorize.vue 界面时,记录接受的 scope 列表 + * + * @author 芋道源码 + */ +@TableName(value = "system_oauth2_approve", autoResultMap = true) +@KeySequence("system_oauth2_approve_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +public class OAuth2ApproveDO extends BaseDO { + + /** + * 编号,数据库自增 + */ + @TableId + private Long id; + /** + * 用户编号 + */ + private Long userId; + /** + * 用户类型 + * + * 枚举 {@link UserTypeEnum} + */ + private Integer userType; + /** + * 客户端编号 + * + * 关联 {@link OAuth2ClientDO#getId()} + */ + private String clientId; + /** + * 授权范围 + */ + private String scope; + /** + * 是否接受 + * + * true - 接受 + * false - 拒绝 + */ + private Boolean approved; + /** + * 过期时间 + */ + private Date expiresTime; + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2ClientDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2ClientDO.java similarity index 97% rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2ClientDO.java rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2ClientDO.java index dee649e5e..7264f6c41 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2ClientDO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2ClientDO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.system.dal.dataobject.auth; +package cn.iocoder.yudao.module.system.dal.dataobject.oauth2; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 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/oauth2/OAuth2CodeDO.java similarity index 96% rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2CodeDO.java rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2CodeDO.java index f7599ffc6..92fec8e75 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/oauth2/OAuth2CodeDO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.system.dal.dataobject.auth; +package cn.iocoder.yudao.module.system.dal.dataobject.oauth2; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2RefreshTokenDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2RefreshTokenDO.java similarity index 96% rename from yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2RefreshTokenDO.java rename to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2RefreshTokenDO.java index 6575a7153..a933e1c52 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2RefreshTokenDO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2RefreshTokenDO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.system.dal.dataobject.auth; +package cn.iocoder.yudao.module.system.dal.dataobject.oauth2; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2AccessTokenMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2AccessTokenMapper.java index 12ac6ddba..3c91b4e67 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2AccessTokenMapper.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2AccessTokenMapper.java @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token.OAuth2AccessTokenPageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; import org.apache.ibatis.annotations.Mapper; import java.util.Date; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2ApproveMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2ApproveMapper.java new file mode 100644 index 000000000..e881bbd99 --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2ApproveMapper.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.system.dal.mysql.oauth2; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ApproveDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface OAuth2ApproveMapper extends BaseMapperX { + + default int update(OAuth2ApproveDO updateObj) { + return update(updateObj, new LambdaQueryWrapperX() + .eq(OAuth2ApproveDO::getUserId, updateObj.getUserId()) + .eq(OAuth2ApproveDO::getUserType, updateObj.getUserType()) + .eq(OAuth2ApproveDO::getClientId, updateObj.getClientId())); + } + + default List selectListByUserIdAndUserTypeAndClientId(Long userId, Integer userType, String clientId) { + return selectList(new LambdaQueryWrapperX() + .eq(OAuth2ApproveDO::getUserId, userId) + .eq(OAuth2ApproveDO::getUserType, userType) + .eq(OAuth2ApproveDO::getClientId, clientId)); + } + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2ClientMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2ClientMapper.java index e7d4c5cd6..13261befc 100755 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2ClientMapper.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2ClientMapper.java @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientPageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2CodeMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2CodeMapper.java index d5fdd7589..c85581c61 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2CodeMapper.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2CodeMapper.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.system.dal.mysql.oauth2; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2CodeDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2CodeDO; import org.apache.ibatis.annotations.Mapper; @Mapper diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2RefreshTokenMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2RefreshTokenMapper.java index 304aa4433..713be89cf 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2RefreshTokenMapper.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2RefreshTokenMapper.java @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.system.dal.mysql.oauth2; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2RefreshTokenDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2RefreshTokenDO; import org.apache.ibatis.annotations.Mapper; @Mapper diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/RedisKeyConstants.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/RedisKeyConstants.java index 839b973ef..5760e3c0f 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/RedisKeyConstants.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/RedisKeyConstants.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.system.dal.redis; import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine; -import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; import java.time.Duration; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/oauth2/OAuth2AccessTokenRedisDAO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/oauth2/OAuth2AccessTokenRedisDAO.java index 3ca0aec8a..a65215224 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/oauth2/OAuth2AccessTokenRedisDAO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/oauth2/OAuth2AccessTokenRedisDAO.java @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.system.dal.redis.oauth2; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Repository; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java index 898b08abf..6b96e1e6f 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java @@ -10,7 +10,7 @@ import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO; import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*; import cn.iocoder.yudao.module.system.convert.auth.AuthConvert; -import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; import cn.iocoder.yudao.module.system.enums.auth.OAuth2ClientConstants; import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ApproveService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ApproveService.java index bd1f1685c..def3e5e2c 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ApproveService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ApproveService.java @@ -1,6 +1,9 @@ package cn.iocoder.yudao.module.system.service.oauth2; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ApproveDO; + import java.util.Collection; +import java.util.List; import java.util.Map; /** @@ -20,10 +23,10 @@ public interface OAuth2ApproveService { * @param userId 用户编号 * @param userType 用户类型 * @param clientId 客户端编号 - * @param scopes 授权范围 + * @param requestedScopes 授权范围 * @return 是否授权通过 */ - boolean checkForPreApproval(Long userId, Integer userType, String clientId, Collection scopes); + boolean checkForPreApproval(Long userId, Integer userType, String clientId, Collection requestedScopes); /** * 在用户发起批准时,基于 scopes 的选项,计算最终是否通过 @@ -31,9 +34,19 @@ public interface OAuth2ApproveService { * @param userId 用户编号 * @param userType 用户类型 * @param clientId 客户端编号 - * @param scopes 授权范围 + * @param requestedScopes 授权范围 * @return 是否授权通过 */ - boolean updateAfterApproval(Long userId, Integer userType, String clientId, Map scopes); + boolean updateAfterApproval(Long userId, Integer userType, String clientId, Map requestedScopes); + + /** + * 获得用户的批准列表,排除已过期的 + * + * @param userId 用户编号 + * @param userType 用户类型 + * @param clientId 客户端编号 + * @return 是否授权通过 + */ + List getApproveList(Long userId, Integer userType, String clientId); } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ApproveServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ApproveServiceImpl.java index 18191caac..d21cec590 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ApproveServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ApproveServiceImpl.java @@ -1,9 +1,18 @@ package cn.iocoder.yudao.module.system.service.oauth2; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.framework.common.util.date.DateUtils; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ApproveDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; +import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2ApproveMapper; import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; -import java.util.Collection; -import java.util.Map; +import javax.annotation.Resource; +import java.util.*; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; /** * OAuth2 批准 Service 实现类 @@ -11,16 +20,79 @@ import java.util.Map; * @author 芋道源码 */ @Service +@Validated public class OAuth2ApproveServiceImpl implements OAuth2ApproveService { + /** + * 批准的过期时间,默认 30 天 + */ + private static final Integer TIMEOUT = 30 * 24 * 60; // 单位:秒 + + @Resource + private OAuth2ClientService oauth2ClientService; + + @Resource + private OAuth2ApproveMapper oauth2ApproveMapper; + @Override - public boolean checkForPreApproval(Long userId, Integer userType, String clientId, Collection scopes) { - return true; + public boolean checkForPreApproval(Long userId, Integer userType, String clientId, Collection requestedScopes) { + // 第一步,基于 Client 的自动授权计算,如果 scopes 都在自动授权中,则返回 true 通过 + OAuth2ClientDO clientDO = oauth2ClientService.validOAuthClientFromCache(clientId); + Assert.notNull(clientDO, "客户端不能为空"); // 防御性编程 + if (CollUtil.containsAll(clientDO.getAutoApproveScopes(), requestedScopes)) { + // gh-877 - if all scopes are auto approved, approvals still need to be added to the approval store. + Date expireTime = DateUtils.addDate(Calendar.SECOND, TIMEOUT); + for (String scope : requestedScopes) { + saveApprove(userId, userType, clientId, scope, true, expireTime); + } + return true; + } + + // 第二步,算上用户已经批准的授权。如果 scopes 都包含,则返回 true + List approveDOs = oauth2ApproveMapper.selectListByUserIdAndUserTypeAndClientId( + userId, userType, clientId); + Set scopes = convertSet(approveDOs, OAuth2ApproveDO::getScope, + o -> o.getApproved() && !DateUtils.isExpired(o.getExpiresTime())); // 只保留未过期的 + return CollUtil.containsAll(scopes, requestedScopes); } @Override - public boolean updateAfterApproval(Long userId, Integer userType, String clientId, Map scopes) { - return false; + public boolean updateAfterApproval(Long userId, Integer userType, String clientId, Map requestedScopes) { + // 如果 requestedScopes 为空,说明没有要求,则返回 true 通过 + if (CollUtil.isEmpty(requestedScopes)) { + return true; + } + + // 更新批准的信息 + boolean success = false; // 需要至少有一个同意 + Date expireTime = DateUtils.addDate(Calendar.SECOND, TIMEOUT); + for (Map.Entry entry :requestedScopes.entrySet()) { + if (entry.getValue()) { + success = true; + } + saveApprove(userId, userType, clientId, entry.getKey(), entry.getValue(), expireTime); + } + return success; + } + + @Override + public List getApproveList(Long userId, Integer userType, String clientId) { + List approveDOs = oauth2ApproveMapper.selectListByUserIdAndUserTypeAndClientId( + userId, userType, clientId); + approveDOs.removeIf(o -> DateUtils.isExpired(o.getExpiresTime())); + return approveDOs; + } + + private void saveApprove(Long userId, Integer userType, String clientId, + String scope, Boolean approved, Date expireTime) { + // 先更新 + OAuth2ApproveDO approveDO = new OAuth2ApproveDO().setUserId(userId).setUserType(userType) + .setClientId(clientId).setScope(scope).setApproved(approved).setExpiresTime(expireTime); + if (oauth2ApproveMapper.update(approveDO) == 1) { + return; + } + // 失败,则说明不存在,进行更新 + oauth2ApproveMapper.insert(approveDO); } } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientService.java index a147fcc14..60e9f2298 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientService.java @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientCreateReqVO; import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientPageReqVO; import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientUpdateReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; import javax.validation.Valid; import java.util.Collection; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImpl.java index 1701db28d..7743e9069 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImpl.java @@ -10,7 +10,7 @@ import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2Cl import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientPageReqVO; import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientUpdateReqVO; import cn.iocoder.yudao.module.system.convert.auth.OAuth2ClientConvert; -import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2ClientMapper; import cn.iocoder.yudao.module.system.mq.producer.auth.OAuth2ClientProducer; import com.google.common.annotations.VisibleForTesting; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeService.java index 31ed9bcf8..f59671fe9 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeService.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.system.service.oauth2; -import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2CodeDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2CodeDO; import java.util.List; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeServiceImpl.java index 0181d8aa0..0dae7692f 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeServiceImpl.java @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.system.service.oauth2; import cn.hutool.core.util.IdUtil; import cn.iocoder.yudao.framework.common.util.date.DateUtils; -import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2CodeDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2CodeDO; import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2CodeMapper; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantService.java index f87a18cc1..6b7a8f56a 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantService.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.system.service.oauth2; -import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; import java.util.List; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImpl.java index 3390cd236..adb07f9b5 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImpl.java @@ -4,8 +4,8 @@ import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; -import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2CodeDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2CodeDO; import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; import cn.iocoder.yudao.module.system.enums.ErrorCodeConstants; import cn.iocoder.yudao.module.system.service.auth.AdminAuthService; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenService.java index 1fdce710d..977d93539 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenService.java @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.system.service.oauth2; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token.OAuth2AccessTokenPageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; import java.util.List; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImpl.java index 37e1c6369..cd253b33c 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImpl.java @@ -8,9 +8,9 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token.OAuth2AccessTokenPageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; -import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO; -import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2RefreshTokenDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2RefreshTokenDO; import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2AccessTokenMapper; import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2RefreshTokenMapper; import cn.iocoder.yudao.module.system.dal.redis.oauth2.OAuth2AccessTokenRedisDAO; diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImplTest.java index 44141a3b2..435e5791f 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImplTest.java @@ -7,7 +7,7 @@ import cn.iocoder.yudao.framework.test.core.util.AssertUtils; import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthLoginReqVO; import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthLoginRespVO; -import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum; diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ClientServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ClientServiceImplTest.java index 859cc5a4c..ef10f2419 100755 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ClientServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ClientServiceImplTest.java @@ -6,7 +6,7 @@ import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientCreateReqVO; import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientPageReqVO; import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientUpdateReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2ClientMapper; import cn.iocoder.yudao.module.system.mq.producer.auth.OAuth2ClientProducer; import cn.iocoder.yudao.module.system.service.oauth2.OAuth2ClientServiceImpl; From d18463866ef0ede008eb8de1208a3feedea5396d Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 24 May 2022 00:01:30 +0800 Subject: [PATCH 12/13] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20sso=20=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/oauth2/OAuth2OpenController.http | 9 +- .../admin/oauth2/OAuth2OpenController.java | 56 ++++----- .../oauth2/vo/client/OAuth2ClientBaseVO.java | 18 +-- .../vo/client/OAuth2ClientPageReqVO.java | 4 +- .../oauth2/vo/client/OAuth2ClientRespVO.java | 2 +- .../vo/client/OAuth2ClientUpdateReqVO.java | 2 +- .../open/OAuth2OpenAuthorizeInfoRespVO.java | 39 +++++++ .../convert/oauth2/OAuth2OpenConvert.java | 25 +++- .../dataobject/oauth2/OAuth2ApproveDO.java | 2 +- .../oauth2/OAuth2ClientServiceImpl.java | 2 +- yudao-ui-admin/src/api/login.js | 38 +++--- yudao-ui-admin/src/router/index.js | 4 +- .../src/views/{authorize.vue => sso.vue} | 109 +++++++++++++----- 13 files changed, 215 insertions(+), 95 deletions(-) create mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenAuthorizeInfoRespVO.java rename yudao-ui-admin/src/views/{authorize.vue => sso.vue} (59%) diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.http index 288f0f672..e11aee721 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.http +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.http @@ -1,10 +1,15 @@ +### 请求 /system/oauth2/authorize 接口 => 成功 +GET {{baseUrl}}/system/oauth2/authorize?clientId=default +Authorization: Bearer {{token}} +tenant-id: {{adminTenentId}} + ### 请求 /system/oauth2/authorize + token 接口 => 成功 POST {{baseUrl}}/system/oauth2/authorize Content-Type: application/x-www-form-urlencoded Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} -response_type=token&client_id=default&scope={"user_info": true}&redirect_uri=https://www.iocoder.cn&auto_approve=true +response_type=token&client_id=default&scope={"user.read": true}&redirect_uri=https://www.iocoder.cn&auto_approve=true ### 请求 /system/oauth2/authorize + code 接口 => 成功 POST {{baseUrl}}/system/oauth2/authorize @@ -12,7 +17,7 @@ Content-Type: application/x-www-form-urlencoded Authorization: Bearer {{token}} tenant-id: {{adminTenentId}} -response_type=code&client_id=default&scope={"user.read": true}&redirect_uri=https://www.iocoder.cn&auto_approve=true +response_type=code&client_id=default&scope={"user.read": true}&redirect_uri=https://www.iocoder.cn&auto_approve=false ### 请求 /system/oauth2/token + code 接口 => 成功 POST {{baseUrl}}/system/oauth2/token diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.java index dfceb6320..6034c6096 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.java @@ -11,14 +11,16 @@ import cn.iocoder.yudao.framework.common.util.http.HttpUtils; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenAccessTokenRespVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenAuthorizeInfoRespVO; import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenCheckTokenRespVO; import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.user.OAuth2OpenUserInfoRespVO; -import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdateReqVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.user.OAuth2OpenUserUpdateReqVO; import cn.iocoder.yudao.module.system.convert.oauth2.OAuth2OpenConvert; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ApproveDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; import cn.iocoder.yudao.module.system.enums.auth.OAuth2GrantTypeEnum; import cn.iocoder.yudao.module.system.service.dept.DeptService; @@ -91,7 +93,7 @@ public class OAuth2OpenController { * 注意,默认需要传递 client_id + client_secret 参数 */ @PostMapping("/token") - @ApiOperation(value = "获得访问令牌", notes = "适合 code 授权码模式,或者 implicit 简化模式;在 authorize.vue 单点登录界面被【获取】调用") + @ApiOperation(value = "获得访问令牌", notes = "适合 code 授权码模式,或者 implicit 简化模式;在 sso.vue 单点登录界面被【获取】调用") @ApiImplicitParams({ @ApiImplicitParam(name = "grant_type", required = true, value = "授权类型", example = "code", dataTypeClass = String.class), @ApiImplicitParam(name = "code", value = "授权范围", example = "userinfo.read", dataTypeClass = String.class), @@ -173,7 +175,7 @@ public class OAuth2OpenController { @RequestParam("token") String token) { // 校验客户端 String[] clientIdAndSecret = obtainBasicAuthorization(request); - OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientIdAndSecret[0], clientIdAndSecret[1], + oauth2ClientService.validOAuthClientFromCache(clientIdAndSecret[0], clientIdAndSecret[1], null, null, null); // 校验令牌 @@ -182,46 +184,36 @@ public class OAuth2OpenController { return success(OAuth2OpenConvert.INSTANCE.convert2(accessTokenDO)); } - // GET oauth/authorize AuthorizationEndpoint TODO + /** + * 对应 Spring Security OAuth 的 AuthorizationEndpoint 类的 authorize 方法 + */ @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 = "redirect_uri", required = true, value = "重定向 URI", example = "https://www.iocoder.cn", dataTypeClass = String.class), - @ApiImplicitParam(name = "state", example = "123321", dataTypeClass = String.class) - }) - 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, ' '); + @ApiOperation(value = "获得授权信息", notes = "适合 code 授权码模式,或者 implicit 简化模式;在 sso.vue 单点登录界面被【获取】调用") + @ApiImplicitParam(name = "clientId", required = true, value = "客户端编号", example = "tudou", dataTypeClass = String.class) + public CommonResult authorize(@RequestParam("clientId") String clientId) { // 0. 校验用户已经登录。通过 Spring Security 实现 - // 1.1 校验 responseType 是否满足 code 或者 token 值 - OAuth2GrantTypeEnum grantTypeEnum = getGrantTypeEnum(responseType); - // 1.2 校验 redirectUri 重定向域名是否合法 + 校验 scope 是否在 Client 授权范围内 - oauth2ClientService.validOAuthClientFromCache(clientId, null, - grantTypeEnum.getGrantType(), scopes, redirectUri); - - // 3. 不满足自动授权,则返回授权相关的展示信息 - return null; + // 1. 获得 Client 客户端的信息 + OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientId, null, + null, null, null); + // 2. 获得用户已经授权的信息 + List approves = oauth2ApproveService.getApproveList(getLoginUserId(), getUserType(), clientId); + // 拼接返回 + return success(OAuth2OpenConvert.INSTANCE.convert(client, approves)); } /** * 对应 Spring Security OAuth 的 AuthorizationEndpoint 类的 approveOrDeny 方法 * * 场景一:【自动授权 autoApprove = true】 - * 刚进入 authorize.vue 界面,调用该接口,用户历史已经给该应用做过对应的授权,或者 OAuth2Client 支持该 scope 的自动授权 + * 刚进入 sso.vue 界面,调用该接口,用户历史已经给该应用做过对应的授权,或者 OAuth2Client 支持该 scope 的自动授权 * 场景二:【手动授权 autoApprove = false】 - * 在 authorize.vue 界面,用户选择好 scope 授权范围,调用该接口,进行授权。此时,approved 为 true 或者 false + * 在 sso.vue 界面,用户选择好 scope 授权范围,调用该接口,进行授权。此时,approved 为 true 或者 false * * 因为前后端分离,Axios 无法很好的处理 302 重定向,所以和 Spring Security OAuth 略有不同,返回结果是重定向的 URL,剩余交给前端处理 */ @PostMapping("/authorize") - @ApiOperation(value = "申请授权", notes = "适合 code 授权码模式,或者 implicit 简化模式;在 authorize.vue 单点登录界面被【提交】调用") + @ApiOperation(value = "申请授权", notes = "适合 code 授权码模式,或者 implicit 简化模式;在 sso.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), @@ -346,7 +338,7 @@ public class OAuth2OpenController { @PutMapping("/user/update") @ApiOperation("更新用户基本信息") @PreAuthorize("@ss.hasScope('user.write')") - public CommonResult updateUserInfo(@Valid @RequestBody UserProfileUpdateReqVO reqVO) { + public CommonResult updateUserInfo(@Valid @RequestBody OAuth2OpenUserUpdateReqVO reqVO) { // 这里将 UserProfileUpdateReqVO =》UserProfileUpdateReqVO 对象,实现接口的复用。 // 主要是,AdminUserService 没有自己的 BO 对象,所以复用只能这么做 userService.updateUserProfile(getLoginUserId(), OAuth2OpenConvert.INSTANCE.convert(reqVO)); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientBaseVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientBaseVO.java index 786bc5267..dbd74e552 100755 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientBaseVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientBaseVO.java @@ -18,39 +18,39 @@ import java.util.List; @Data public class OAuth2ClientBaseVO { - @ApiModelProperty(value = "客户端编号", required = true) + @ApiModelProperty(value = "客户端编号", required = true, example = "tudou") @NotNull(message = "客户端编号不能为空") private String clientId; - @ApiModelProperty(value = "客户端密钥", required = true) + @ApiModelProperty(value = "客户端密钥", required = true, example = "fan") @NotNull(message = "客户端密钥不能为空") private String secret; - @ApiModelProperty(value = "应用名", required = true) + @ApiModelProperty(value = "应用名", required = true, example = "土豆") @NotNull(message = "应用名不能为空") private String name; - @ApiModelProperty(value = "应用图标", required = true) + @ApiModelProperty(value = "应用图标", required = true, example = "https://www.iocoder.cn/xx.png") @NotNull(message = "应用图标不能为空") @URL(message = "应用图标的地址不正确") private String logo; - @ApiModelProperty(value = "应用描述") + @ApiModelProperty(value = "应用描述", example = "我是一个应用") private String description; - @ApiModelProperty(value = "状态", required = true) + @ApiModelProperty(value = "状态", required = true, example = "1", notes = "参见 CommonStatusEnum 枚举") @NotNull(message = "状态不能为空") private Integer status; - @ApiModelProperty(value = "访问令牌的有效期", required = true) + @ApiModelProperty(value = "访问令牌的有效期", required = true, example = "8640") @NotNull(message = "访问令牌的有效期不能为空") private Integer accessTokenValiditySeconds; - @ApiModelProperty(value = "刷新令牌的有效期", required = true) + @ApiModelProperty(value = "刷新令牌的有效期", required = true, example = "8640000") @NotNull(message = "刷新令牌的有效期不能为空") private Integer refreshTokenValiditySeconds; - @ApiModelProperty(value = "可重定向的 URI 地址", required = true) + @ApiModelProperty(value = "可重定向的 URI 地址", required = true, example = "https://www.iocoder.cn") @NotNull(message = "可重定向的 URI 地址不能为空") private List<@NotEmpty(message = "重定向的 URI 不能为空") @URL(message = "重定向的 URI 格式不正确") String> redirectUris; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientPageReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientPageReqVO.java index b6d2a2172..286fc73ac 100755 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientPageReqVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientPageReqVO.java @@ -10,10 +10,10 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam; @ToString(callSuper = true) public class OAuth2ClientPageReqVO extends PageParam { - @ApiModelProperty(value = "应用名") + @ApiModelProperty(value = "应用名", example = "土豆", notes = "模糊匹配") private String name; - @ApiModelProperty(value = "状态") + @ApiModelProperty(value = "状态", example = "1", notes = "参见 CommonStatusEnum 枚举") private Integer status; } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientRespVO.java index e0df90f3d..37800c2dc 100755 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientRespVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientRespVO.java @@ -14,7 +14,7 @@ import java.util.Date; @ToString(callSuper = true) public class OAuth2ClientRespVO extends OAuth2ClientBaseVO { - @ApiModelProperty(value = "编号", required = true) + @ApiModelProperty(value = "编号", required = true, example = "1024") private Long id; @ApiModelProperty(value = "创建时间", required = true) diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientUpdateReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientUpdateReqVO.java index 3b1d2a0f3..024a1511f 100755 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientUpdateReqVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientUpdateReqVO.java @@ -14,7 +14,7 @@ import javax.validation.constraints.NotNull; @ToString(callSuper = true) public class OAuth2ClientUpdateReqVO extends OAuth2ClientBaseVO { - @ApiModelProperty(value = "编号", required = true) + @ApiModelProperty(value = "编号", required = true, example = "1024") @NotNull(message = "编号不能为空") private Long id; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenAuthorizeInfoRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenAuthorizeInfoRespVO.java new file mode 100644 index 000000000..d2a7bb36e --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenAuthorizeInfoRespVO.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open; + +import cn.iocoder.yudao.framework.common.core.KeyValue; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@ApiModel("管理后台 - 授权页的信息 Response VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class OAuth2OpenAuthorizeInfoRespVO { + + /** + * 客户端 + */ + private Client client; + + @ApiModelProperty(value = "scope 的选中信息", required = true, notes = "使用 List 保证有序性,Key 是 scope,Value 为是否选中") + private List> scopes; + + @Data + @NoArgsConstructor + @AllArgsConstructor + public static class Client { + + @ApiModelProperty(value = "应用名", required = true, example = "土豆") + private String name; + + @ApiModelProperty(value = "应用图标", required = true, example = "https://www.iocoder.cn/xx.png") + private String logo; + + } + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/oauth2/OAuth2OpenConvert.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/oauth2/OAuth2OpenConvert.java index 0972670a2..90907d606 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/oauth2/OAuth2OpenConvert.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/oauth2/OAuth2OpenConvert.java @@ -1,20 +1,28 @@ package cn.iocoder.yudao.module.system.convert.oauth2; +import cn.iocoder.yudao.framework.common.core.KeyValue; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenAccessTokenRespVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenAuthorizeInfoRespVO; import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenCheckTokenRespVO; import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.user.OAuth2OpenUserInfoRespVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.user.OAuth2OpenUserUpdateReqVO; import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdateReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ApproveDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; import cn.iocoder.yudao.module.system.util.oauth2.OAuth2Utils; import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; +import java.util.ArrayList; import java.util.List; +import java.util.Map; @Mapper public interface OAuth2OpenConvert { @@ -44,6 +52,19 @@ public interface OAuth2OpenConvert { OAuth2OpenUserInfoRespVO.Dept convert(DeptDO dept); List convertList(List list); - UserProfileUpdateReqVO convert(UserProfileUpdateReqVO bean); + UserProfileUpdateReqVO convert(OAuth2OpenUserUpdateReqVO bean); + + default OAuth2OpenAuthorizeInfoRespVO convert(OAuth2ClientDO client, List approves) { + // 构建 scopes + List> scopes = new ArrayList<>(client.getScopes().size()); + Map approveMap = CollectionUtils.convertMap(approves, OAuth2ApproveDO::getScope); + client.getScopes().forEach(scope -> { + OAuth2ApproveDO approve = approveMap.get(scope); + scopes.add(new KeyValue<>(scope, approve != null ? approve.getApproved() : false)); + }); + // 拼接返回 + return new OAuth2OpenAuthorizeInfoRespVO( + new OAuth2OpenAuthorizeInfoRespVO.Client(client.getName(), client.getLogo()), scopes); + } } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2ApproveDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2ApproveDO.java index ac1bed923..501b799f0 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2ApproveDO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2ApproveDO.java @@ -13,7 +13,7 @@ import java.util.Date; /** * OAuth2 批准 DO * - * 用户在 authorize.vue 界面时,记录接受的 scope 列表 + * 用户在 sso.vue 界面时,记录接受的 scope 列表 * * @author 芋道源码 */ diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImpl.java index 7743e9069..390282c47 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImpl.java @@ -181,7 +181,7 @@ public class OAuth2ClientServiceImpl implements OAuth2ClientService { // 校验客户端存在、且开启 OAuth2ClientDO client = clientCache.get(clientId); if (client == null) { - throw exception(OAUTH2_CLIENT_EXISTS); + throw exception(OAUTH2_CLIENT_NOT_EXISTS); } if (ObjectUtil.notEqual(client.getStatus(), CommonStatusEnum.ENABLE.getStatus())) { throw exception(OAUTH2_CLIENT_DISABLE); diff --git a/yudao-ui-admin/src/api/login.js b/yudao-ui-admin/src/api/login.js index ef667fc6b..a76decf17 100644 --- a/yudao-ui-admin/src/api/login.js +++ b/yudao-ui-admin/src/api/login.js @@ -111,25 +111,37 @@ export function refreshToken() { } // ========== OAUTH 2.0 相关 ========== -export function authorize() { + +export function getAuthorize(clientId) { + return request({ + url: '/system/oauth2/authorize?clientId=' + clientId, + method: 'get' + }) +} + +export function authorize(responseType, clientId, redirectUri, state, + autoApprove, checkedScopes, uncheckedScopes) { + // 构建 scopes + const scopes = {}; + for (const scope of checkedScopes) { + scopes[scope] = true; + } + for (const scope of uncheckedScopes) { + scopes[scope] = false; + } + // 发起请求 return service({ url: '/system/oauth2/authorize', headers:{ 'Content-type': 'application/x-www-form-urlencoded', - "Access-Control-Allow-Origin": "*" }, params: { - response_type: 'code', - client_id: 'test', - redirect_uri: 'https://www.iocoder.cn', - // scopes: { - // read: true, - // write: false - // } - scope: { - read: true, - write: false - } + response_type: responseType, + client_id: clientId, + redirect_uri: redirectUri, + state: state, + auto_approve: autoApprove, + scope: JSON.stringify(scopes) }, method: 'post' }) diff --git a/yudao-ui-admin/src/router/index.js b/yudao-ui-admin/src/router/index.js index e6b3d6e01..7446a2f2b 100644 --- a/yudao-ui-admin/src/router/index.js +++ b/yudao-ui-admin/src/router/index.js @@ -43,8 +43,8 @@ export const constantRoutes = [ hidden: true }, { - path: '/authorize', - component: (resolve) => require(['@/views/authorize'], resolve), + path: '/sso', + component: (resolve) => require(['@/views/sso'], resolve), hidden: true }, { diff --git a/yudao-ui-admin/src/views/authorize.vue b/yudao-ui-admin/src/views/sso.vue similarity index 59% rename from yudao-ui-admin/src/views/authorize.vue rename to yudao-ui-admin/src/views/sso.vue index 37bba7aa2..8091ba49b 100644 --- a/yudao-ui-admin/src/views/authorize.vue +++ b/yudao-ui-admin/src/views/sso.vue @@ -14,7 +14,7 @@
- + @@ -53,7 +53,7 @@ 同意授权 登 录 中... - 拒绝 + 拒绝
@@ -69,10 +69,9 @@