Преглед изворни кода

增加 oauth2 的 code 的生成与消费的逻辑

YunaiV пре 3 година
родитељ
комит
60bb8dd29c
13 измењених фајлова са 128 додато и 24 уклоњено
  1. 8 4
      yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java
  2. 2 2
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.http
  3. 1 1
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2AccessTokenMapper.java
  4. 1 1
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2ClientMapper.java
  5. 14 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2CodeMapper.java
  6. 1 1
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2RefreshTokenMapper.java
  7. 1 1
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/oauth2/OAuth2AccessTokenRedisDAO.java
  8. 1 1
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImpl.java
  9. 25 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeService.java
  10. 64 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeServiceImpl.java
  11. 6 9
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImpl.java
  12. 3 3
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImpl.java
  13. 1 1
      yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ClientServiceImplTest.java

+ 8 - 4
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 已过期");
 
 }

+ 2 - 2
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

+ 1 - 1
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2AccessTokenMapper.java → 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;

+ 1 - 1
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2ClientMapper.java → 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;

+ 14 - 0
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<OAuth2CodeDO> {
+
+    default OAuth2CodeDO selectByCode(String code) {
+        return selectOne(OAuth2CodeDO::getCode, code);
+    }
+
+}

+ 1 - 1
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2RefreshTokenMapper.java → 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;

+ 1 - 1
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/auth/OAuth2AccessTokenRedisDAO.java → 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;

+ 1 - 1
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;

+ 25 - 0
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<String> scopes,
+                                         String redirectUri, String state);
 
+    /**
+     * 使用授权码
+     *
+     * @param code 授权码
+     */
+    OAuth2CodeDO consumeAuthorizationCode(String code);
 
 }

+ 64 - 0
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<String> 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();
+    }
+
+}

+ 6 - 9
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<String> 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);

+ 3 - 3
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;
 

+ 1 - 1
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;