完成 oauth2.0 的逻辑
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
package cn.iocoder.mall.systemservice.convert.oauth;
|
||||
|
||||
import cn.iocoder.mall.systemservice.dal.mysql.dataobject.oauth.OAuth2AccessTokenDO;
|
||||
import cn.iocoder.mall.systemservice.rpc.oauth.vo.OAuth2AccessTokenVO;
|
||||
import cn.iocoder.mall.systemservice.service.oauth.bo.OAuth2AccessTokenBO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper
|
||||
public interface OAuth2Convert {
|
||||
|
||||
OAuth2Convert INSTANCE = Mappers.getMapper(OAuth2Convert.class);
|
||||
|
||||
@Mapping(source = "id", target = "accessToken")
|
||||
OAuth2AccessTokenBO convert(OAuth2AccessTokenDO bean);
|
||||
|
||||
OAuth2AccessTokenVO convert(OAuth2AccessTokenBO bean);
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package cn.iocoder.mall.systemservice.convert;
|
||||
@@ -0,0 +1,56 @@
|
||||
package cn.iocoder.mall.systemservice.dal.mysql.dataobject.oauth;
|
||||
|
||||
import cn.iocoder.common.framework.enums.UserTypeEnum;
|
||||
import cn.iocoder.mall.mybatis.dataobject.BaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* OAuth2 访问令牌
|
||||
*
|
||||
* idx_userId 索引:对应 {@link #userId} 字段
|
||||
* idx_refreshToken 索引:对应 {@link #refreshToken} 字段
|
||||
*/
|
||||
@TableName("oauth2_access_token")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Accessors(chain = true)
|
||||
public class OAuth2AccessTokenDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 访问令牌
|
||||
*/
|
||||
@TableId(type = IdType.INPUT)
|
||||
private String id;
|
||||
/**
|
||||
* 用户编号
|
||||
*/
|
||||
private Integer userId;
|
||||
/**
|
||||
* 用户类型
|
||||
*
|
||||
* 枚举 {@link UserTypeEnum}
|
||||
*/
|
||||
private Integer userType;
|
||||
/**
|
||||
* 刷新令牌
|
||||
*
|
||||
* 关联 {@link OAuth2RefreshTokenDO#getId()}
|
||||
*/
|
||||
private String refreshToken;
|
||||
/**
|
||||
* 过期时间
|
||||
*/
|
||||
private Date expiresTime;
|
||||
/**
|
||||
* 创建 IP
|
||||
*/
|
||||
private String createIp;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package cn.iocoder.mall.systemservice.dal.mysql.dataobject.oauth;
|
||||
|
||||
import cn.iocoder.common.framework.enums.UserTypeEnum;
|
||||
import cn.iocoder.mall.mybatis.dataobject.DeletableDO;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* OAuth2 刷新令牌
|
||||
*
|
||||
* idx_userId 索引:对应 {@link #userId} 字段
|
||||
*/
|
||||
@TableName("oauth2_refresh_token")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Accessors(chain = true)
|
||||
public class OAuth2RefreshTokenDO extends DeletableDO {
|
||||
|
||||
/**
|
||||
* 刷新令牌
|
||||
*/
|
||||
@TableId(type = IdType.INPUT)
|
||||
private String id;
|
||||
/**
|
||||
* 用户编号
|
||||
*/
|
||||
private Integer userId;
|
||||
/**
|
||||
* 用户类型
|
||||
*
|
||||
* 枚举 {@link UserTypeEnum}
|
||||
*/
|
||||
private Integer userType;
|
||||
/**
|
||||
* 过期时间
|
||||
*/
|
||||
private Date expiresTime;
|
||||
/**
|
||||
* 创建 IP
|
||||
*/
|
||||
private String createIp;
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package cn.iocoder.mall.systemservice.dal.mysql.dataobject;
|
||||
@@ -0,0 +1,20 @@
|
||||
package cn.iocoder.mall.systemservice.dal.mysql.mapper.oauth;
|
||||
|
||||
import cn.iocoder.mall.systemservice.dal.mysql.dataobject.oauth.OAuth2AccessTokenDO;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface OAuth2AccessTokenMapper extends BaseMapper<OAuth2AccessTokenDO> {
|
||||
|
||||
default int deleteByUserIdAndUserType(Integer userId, Integer userType) {
|
||||
return delete(new QueryWrapper<OAuth2AccessTokenDO>()
|
||||
.eq("user_id", userId).eq("user_type", userType));
|
||||
}
|
||||
|
||||
default int deleteByRefreshToken(String refreshToken) {
|
||||
return delete(new QueryWrapper<OAuth2AccessTokenDO>().eq("refresh_token", refreshToken));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package cn.iocoder.mall.systemservice.dal.mysql.mapper.oauth;
|
||||
|
||||
import cn.iocoder.mall.systemservice.dal.mysql.dataobject.oauth.OAuth2RefreshTokenDO;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface OAuth2RefreshTokenMapper extends BaseMapper<OAuth2RefreshTokenDO> {
|
||||
|
||||
default int deleteByUserIdAndUserType(Integer userId, Integer userType) {
|
||||
return delete(new QueryWrapper<OAuth2RefreshTokenDO>()
|
||||
.eq("user_id", userId).eq("user_type", userType));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package cn.iocoder.mall.systemservice.dal.mysql.mapper;
|
||||
@@ -0,0 +1,38 @@
|
||||
package cn.iocoder.mall.systemservice.manager.oauth;
|
||||
|
||||
import cn.iocoder.mall.systemservice.convert.oauth.OAuth2Convert;
|
||||
import cn.iocoder.mall.systemservice.rpc.oauth.dto.OAuth2CreateAccessTokenDTO;
|
||||
import cn.iocoder.mall.systemservice.rpc.oauth.dto.OAuth2RefreshAccessTokenDTO;
|
||||
import cn.iocoder.mall.systemservice.rpc.oauth.vo.OAuth2AccessTokenVO;
|
||||
import cn.iocoder.mall.systemservice.service.oauth.OAuth2Service;
|
||||
import cn.iocoder.mall.systemservice.service.oauth.bo.OAuth2AccessTokenBO;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* OAuth2.0 Manager
|
||||
*/
|
||||
@Service
|
||||
public class OAuth2Manager {
|
||||
|
||||
@Autowired
|
||||
private OAuth2Service oauth2Service;
|
||||
|
||||
public OAuth2AccessTokenVO createAccessToken(OAuth2CreateAccessTokenDTO createAccessTokenDTO) {
|
||||
OAuth2AccessTokenBO accessTokenBO = oauth2Service.createAccessToken(createAccessTokenDTO.getUserId(),
|
||||
createAccessTokenDTO.getUserType(), createAccessTokenDTO.getCreateIp());
|
||||
return OAuth2Convert.INSTANCE.convert(accessTokenBO);
|
||||
}
|
||||
|
||||
public OAuth2AccessTokenVO checkAccessToken(String accessToken) {
|
||||
OAuth2AccessTokenBO accessTokenBO = oauth2Service.checkAccessToken(accessToken);
|
||||
return OAuth2Convert.INSTANCE.convert(accessTokenBO);
|
||||
}
|
||||
|
||||
public OAuth2AccessTokenVO refreshAccessToken(OAuth2RefreshAccessTokenDTO refreshAccessTokenDTO) {
|
||||
OAuth2AccessTokenBO accessTokenBO = oauth2Service.refreshAccessToken(refreshAccessTokenDTO.getRefreshToken(),
|
||||
refreshAccessTokenDTO.getCreateIp());
|
||||
return OAuth2Convert.INSTANCE.convert(accessTokenBO);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package cn.iocoder.mall.systemservice.manager;
|
||||
@@ -1,7 +1,34 @@
|
||||
package cn.iocoder.mall.systemservice.rpc.oauth;
|
||||
|
||||
import cn.iocoder.common.framework.vo.CommonResult;
|
||||
import cn.iocoder.mall.systemservice.manager.oauth.OAuth2Manager;
|
||||
import cn.iocoder.mall.systemservice.rpc.oauth.dto.OAuth2CreateAccessTokenDTO;
|
||||
import cn.iocoder.mall.systemservice.rpc.oauth.dto.OAuth2RefreshAccessTokenDTO;
|
||||
import cn.iocoder.mall.systemservice.rpc.oauth.vo.OAuth2AccessTokenVO;
|
||||
import org.apache.dubbo.config.annotation.Service;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import static cn.iocoder.common.framework.vo.CommonResult.success;
|
||||
|
||||
@Service(version = "${dubbo.provider.OAuth2Rpc.version}", validation = "false")
|
||||
public class OAuth2RpcImpl implements OAuth2Rpc {
|
||||
|
||||
@Autowired
|
||||
private OAuth2Manager oauth2Manager;
|
||||
|
||||
@Override
|
||||
public CommonResult<OAuth2AccessTokenVO> createAccessToken(OAuth2CreateAccessTokenDTO createAccessTokenDTO) {
|
||||
return success(oauth2Manager.createAccessToken(createAccessTokenDTO));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<OAuth2AccessTokenVO> checkAccessToken(String accessToken) {
|
||||
return success(oauth2Manager.checkAccessToken(accessToken));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<OAuth2AccessTokenVO> refreshAccessToken(OAuth2RefreshAccessTokenDTO refreshAccessTokenDTO) {
|
||||
return success(oauth2Manager.refreshAccessToken(refreshAccessTokenDTO));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
package cn.iocoder.mall.systemservice.service.oauth;
|
||||
|
||||
import cn.iocoder.common.framework.util.ServiceExceptionUtil;
|
||||
import cn.iocoder.common.framework.util.StringUtils;
|
||||
import cn.iocoder.mall.systemservice.convert.oauth.OAuth2Convert;
|
||||
import cn.iocoder.mall.systemservice.dal.mysql.dataobject.oauth.OAuth2AccessTokenDO;
|
||||
import cn.iocoder.mall.systemservice.dal.mysql.dataobject.oauth.OAuth2RefreshTokenDO;
|
||||
import cn.iocoder.mall.systemservice.dal.mysql.mapper.oauth.OAuth2AccessTokenMapper;
|
||||
import cn.iocoder.mall.systemservice.dal.mysql.mapper.oauth.OAuth2RefreshTokenMapper;
|
||||
import cn.iocoder.mall.systemservice.service.oauth.bo.OAuth2AccessTokenBO;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import static cn.iocoder.mall.systemservice.enums.SystemErrorCodeEnum.*;
|
||||
|
||||
/**
|
||||
* OAuth2.0 Service
|
||||
*/
|
||||
@Service
|
||||
public class OAuth2Service {
|
||||
|
||||
/**
|
||||
* 访问令牌过期时间,单位:毫秒
|
||||
*/
|
||||
@Value("${modules.oauth2-service.access-token-expire-time-millis}")
|
||||
private int accessTokenExpireTimeMillis;
|
||||
/**
|
||||
* 刷新令牌过期时间,单位:毫秒
|
||||
*/
|
||||
@Value("${modules.oauth2-service.refresh-token-expire-time-millis}")
|
||||
private int refreshTokenExpireTimeMillis;
|
||||
|
||||
@Autowired
|
||||
private OAuth2AccessTokenMapper oauth2AccessTokenMapper;
|
||||
@Autowired
|
||||
private OAuth2RefreshTokenMapper oauth2RefreshTokenMapper;
|
||||
|
||||
@Transactional
|
||||
public OAuth2AccessTokenBO createAccessToken(Integer userId, Integer userType, String createIp) {
|
||||
// 创建刷新令牌 + 访问令牌
|
||||
OAuth2RefreshTokenDO refreshTokenDO = createOAuth2RefreshToken(userId, userType, createIp);
|
||||
OAuth2AccessTokenDO accessTokenDO = createOAuth2AccessToken(refreshTokenDO, createIp);
|
||||
// 返回访问令牌
|
||||
return OAuth2Convert.INSTANCE.convert(accessTokenDO);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public OAuth2AccessTokenBO checkAccessToken(String accessToken) {
|
||||
OAuth2AccessTokenDO accessTokenDO = oauth2AccessTokenMapper.selectById(accessToken);
|
||||
if (accessTokenDO == null) { // 不存在
|
||||
throw ServiceExceptionUtil.exception(OAUTH2_ACCESS_TOKEN_NOT_FOUND);
|
||||
}
|
||||
if (accessTokenDO.getExpiresTime().getTime() < System.currentTimeMillis()) { // 已过期
|
||||
throw ServiceExceptionUtil.exception(OAUTH2_ACCESS_TOKEN_TOKEN_EXPIRED);
|
||||
}
|
||||
// 返回访问令牌
|
||||
return OAuth2Convert.INSTANCE.convert(accessTokenDO);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public OAuth2AccessTokenBO refreshAccessToken(String refreshToken, String createIp) {
|
||||
OAuth2RefreshTokenDO refreshTokenDO = oauth2RefreshTokenMapper.selectById(refreshToken);
|
||||
// 校验刷新令牌是否合法
|
||||
if (refreshTokenDO == null) { // 不存在
|
||||
throw ServiceExceptionUtil.exception(OAUTH2_REFRESH_TOKEN_NOT_FOUND);
|
||||
}
|
||||
if (refreshTokenDO.getExpiresTime().getTime() < System.currentTimeMillis()) { // 已过期
|
||||
throw ServiceExceptionUtil.exception(OAUTH_REFRESH_TOKEN_EXPIRED);
|
||||
}
|
||||
// 标记 refreshToken 对应的 accessToken 都不合法
|
||||
// 这块的实现,参考了 Spring Security OAuth2 的代码
|
||||
oauth2AccessTokenMapper.deleteByRefreshToken(refreshToken);
|
||||
// 创建访问令牌
|
||||
OAuth2AccessTokenDO oauth2AccessTokenDO = createOAuth2AccessToken(refreshTokenDO, createIp);
|
||||
// 返回访问令牌
|
||||
return OAuth2Convert.INSTANCE.convert(oauth2AccessTokenDO);
|
||||
}
|
||||
|
||||
private OAuth2AccessTokenDO createOAuth2AccessToken(OAuth2RefreshTokenDO refreshTokenDO, String createIp) {
|
||||
OAuth2AccessTokenDO accessToken = new OAuth2AccessTokenDO()
|
||||
.setId(generateAccessToken())
|
||||
.setUserId(refreshTokenDO.getUserId()).setUserType(refreshTokenDO.getUserType())
|
||||
.setRefreshToken(refreshTokenDO.getId())
|
||||
.setExpiresTime(new Date(System.currentTimeMillis() + accessTokenExpireTimeMillis))
|
||||
.setCreateIp(createIp);
|
||||
oauth2AccessTokenMapper.insert(accessToken);
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
private OAuth2RefreshTokenDO createOAuth2RefreshToken(Integer userId, Integer userType, String createIp) {
|
||||
OAuth2RefreshTokenDO refreshToken = new OAuth2RefreshTokenDO()
|
||||
.setId(generateRefreshToken())
|
||||
.setUserId(userId).setUserType(userType)
|
||||
.setExpiresTime(new Date(System.currentTimeMillis() + refreshTokenExpireTimeMillis))
|
||||
.setCreateIp(createIp);
|
||||
oauth2RefreshTokenMapper.insert(refreshToken);
|
||||
return refreshToken;
|
||||
}
|
||||
|
||||
private String generateAccessToken() {
|
||||
return StringUtils.uuid(true);
|
||||
}
|
||||
|
||||
private String generateRefreshToken() {
|
||||
return StringUtils.uuid(true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package cn.iocoder.mall.systemservice.service.oauth.bo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* OAuth2.0 访问令牌 BO
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class OAuth2AccessTokenBO {
|
||||
|
||||
/**
|
||||
* 访问令牌
|
||||
*/
|
||||
private String accessToken;
|
||||
/**
|
||||
* 刷新令牌
|
||||
*/
|
||||
private String refreshToken;
|
||||
/**
|
||||
* 用户编号
|
||||
*/
|
||||
private Integer userId;
|
||||
/**
|
||||
* 用户类型
|
||||
*/
|
||||
private Integer userType;
|
||||
/**
|
||||
* 过期时间
|
||||
*/
|
||||
private Date expiresTime;
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package cn.iocoder.mall.systemservice.service;
|
||||
Reference in New Issue
Block a user