实现管理后台登录时,使用 OAuth2 的 access token
parent
ebee4ddb7c
commit
4f52d1367b
@ -1,12 +0,0 @@
|
||||
package cn.iocoder.yudao.module.system.api.auth;
|
||||
|
||||
/**
|
||||
* OAuth2.0 API 接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface OAuth2Api {
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package cn.iocoder.yudao.module.system.api.auth;
|
||||
|
||||
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 javax.validation.Valid;
|
||||
|
||||
/**
|
||||
* OAuth2.0 Token API 接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface OAuth2TokenApi {
|
||||
|
||||
OAuth2AccessTokenRespDTO createAccessToken(@Valid OAuth2AccessTokenCreateReqDTO reqDTO);
|
||||
|
||||
OAuth2AccessTokenCheckRespDTO checkAccessToken(String accessToken);
|
||||
|
||||
// void removeToken(OAuth2RemoveTokenByUserReqDTO removeTokenDTO);
|
||||
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
package cn.iocoder.yudao.module.system.api.auth.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* OAuth2.0 访问令牌的校验 Response DTO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
public class OAuth2AccessTokenCheckRespDTO implements Serializable {
|
||||
|
||||
/**
|
||||
* 用户编号
|
||||
*/
|
||||
private Long userId;
|
||||
/**
|
||||
* 用户类型
|
||||
*/
|
||||
private Integer userType;
|
||||
/**
|
||||
* 租户编号
|
||||
*/
|
||||
private Long tenantId;
|
||||
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
package cn.iocoder.yudao.module.system.api.auth.dto;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* OAuth2.0 访问令牌创建 Request DTO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class OAuth2AccessTokenCreateReqDTO implements Serializable {
|
||||
|
||||
/**
|
||||
* 用户编号
|
||||
*/
|
||||
@NotNull(message = "用户编号不能为空")
|
||||
private Integer userId;
|
||||
/**
|
||||
* 用户类型
|
||||
*/
|
||||
@NotNull(message = "用户类型不能为空")
|
||||
@InEnum(value = UserTypeEnum.class, message = "用户类型必须是 {value}")
|
||||
private Integer userType;
|
||||
/**
|
||||
* 客户端编号
|
||||
*/
|
||||
@NotNull(message = "客户端编号不能为空")
|
||||
private Long clientId;
|
||||
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
package cn.iocoder.yudao.module.system.api.auth.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* OAuth2.0 访问令牌的信息 Response DTO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class OAuth2AccessTokenRespDTO implements Serializable {
|
||||
|
||||
/**
|
||||
* 访问令牌
|
||||
*/
|
||||
private String accessToken;
|
||||
/**
|
||||
* 刷新令牌
|
||||
*/
|
||||
private String refreshToken;
|
||||
/**
|
||||
* 用户编号
|
||||
*/
|
||||
private Integer userId;
|
||||
/**
|
||||
* 用户类型
|
||||
*/
|
||||
private Integer userType;
|
||||
/**
|
||||
* 过期时间
|
||||
*/
|
||||
private Date expiresTime;
|
||||
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
package cn.iocoder.yudao.module.system.api.auth;
|
||||
|
||||
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.UserSessionConvert;
|
||||
import cn.iocoder.yudao.module.system.service.auth.OAuth2TokenService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* OAuth2.0 Token API 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
public class OAuth2TokenApiImpl implements OAuth2TokenApi {
|
||||
|
||||
@Resource
|
||||
private OAuth2TokenService oauth2TokenService;
|
||||
|
||||
@Override
|
||||
public OAuth2AccessTokenRespDTO createAccessToken(OAuth2AccessTokenCreateReqDTO reqDTO) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2AccessTokenCheckRespDTO checkAccessToken(String accessToken) {
|
||||
return UserSessionConvert.INSTANCE.convert(oauth2TokenService.checkAccessToken(accessToken));
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,47 +0,0 @@
|
||||
package cn.iocoder.yudao.module.system.api.auth;
|
||||
|
||||
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
||||
import cn.iocoder.yudao.module.system.service.auth.UserSessionService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 在线用户 Session API 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class UserSessionApiImpl implements UserSessionApi {
|
||||
|
||||
@Resource
|
||||
private UserSessionService userSessionService;
|
||||
|
||||
@Override
|
||||
public String createUserSession(LoginUser loginUser, String userIp, String userAgent) {
|
||||
return userSessionService.createUserSession(loginUser, userIp, userAgent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshUserSession(String token, LoginUser loginUser) {
|
||||
userSessionService.refreshUserSession(token, loginUser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteUserSession(String token) {
|
||||
userSessionService.deleteUserSession(token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginUser getLoginUser(String token) {
|
||||
return userSessionService.getLoginUser(token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getSessionTimeoutMillis() {
|
||||
return userSessionService.getSessionTimeoutMillis();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
package cn.iocoder.yudao.module.system.dal.mysql.auth;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface OAuth2AccessTokenMapper extends BaseMapperX<OAuth2AccessTokenDO> {
|
||||
|
||||
default OAuth2AccessTokenDO selectByAccessToken(String accessToken) {
|
||||
return selectOne(OAuth2AccessTokenDO::getAccessToken, accessToken);
|
||||
}
|
||||
|
||||
// default OAuth2AccessTokenDO selectByUserIdAndUserType(Integer userId, Integer userType) {
|
||||
// return selectOne(new QueryWrapper<OAuth2AccessTokenDO>()
|
||||
// .eq("user_id", userId).eq("user_type", userType));
|
||||
// }
|
||||
//
|
||||
// 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));
|
||||
// }
|
||||
//
|
||||
// default List<OAuth2AccessTokenDO> selectListByRefreshToken(String refreshToken) {
|
||||
// return selectList(new QueryWrapper<OAuth2AccessTokenDO>().eq("refresh_token", refreshToken));
|
||||
// }
|
||||
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package cn.iocoder.yudao.module.system.dal.mysql.auth;
|
||||
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2RefreshTokenDO;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
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,46 @@
|
||||
package cn.iocoder.yudao.module.system.dal.redis.auth;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants.OAUTH2_ACCESS_TOKEN;
|
||||
|
||||
/**
|
||||
* {@link OAuth2AccessTokenDO} 的 RedisDAO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Repository
|
||||
public class OAuth2AccessTokenRedisDAO {
|
||||
|
||||
@Resource
|
||||
private StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
public OAuth2AccessTokenDO get(String accessToken) {
|
||||
String redisKey = formatKey(accessToken);
|
||||
return JsonUtils.parseObject(stringRedisTemplate.opsForValue().get(redisKey), OAuth2AccessTokenDO.class);
|
||||
}
|
||||
|
||||
public void set(OAuth2AccessTokenDO accessTokenDO) {
|
||||
String redisKey = formatKey(accessTokenDO.getAccessToken());
|
||||
// 清理多余字段,避免缓存
|
||||
accessTokenDO.setUpdater(null).setUpdateTime(null).setCreateTime(null).setCreator(null).setDeleted(null);
|
||||
stringRedisTemplate.opsForValue().set(redisKey, JsonUtils.toJsonString(accessTokenDO),
|
||||
accessTokenDO.getExpiresTime().getTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public void delete(String accessToken) {
|
||||
String redisKey = formatKey(accessToken);
|
||||
stringRedisTemplate.delete(redisKey);
|
||||
}
|
||||
|
||||
private static String formatKey(String accessToken) {
|
||||
return String.format(OAUTH2_ACCESS_TOKEN.getKeyTemplate(), accessToken);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package cn.iocoder.yudao.module.system.service.auth;
|
||||
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* OAuth2.0 Client Service 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
public class OAuth2ClientServiceImpl implements OAuth2ClientService {
|
||||
|
||||
@Override
|
||||
public OAuth2ClientDO validOAuthClientFromCache(Long id) {
|
||||
return new OAuth2ClientDO().setId(id)
|
||||
.setAccessTokenValiditySeconds(60 * 30)
|
||||
.setRefreshTokenValiditySeconds(60 * 60 * 24 * 30);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,145 +0,0 @@
|
||||
package cn.iocoder.yudao.module.system.service.auth;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* OAuth2.0 Service 实现类
|
||||
*
|
||||
*
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
public class OAuth2ServiceImpl implements OAuth2TokenService {
|
||||
|
||||
// @Autowired
|
||||
// private SystemBizProperties systemBizProperties;
|
||||
//
|
||||
// @Autowired
|
||||
// private OAuth2AccessTokenMapper oauth2AccessTokenMapper;
|
||||
// @Autowired
|
||||
// private OAuth2RefreshTokenMapper oauth2RefreshTokenMapper;
|
||||
//
|
||||
// @Autowired
|
||||
// private OAuth2AccessTokenRedisDAO oauth2AccessTokenRedisDAO;
|
||||
//
|
||||
// @Override
|
||||
// @Transactional
|
||||
// public OAuth2AccessTokenRespDTO createAccessToken(OAuth2CreateAccessTokenReqDTO createAccessTokenDTO) {
|
||||
// // 创建刷新令牌 + 访问令牌
|
||||
// OAuth2RefreshTokenDO refreshTokenDO = createOAuth2RefreshToken(createAccessTokenDTO.getUserId(),
|
||||
// createAccessTokenDTO.getUserType(), createAccessTokenDTO.getCreateIp());
|
||||
// OAuth2AccessTokenDO accessTokenDO = createOAuth2AccessToken(refreshTokenDO, createAccessTokenDTO.getCreateIp());
|
||||
// // 返回访问令牌
|
||||
// return OAuth2Convert.INSTANCE.convert(accessTokenDO);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// @Transactional
|
||||
// public OAuth2AccessTokenRespDTO checkAccessToken(String accessToken) {
|
||||
// OAuth2AccessTokenDO accessTokenDO = this.getOAuth2AccessToken(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);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// @Transactional
|
||||
// public OAuth2AccessTokenRespDTO refreshAccessToken(OAuth2RefreshAccessTokenReqDTO refreshAccessTokenDTO) {
|
||||
// OAuth2RefreshTokenDO refreshTokenDO = oauth2RefreshTokenMapper.selectById(refreshAccessTokenDTO.getRefreshToken());
|
||||
// // 校验刷新令牌是否合法
|
||||
// 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 的代码
|
||||
// List<OAuth2AccessTokenDO> accessTokenDOs = oauth2AccessTokenMapper.selectListByRefreshToken(refreshAccessTokenDTO.getRefreshToken());
|
||||
// accessTokenDOs.forEach(accessTokenDO -> deleteOAuth2AccessToken(accessTokenDO.getId()));
|
||||
//
|
||||
// // 创建访问令牌
|
||||
// OAuth2AccessTokenDO oauth2AccessTokenDO = createOAuth2AccessToken(refreshTokenDO, refreshAccessTokenDTO.getCreateIp());
|
||||
// // 返回访问令牌
|
||||
// return OAuth2Convert.INSTANCE.convert(oauth2AccessTokenDO);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// @Transactional
|
||||
// public void removeToken(OAuth2RemoveTokenByUserReqDTO removeTokenDTO) {
|
||||
// // 删除 Access Token
|
||||
// OAuth2AccessTokenDO accessTokenDO = oauth2AccessTokenMapper.selectByUserIdAndUserType(
|
||||
// removeTokenDTO.getUserId(), removeTokenDTO.getUserType());
|
||||
// if (accessTokenDO != null) {
|
||||
// this.deleteOAuth2AccessToken(accessTokenDO.getId());
|
||||
// }
|
||||
//
|
||||
// // 删除 Refresh Token
|
||||
// oauth2RefreshTokenMapper.deleteByUserIdAndUserType(removeTokenDTO.getUserId(), removeTokenDTO.getUserType());
|
||||
// }
|
||||
//
|
||||
// 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() + systemBizProperties.getAccessTokenExpireTimeMillis()))
|
||||
// .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() + systemBizProperties.getRefreshTokenExpireTimeMillis()))
|
||||
// .setCreateIp(createIp);
|
||||
// oauth2RefreshTokenMapper.insert(refreshToken);
|
||||
// return refreshToken;
|
||||
// }
|
||||
//
|
||||
// private OAuth2AccessTokenDO getOAuth2AccessToken(String accessToken) {
|
||||
// // 优先从 Redis 中获取
|
||||
// OAuth2AccessTokenDO accessTokenDO = oauth2AccessTokenRedisDAO.get(accessToken);
|
||||
// if (accessTokenDO != null) {
|
||||
// return accessTokenDO;
|
||||
// }
|
||||
//
|
||||
// // 获取不到,从 MySQL 中获取
|
||||
// accessTokenDO = oauth2AccessTokenMapper.selectById(accessToken);
|
||||
// // 如果在 MySQL 存在,则往 Redis 中写入
|
||||
// if (accessTokenDO != null) {
|
||||
// oauth2AccessTokenRedisDAO.set(accessTokenDO);
|
||||
// }
|
||||
// return accessTokenDO;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 删除 accessToken 的 MySQL 与 Redis 的数据
|
||||
// *
|
||||
// * @param accessToken 访问令牌
|
||||
// */
|
||||
// private void deleteOAuth2AccessToken(String accessToken) {
|
||||
// // 删除 MySQL
|
||||
// oauth2AccessTokenMapper.deleteById(accessToken);
|
||||
// // 删除 Redis
|
||||
// oauth2AccessTokenRedisDAO.delete(accessToken);
|
||||
// }
|
||||
//
|
||||
// private static String generateAccessToken() {
|
||||
// return StringUtils.uuid(true);
|
||||
// }
|
||||
//
|
||||
// private static String generateRefreshToken() {
|
||||
// return StringUtils.uuid(true);
|
||||
// }
|
||||
|
||||
}
|
||||
@ -0,0 +1,182 @@
|
||||
package cn.iocoder.yudao.module.system.service.auth;
|
||||
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
|
||||
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
|
||||
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
||||
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 org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Calendar;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
|
||||
/**
|
||||
* OAuth2.0 Token Service 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
public class OAuth2TokenServiceImpl implements OAuth2TokenService {
|
||||
|
||||
@Resource
|
||||
private OAuth2AccessTokenMapper oauth2AccessTokenMapper;
|
||||
@Resource
|
||||
private OAuth2RefreshTokenMapper oauth2RefreshTokenMapper;
|
||||
|
||||
@Resource
|
||||
private OAuth2AccessTokenRedisDAO oauth2AccessTokenRedisDAO;
|
||||
|
||||
@Resource
|
||||
private OAuth2ClientService oauth2ClientService;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public OAuth2AccessTokenDO createAccessToken(Long userId, Integer userType, Long clientId) {
|
||||
OAuth2ClientDO clientDO = oauth2ClientService.validOAuthClientFromCache(clientId);
|
||||
// 创建刷新令牌
|
||||
OAuth2RefreshTokenDO refreshTokenDO = createOAuth2RefreshToken(userId, userType, clientDO);
|
||||
// 创建访问令牌
|
||||
OAuth2AccessTokenDO accessTokenDO = createOAuth2AccessToken(refreshTokenDO, clientDO);
|
||||
// 记录到 Redis 中
|
||||
oauth2AccessTokenRedisDAO.set(accessTokenDO);
|
||||
return accessTokenDO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2AccessTokenDO refreshAccessToken(String refreshToken) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2AccessTokenDO getAccessToken(String accessToken) {
|
||||
// 优先从 Redis 中获取
|
||||
OAuth2AccessTokenDO accessTokenDO = oauth2AccessTokenRedisDAO.get(accessToken);
|
||||
if (accessTokenDO != null) {
|
||||
return accessTokenDO;
|
||||
}
|
||||
|
||||
// 获取不到,从 MySQL 中获取
|
||||
accessTokenDO = oauth2AccessTokenMapper.selectById(accessToken);
|
||||
// 如果在 MySQL 存在,则往 Redis 中写入
|
||||
if (accessTokenDO != null && !DateUtils.isExpired(accessTokenDO.getExpiresTime())) {
|
||||
oauth2AccessTokenRedisDAO.set(accessTokenDO);
|
||||
}
|
||||
return accessTokenDO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2AccessTokenDO checkAccessToken(String accessToken) {
|
||||
OAuth2AccessTokenDO accessTokenDO = getAccessToken(accessToken);
|
||||
if (accessTokenDO == null) {
|
||||
throw exception(GlobalErrorCodeConstants.UNAUTHORIZED, "Token 不存在");
|
||||
}
|
||||
if (DateUtils.isExpired(accessTokenDO.getExpiresTime())) {
|
||||
throw exception(GlobalErrorCodeConstants.UNAUTHORIZED, "Token 已过期");
|
||||
}
|
||||
return accessTokenDO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAccessToken(String accessToken) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// @Override
|
||||
// @Transactional
|
||||
// public OAuth2AccessTokenRespDTO checkAccessToken(String accessToken) {
|
||||
// OAuth2AccessTokenDO accessTokenDO = this.getOAuth2AccessToken(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);
|
||||
// }
|
||||
|
||||
// @Override
|
||||
// @Transactional
|
||||
// public OAuth2AccessTokenRespDTO refreshAccessToken(OAuth2RefreshAccessTokenReqDTO refreshAccessTokenDTO) {
|
||||
// OAuth2RefreshTokenDO refreshTokenDO = oauth2RefreshTokenMapper.selectById(refreshAccessTokenDTO.getRefreshToken());
|
||||
// // 校验刷新令牌是否合法
|
||||
// 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 的代码
|
||||
// List<OAuth2AccessTokenDO> accessTokenDOs = oauth2AccessTokenMapper.selectListByRefreshToken(refreshAccessTokenDTO.getRefreshToken());
|
||||
// accessTokenDOs.forEach(accessTokenDO -> deleteOAuth2AccessToken(accessTokenDO.getId()));
|
||||
//
|
||||
// // 创建访问令牌
|
||||
// OAuth2AccessTokenDO oauth2AccessTokenDO = createOAuth2AccessToken(refreshTokenDO, refreshAccessTokenDTO.getCreateIp());
|
||||
// // 返回访问令牌
|
||||
// return OAuth2Convert.INSTANCE.convert(oauth2AccessTokenDO);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// @Transactional
|
||||
// public void removeToken(OAuth2RemoveTokenByUserReqDTO removeTokenDTO) {
|
||||
// // 删除 Access Token
|
||||
// OAuth2AccessTokenDO accessTokenDO = oauth2AccessTokenMapper.selectByUserIdAndUserType(
|
||||
// removeTokenDTO.getUserId(), removeTokenDTO.getUserType());
|
||||
// if (accessTokenDO != null) {
|
||||
// this.deleteOAuth2AccessToken(accessTokenDO.getId());
|
||||
// }
|
||||
//
|
||||
// // 删除 Refresh Token
|
||||
// oauth2RefreshTokenMapper.deleteByUserIdAndUserType(removeTokenDTO.getUserId(), removeTokenDTO.getUserType());
|
||||
// }
|
||||
|
||||
private OAuth2AccessTokenDO createOAuth2AccessToken(OAuth2RefreshTokenDO refreshTokenDO, OAuth2ClientDO clientDO) {
|
||||
OAuth2AccessTokenDO accessToken = new OAuth2AccessTokenDO().setAccessToken(generateAccessToken())
|
||||
.setUserId(refreshTokenDO.getUserId()).setUserType(refreshTokenDO.getUserType()).setClientId(clientDO.getId())
|
||||
.setRefreshToken(refreshTokenDO.getRefreshToken())
|
||||
.setExpiresTime(DateUtils.addDate(Calendar.SECOND, clientDO.getAccessTokenValiditySeconds()));
|
||||
accessToken.setTenantId(TenantContextHolder.getTenantId()); // 手动设置租户编号,避免缓存到 Redis 的时候,无对应的租户编号
|
||||
oauth2AccessTokenMapper.insert(accessToken);
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
private OAuth2RefreshTokenDO createOAuth2RefreshToken(Long userId, Integer userType, OAuth2ClientDO clientDO) {
|
||||
OAuth2RefreshTokenDO refreshToken = new OAuth2RefreshTokenDO().setRefreshToken(generateRefreshToken())
|
||||
.setUserId(userId).setUserType(userType).setClientId(clientDO.getId())
|
||||
.setExpiresTime(DateUtils.addDate(Calendar.SECOND, clientDO.getRefreshTokenValiditySeconds()));
|
||||
oauth2RefreshTokenMapper.insert(refreshToken);
|
||||
return refreshToken;
|
||||
}
|
||||
|
||||
|
||||
// /**
|
||||
// * 删除 accessToken 的 MySQL 与 Redis 的数据
|
||||
// *
|
||||
// * @param accessToken 访问令牌
|
||||
// */
|
||||
// private void deleteOAuth2AccessToken(String accessToken) {
|
||||
// // 删除 MySQL
|
||||
// oauth2AccessTokenMapper.deleteById(accessToken);
|
||||
// // 删除 Redis
|
||||
// oauth2AccessTokenRedisDAO.delete(accessToken);
|
||||
// }
|
||||
//
|
||||
private static String generateAccessToken() {
|
||||
return IdUtil.fastSimpleUUID();
|
||||
}
|
||||
|
||||
private static String generateRefreshToken() {
|
||||
return IdUtil.fastSimpleUUID();
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue