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

This commit is contained in:
YunaiV 2022-05-15 22:23:28 +08:00
parent feff5aba07
commit 60bb8dd29c
13 changed files with 128 additions and 24 deletions

View File

@ -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 已过期");
}

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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);
}

View File

@ -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();
}
}

View File

@ -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);

View File

@ -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;

View File

@ -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;