Commit 60bb8dd29ca970cfed61765812ff372e313ad080

Authored by YunaiV
1 parent feff5aba

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

Showing 13 changed files with 128 additions and 24 deletions
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 { @@ -133,9 +133,13 @@ public interface ErrorCodeConstants {
133 ErrorCode OAUTH2_CLIENT_CLIENT_SECRET_ERROR = new ErrorCode(1002020006, "无效 client_secret: {}"); 133 ErrorCode OAUTH2_CLIENT_CLIENT_SECRET_ERROR = new ErrorCode(1002020006, "无效 client_secret: {}");
134 134
135 // ========== OAuth2 授权 1002021000 ========= 135 // ========== OAuth2 授权 1002021000 =========
136 - ErrorCode OAUTH2_GRANT_CLIENT_ID_MISMATCH = new ErrorCode(1002020000, "client_id 不匹配");  
137 - ErrorCode OAUTH2_GRANT_REDIRECT_URI_MISMATCH = new ErrorCode(1002020001, "redirect_uri 不匹配");  
138 - ErrorCode OAUTH2_GRANT_STATE_MISMATCH = new ErrorCode(1002020002, "state 不匹配");  
139 - ErrorCode OAUTH2_GRANT_CODE_NOT_EXISTS = new ErrorCode(1002020003, "code 不存在"); 136 + ErrorCode OAUTH2_GRANT_CLIENT_ID_MISMATCH = new ErrorCode(1002021000, "client_id 不匹配");
  137 + ErrorCode OAUTH2_GRANT_REDIRECT_URI_MISMATCH = new ErrorCode(1002021001, "redirect_uri 不匹配");
  138 + ErrorCode OAUTH2_GRANT_STATE_MISMATCH = new ErrorCode(1002021002, "state 不匹配");
  139 + ErrorCode OAUTH2_GRANT_CODE_NOT_EXISTS = new ErrorCode(1002021003, "code 不存在");
  140 +
  141 + // ========== OAuth2 授权 1002022000 =========
  142 + ErrorCode OAUTH2_CODE_NOT_EXISTS = new ErrorCode(1002022000, "code 不存在");
  143 + ErrorCode OAUTH2_CODE_EXPIRE = new ErrorCode(1002022000, "code 已过期");
140 144
141 } 145 }
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 @@ -12,7 +12,7 @@ Content-Type: application/x-www-form-urlencoded
12 Authorization: Bearer {{token}} 12 Authorization: Bearer {{token}}
13 tenant-id: {{adminTenentId}} 13 tenant-id: {{adminTenentId}}
14 14
15 -response_type=code&client_id=default&scope={"user_info": true}&redirect_uri=https://www.iocoder.cn&auto_approve=true 15 +response_type=code&client_id=default&scope={"user.read": true}&redirect_uri=https://www.iocoder.cn&auto_approve=true
16 16
17 ### 请求 /system/oauth2/token + code 接口 => 成功 17 ### 请求 /system/oauth2/token + code 接口 => 成功
18 POST {{baseUrl}}/system/oauth2/token 18 POST {{baseUrl}}/system/oauth2/token
@@ -20,7 +20,7 @@ Content-Type: application/x-www-form-urlencoded @@ -20,7 +20,7 @@ Content-Type: application/x-www-form-urlencoded
20 Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw== 20 Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw==
21 tenant-id: {{adminTenentId}} 21 tenant-id: {{adminTenentId}}
22 22
23 -grant_type=authorization_code&redirect_uri=https://www.iocoder.cn 23 +grant_type=authorization_code&redirect_uri=https://www.iocoder.cn&code=189956c07a174588a97157eabef2f93a
24 24
25 ### 请求 /system/oauth2/token + password 接口 => 成功 25 ### 请求 /system/oauth2/token + password 接口 => 成功
26 POST {{baseUrl}}/system/oauth2/token 26 POST {{baseUrl}}/system/oauth2/token
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2AccessTokenMapper.java renamed to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2AccessTokenMapper.java
1 -package cn.iocoder.yudao.module.system.dal.mysql.auth; 1 +package cn.iocoder.yudao.module.system.dal.mysql.oauth2;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2ClientMapper.java renamed to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2ClientMapper.java
1 -package cn.iocoder.yudao.module.system.dal.mysql.auth; 1 +package cn.iocoder.yudao.module.system.dal.mysql.oauth2;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2CodeMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.dal.mysql.oauth2;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  4 +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2CodeDO;
  5 +import org.apache.ibatis.annotations.Mapper;
  6 +
  7 +@Mapper
  8 +public interface OAuth2CodeMapper extends BaseMapperX<OAuth2CodeDO> {
  9 +
  10 + default OAuth2CodeDO selectByCode(String code) {
  11 + return selectOne(OAuth2CodeDO::getCode, code);
  12 + }
  13 +
  14 +}
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2RefreshTokenMapper.java renamed to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2RefreshTokenMapper.java
1 -package cn.iocoder.yudao.module.system.dal.mysql.auth; 1 +package cn.iocoder.yudao.module.system.dal.mysql.oauth2;
2 2
3 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 3 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
4 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 4 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/auth/OAuth2AccessTokenRedisDAO.java renamed to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/oauth2/OAuth2AccessTokenRedisDAO.java
1 -package cn.iocoder.yudao.module.system.dal.redis.auth; 1 +package cn.iocoder.yudao.module.system.dal.redis.oauth2;
2 2
3 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 3 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
4 import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 4 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
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 @@ -11,7 +11,7 @@ import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2Cl
11 import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientUpdateReqVO; 11 import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientUpdateReqVO;
12 import cn.iocoder.yudao.module.system.convert.auth.OAuth2ClientConvert; 12 import cn.iocoder.yudao.module.system.convert.auth.OAuth2ClientConvert;
13 import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO; 13 import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO;
14 -import cn.iocoder.yudao.module.system.dal.mysql.auth.OAuth2ClientMapper; 14 +import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2ClientMapper;
15 import cn.iocoder.yudao.module.system.mq.producer.auth.OAuth2ClientProducer; 15 import cn.iocoder.yudao.module.system.mq.producer.auth.OAuth2ClientProducer;
16 import com.google.common.annotations.VisibleForTesting; 16 import com.google.common.annotations.VisibleForTesting;
17 import lombok.Getter; 17 import lombok.Getter;
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeService.java
1 package cn.iocoder.yudao.module.system.service.oauth2; 1 package cn.iocoder.yudao.module.system.service.oauth2;
2 2
  3 +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2CodeDO;
  4 +
  5 +import java.util.List;
  6 +
