实现单设备登录功能
This commit is contained in:
@@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
|||||||
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token.OAuth2AccessTokenPageReqVO;
|
import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token.OAuth2AccessTokenPageReqVO;
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
@@ -32,4 +33,12 @@ public interface OAuth2AccessTokenMapper extends BaseMapperX<OAuth2AccessTokenDO
|
|||||||
.orderByDesc(OAuth2AccessTokenDO::getId));
|
.orderByDesc(OAuth2AccessTokenDO::getId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default void deleteByUserId(Long userId) {
|
||||||
|
delete(Wrappers.lambdaUpdate(OAuth2AccessTokenDO.class).eq(OAuth2AccessTokenDO::getUserId, userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
default List<OAuth2AccessTokenDO> selectListByUserId(Long userId) {
|
||||||
|
return selectList(OAuth2AccessTokenDO::getUserId, userId);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
|||||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||||
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2RefreshTokenDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2RefreshTokenDO;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
@Mapper
|
@Mapper
|
||||||
@@ -18,5 +19,7 @@ public interface OAuth2RefreshTokenMapper extends BaseMapperX<OAuth2RefreshToken
|
|||||||
default OAuth2RefreshTokenDO selectByRefreshToken(String refreshToken) {
|
default OAuth2RefreshTokenDO selectByRefreshToken(String refreshToken) {
|
||||||
return selectOne(OAuth2RefreshTokenDO::getRefreshToken, refreshToken);
|
return selectOne(OAuth2RefreshTokenDO::getRefreshToken, refreshToken);
|
||||||
}
|
}
|
||||||
|
default void deleteByUserId(Long userId) {
|
||||||
|
delete(Wrappers.lambdaUpdate(OAuth2RefreshTokenDO.class).eq(OAuth2RefreshTokenDO::getUserId, userId));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,11 @@ public interface OAuth2TokenService {
|
|||||||
*/
|
*/
|
||||||
OAuth2AccessTokenDO refreshAccessToken(String refreshToken, String clientId);
|
OAuth2AccessTokenDO refreshAccessToken(String refreshToken, String clientId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除用户所有返回令牌
|
||||||
|
* @param userId 用户 Id
|
||||||
|
*/
|
||||||
|
void removeAccessTokenByUserId(Long userId);
|
||||||
/**
|
/**
|
||||||
* 获得访问令牌
|
* 获得访问令牌
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import cn.hutool.core.util.StrUtil;
|
|||||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||||
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
|
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
|
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||||
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
|
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
|
||||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||||
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
||||||
@@ -22,6 +23,7 @@ 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.mysql.oauth2.OAuth2RefreshTokenMapper;
|
||||||
import cn.iocoder.yudao.module.system.dal.redis.oauth2.OAuth2AccessTokenRedisDAO;
|
import cn.iocoder.yudao.module.system.dal.redis.oauth2.OAuth2AccessTokenRedisDAO;
|
||||||
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
|
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
@@ -31,6 +33,7 @@ import java.time.LocalDateTime;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0;
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0;
|
||||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||||
@@ -57,11 +60,19 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
|
|||||||
@Lazy // 懒加载,避免循环依赖
|
@Lazy // 懒加载,避免循环依赖
|
||||||
private AdminUserService adminUserService;
|
private AdminUserService adminUserService;
|
||||||
|
|
||||||
|
@Value("${multiple-device-login}")
|
||||||
|
private Boolean multipleDeviceLoginConfig;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public OAuth2AccessTokenDO createAccessToken(Long userId, Integer userType, String clientId, List<String> scopes) {
|
public OAuth2AccessTokenDO createAccessToken(Long userId, Integer userType, String clientId, List<String> scopes) {
|
||||||
|
// 在 yaml multiple-device-login = True 时 删除用户上次登录令牌,实现单设备登录
|
||||||
|
if (multipleDeviceLoginConfig){
|
||||||
|
removeAccessTokenByUserId(userId);
|
||||||
|
}
|
||||||
OAuth2ClientDO clientDO = oauth2ClientService.validOAuthClientFromCache(clientId);
|
OAuth2ClientDO clientDO = oauth2ClientService.validOAuthClientFromCache(clientId);
|
||||||
// 创建刷新令牌
|
// 创建刷新令牌
|
||||||
|
|
||||||
OAuth2RefreshTokenDO refreshTokenDO = createOAuth2RefreshToken(userId, userType, clientDO, scopes);
|
OAuth2RefreshTokenDO refreshTokenDO = createOAuth2RefreshToken(userId, userType, clientDO, scopes);
|
||||||
// 创建访问令牌
|
// 创建访问令牌
|
||||||
return createOAuth2AccessToken(refreshTokenDO, clientDO);
|
return createOAuth2AccessToken(refreshTokenDO, clientDO);
|
||||||
@@ -99,6 +110,18 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
|
|||||||
return createOAuth2AccessToken(refreshTokenDO, clientDO);
|
return createOAuth2AccessToken(refreshTokenDO, clientDO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void removeAccessTokenByUserId(Long userId) {
|
||||||
|
List<OAuth2AccessTokenDO> oAuth2AccessTokenDOList = oauth2AccessTokenMapper.selectListByUserId(userId);
|
||||||
|
if (!CollectionUtils.isAnyEmpty(oAuth2AccessTokenDOList)) {
|
||||||
|
oauth2AccessTokenRedisDAO.deleteList(oAuth2AccessTokenDOList.stream().map(OAuth2AccessTokenDO::getAccessToken).collect(Collectors.toList()));
|
||||||
|
|
||||||
|
}
|
||||||
|
oauth2AccessTokenMapper.deleteByUserId(userId);
|
||||||
|
oauth2RefreshTokenMapper.deleteByUserId(userId);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OAuth2AccessTokenDO getAccessToken(String accessToken) {
|
public OAuth2AccessTokenDO getAccessToken(String accessToken) {
|
||||||
// 优先从 Redis 中获取
|
// 优先从 Redis 中获取
|
||||||
@@ -126,6 +149,7 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
|
|||||||
return accessTokenDO;
|
return accessTokenDO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OAuth2AccessTokenDO checkAccessToken(String accessToken) {
|
public OAuth2AccessTokenDO checkAccessToken(String accessToken) {
|
||||||
OAuth2AccessTokenDO accessTokenDO = getAccessToken(accessToken);
|
OAuth2AccessTokenDO accessTokenDO = getAccessToken(accessToken);
|
||||||
|
|||||||
@@ -227,4 +227,6 @@ pf4j:
|
|||||||
pluginsDir: /Users/anhaohao/code/gitee/ruoyi-vue-pro/plugins # 插件目录
|
pluginsDir: /Users/anhaohao/code/gitee/ruoyi-vue-pro/plugins # 插件目录
|
||||||
|
|
||||||
md5:
|
md5:
|
||||||
salt: (-FhqvXO,wMz
|
salt: (-FhqvXO,wMz
|
||||||
|
|
||||||
|
multiple-device-login: true
|
||||||
Reference in New Issue
Block a user