3 /** 7 /**
4 * OAuth2.0 授权码 Service 接口 8 * OAuth2.0 授权码 Service 接口
5 * 9 *
@@ -9,6 +13,27 @@ package cn.iocoder.yudao.module.system.service.oauth2; @@ -9,6 +13,27 @@ package cn.iocoder.yudao.module.system.service.oauth2;
9 */ 13 */
10 public interface OAuth2CodeService { 14 public interface OAuth2CodeService {
11 15
  16 + /**
  17 + * 创建授权码
  18 + *
  19 + * 参考 JdbcAuthorizationCodeServices 的 createAuthorizationCode 方法
  20 + *
  21 + * @param userId 用户编号
  22 + * @param userType 用户类型
  23 + * @param clientId 客户端编号
  24 + * @param scopes 授权范围
  25 + * @param redirectUri 重定向 URI
  26 + * @param state 状态
  27 + * @return 授权码的信息
  28 + */
  29 + OAuth2CodeDO createAuthorizationCode(Long userId, Integer userType, String clientId, List<String> scopes,
  30 + String redirectUri, String state);
12 31
  32 + /**
  33 + * 使用授权码
  34 + *
  35 + * @param code 授权码
  36 + */
  37 + OAuth2CodeDO consumeAuthorizationCode(String code);
13 38
14 } 39 }
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.service.oauth2;
  2 +
  3 +import cn.hutool.core.util.IdUtil;
  4 +import cn.iocoder.yudao.framework.common.util.date.DateUtils;
  5 +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2CodeDO;
  6 +import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2CodeMapper;
  7 +import org.springframework.stereotype.Service;
  8 +import org.springframework.validation.annotation.Validated;
  9 +
  10 +import javax.annotation.Resource;
  11 +import java.util.Calendar;
  12 +import java.util.List;
  13 +
  14 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  15 +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.OAUTH2_CODE_EXPIRE;
  16 +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.OAUTH2_CODE_NOT_EXISTS;
  17 +
  18 +/**
  19 + * OAuth2.0 授权码 Service 实现类
  20 + *
  21 + * @author 芋道源码
  22 + */
  23 +@Service
  24 +@Validated
  25 +public class OAuth2CodeServiceImpl implements OAuth2CodeService {
  26 +
  27 + /**
  28 + * 授权码的过期时间,默认 5 分钟
  29 + */
  30 + private static final Integer TIMEOUT = 5 * 60;
  31 +
  32 + @Resource
  33 + private OAuth2CodeMapper oauth2CodeMapper;
  34 +
  35 + @Override
  36 + public OAuth2CodeDO createAuthorizationCode(Long userId, Integer userType, String clientId, List<String> scopes,
  37 + String redirectUri, String state) {
  38 + OAuth2CodeDO codeDO = new OAuth2CodeDO().setCode(generateCode())
  39 + .setUserId(userId).setUserType(userType)
  40 + .setClientId(clientId).setScopes(scopes)
  41 + .setExpiresTime(DateUtils.addDate(Calendar.SECOND, TIMEOUT))
  42 + .setRedirectUri(redirectUri).setState(state);
  43 + oauth2CodeMapper.insert(codeDO);
  44 + return codeDO;
  45 + }
  46 +
  47 + @Override
  48 + public OAuth2CodeDO consumeAuthorizationCode(String code) {
  49 + OAuth2CodeDO codeDO = oauth2CodeMapper.selectByCode(code);
  50 + if (codeDO == null) {
  51 + throw exception(OAUTH2_CODE_NOT_EXISTS);
  52 + }
  53 + if (DateUtils.isExpired(codeDO.getExpiresTime())) {
  54 + throw exception(OAUTH2_CODE_EXPIRE);
  55 + }
  56 + oauth2CodeMapper.deleteById(codeDO.getId());
  57 + return codeDO;
  58 + }
  59 +
  60 + private static String generateCode() {
  61 + return IdUtil.fastSimpleUUID();
  62 + }
  63 +
  64 +}
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; @@ -15,8 +15,6 @@ import javax.annotation.Resource;
15 import java.util.List; 15 import java.util.List;
16 16
17 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 17 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
18 -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.OAUTH2_GRANT_CODE_NOT_EXISTS;  
19 -import static java.util.Collections.singletonList;  
20 18
21 /** 19 /**
22 * OAuth2 授予 Service 实现类 20 * OAuth2 授予 Service 实现类
@@ -29,6 +27,8 @@ public class OAuth2GrantServiceImpl implements OAuth2GrantService { @@ -29,6 +27,8 @@ public class OAuth2GrantServiceImpl implements OAuth2GrantService {
29 @Resource 27 @Resource
30 private OAuth2TokenService oauth2TokenService; 28 private OAuth2TokenService oauth2TokenService;
31 @Resource 29 @Resource
  30 + private OAuth2CodeService oauth2CodeService;
  31 + @Resource
32 private AdminAuthService adminAuthService; 32 private AdminAuthService adminAuthService;
33 33
34 @Override 34 @Override
@@ -41,18 +41,15 @@ public class OAuth2GrantServiceImpl implements OAuth2GrantService { @@ -41,18 +41,15 @@ public class OAuth2GrantServiceImpl implements OAuth2GrantService {
41 public String grantAuthorizationCodeForCode(Long userId, Integer userType, 41 public String grantAuthorizationCodeForCode(Long userId, Integer userType,
42 String clientId, List<String> scopes, 42 String clientId, List<String> scopes,
43 String redirectUri, String state) { 43 String redirectUri, String state) {
44 - return "test"; 44 + return oauth2CodeService.createAuthorizationCode(userId, userType, clientId, scopes,
  45 + redirectUri, state).getCode();
45 } 46 }
46 47
47 @Override 48 @Override
48 public OAuth2AccessTokenDO grantAuthorizationCodeForAccessToken(String clientId, String code, 49 public OAuth2AccessTokenDO grantAuthorizationCodeForAccessToken(String clientId, String code,
49 String redirectUri, String state) { 50 String redirectUri, String state) {
50 - // TODO 消费 code  
51 - OAuth2CodeDO codeDO = new OAuth2CodeDO().setClientId("default").setRedirectUri("https://www.iocoder.cn").setState("")  
52 - .setUserId(1L).setUserType(2).setScopes(singletonList("user_info"));  
53 - if (codeDO == null) {  
54 - throw exception(OAUTH2_GRANT_CODE_NOT_EXISTS);  
55 - } 51 + OAuth2CodeDO codeDO = oauth2CodeService.consumeAuthorizationCode(code);
  52 + Assert.notNull(codeDO, "授权码不能为空"); // 防御性编程
56 // 校验 clientId 是否匹配 53 // 校验 clientId 是否匹配
57 if (!StrUtil.equals(clientId, codeDO.getClientId())) { 54 if (!StrUtil.equals(clientId, codeDO.getClientId())) {
58 throw exception(ErrorCodeConstants.OAUTH2_GRANT_CLIENT_ID_MISMATCH); 55 throw exception(ErrorCodeConstants.OAUTH2_GRANT_CLIENT_ID_MISMATCH);
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 @@ -11,9 +11,9 @@ import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token.OAuth2Acc
11 import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; 11 import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
12 import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO; 12 import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO;
13 import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2RefreshTokenDO; 13 import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2RefreshTokenDO;
14 -import cn.iocoder.yudao.module.system.dal.mysql.auth.OAuth2AccessTokenMapper;  
15 -import cn.iocoder.yudao.module.system.dal.mysql.auth.OAuth2RefreshTokenMapper;  
16 -import cn.iocoder.yudao.module.system.dal.redis.auth.OAuth2AccessTokenRedisDAO; 14 +import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2AccessTokenMapper;
  15 +import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2RefreshTokenMapper;
  16 +import cn.iocoder.yudao.module.system.dal.redis.oauth2.OAuth2AccessTokenRedisDAO;
17 import org.springframework.stereotype.Service; 17 import org.springframework.stereotype.Service;
18 import org.springframework.transaction.annotation.Transactional; 18 import org.springframework.transaction.annotation.Transactional;
19 19
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 @@ -7,7 +7,7 @@ import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2Cl
7 import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientPageReqVO; 7 import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientPageReqVO;
8 import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientUpdateReqVO; 8 import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientUpdateReqVO;
9 import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO; 9 import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO;
10 -import cn.iocoder.yudao.module.system.dal.mysql.auth.OAuth2ClientMapper; 10 +import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2ClientMapper;
11 import cn.iocoder.yudao.module.system.mq.producer.auth.OAuth2ClientProducer; 11 import cn.iocoder.yudao.module.system.mq.producer.auth.OAuth2ClientProducer;
12 import cn.iocoder.yudao.module.system.service.oauth2.OAuth2ClientServiceImpl; 12 import cn.iocoder.yudao.module.system.service.oauth2.OAuth2ClientServiceImpl;
13 import org.junit.jupiter.api.Disabled; 13 import org.junit.jupiter.api.Disabled;