mirror of
http://192.168.1.205:9980/cf_devdept2/cf_imes_server.git
synced 2026-08-12 21:02:08 +08:00
禅道bug#1588修复
This commit is contained in:
+1
@@ -23,6 +23,7 @@ public class GlobalErrorCodeConstants {
|
|||||||
public static final ErrorCode NO_PERMISSION_TO_VISIT_ORG = new ErrorCode(400, "global.error.no.permission.to.visit.org");
|
public static final ErrorCode NO_PERMISSION_TO_VISIT_ORG = new ErrorCode(400, "global.error.no.permission.to.visit.org");
|
||||||
public static final ErrorCode REQUEST_PARAM_TYPE_ERROR = new ErrorCode(400, "global.error.request.param.type.error");
|
public static final ErrorCode REQUEST_PARAM_TYPE_ERROR = new ErrorCode(400, "global.error.request.param.type.error");
|
||||||
public static final ErrorCode UNAUTHORIZED = new ErrorCode(401, "global.not.login");
|
public static final ErrorCode UNAUTHORIZED = new ErrorCode(401, "global.not.login");
|
||||||
|
public static final ErrorCode BE_KICKED_OUT = new ErrorCode(401, "be.kicked.out");
|
||||||
public static final ErrorCode FORBIDDEN = new ErrorCode(403, "global.error.no.permission");
|
public static final ErrorCode FORBIDDEN = new ErrorCode(403, "global.error.no.permission");
|
||||||
public static final ErrorCode REQUEST_PARAM_MISSING = new ErrorCode(403, "global.error.request.param.missing");
|
public static final ErrorCode REQUEST_PARAM_MISSING = new ErrorCode(403, "global.error.request.param.missing");
|
||||||
public static final ErrorCode NOT_FOUND = new ErrorCode(404, "global.error.request.not.found");
|
public static final ErrorCode NOT_FOUND = new ErrorCode(404, "global.error.request.not.found");
|
||||||
|
|||||||
+14
@@ -80,6 +80,20 @@ public class CommonResult<T> implements Serializable {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T> CommonResult<T> error(Integer code, T data, String message) {
|
||||||
|
Assert.isTrue(!GlobalErrorCodeConstants.SUCCESS.getCode().equals(code), "code 必须是错误的!");
|
||||||
|
CommonResult<T> result = new CommonResult<>();
|
||||||
|
result.code = code;
|
||||||
|
result.msg = message;
|
||||||
|
result.data = data;
|
||||||
|
|
||||||
|
String traceId = MDC.get(TRACE_ID);
|
||||||
|
if (StringUtils.hasText(traceId)) {
|
||||||
|
result.traceId = traceId;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
public static <T> CommonResult<T> error(Integer code, String message, Object... args) {
|
public static <T> CommonResult<T> error(Integer code, String message, Object... args) {
|
||||||
Assert.isTrue(!GlobalErrorCodeConstants.SUCCESS.getCode().equals(code), "code 必须是错误的!");
|
Assert.isTrue(!GlobalErrorCodeConstants.SUCCESS.getCode().equals(code), "code 必须是错误的!");
|
||||||
CommonResult<T> result = new CommonResult<>();
|
CommonResult<T> result = new CommonResult<>();
|
||||||
|
|||||||
+15
-1
@@ -13,10 +13,12 @@ import com.cf.imes.framework.web.core.handler.GlobalExceptionHandler;
|
|||||||
import com.cf.imes.framework.web.core.util.WebFrameworkUtils;
|
import com.cf.imes.framework.web.core.util.WebFrameworkUtils;
|
||||||
import com.cf.imes.module.system.api.oauth2.OAuth2TokenApi;
|
import com.cf.imes.module.system.api.oauth2.OAuth2TokenApi;
|
||||||
import com.cf.imes.module.system.api.oauth2.dto.OAuth2AccessTokenCheckRespDTO;
|
import com.cf.imes.module.system.api.oauth2.dto.OAuth2AccessTokenCheckRespDTO;
|
||||||
|
import com.cf.imes.module.system.api.oauth2.dto.OAuth2KickOutAccessTokenRespDTO;
|
||||||
import com.cf.imes.module.system.api.permission.PermissionApi;
|
import com.cf.imes.module.system.api.permission.PermissionApi;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.security.access.AccessDeniedException;
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
import org.springframework.web.filter.OncePerRequestFilter;
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
@@ -46,6 +48,9 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
|
|
||||||
private final PermissionApi permissionApi;
|
private final PermissionApi permissionApi;
|
||||||
|
|
||||||
|
public static final String KICKOUT_IP = "kickoutIp";
|
||||||
|
public static final String KICKOUT_TIME = "kickoutTime";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("NullableProblems")
|
@SuppressWarnings("NullableProblems")
|
||||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
|
||||||
@@ -55,8 +60,9 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
|
|
||||||
// 情况二,基于 Token 获得用户
|
// 情况二,基于 Token 获得用户
|
||||||
// 注意,这里主要满足直接使用 Nginx 直接转发到 Spring Cloud 服务的场景。
|
// 注意,这里主要满足直接使用 Nginx 直接转发到 Spring Cloud 服务的场景。
|
||||||
|
String token = null;
|
||||||
if (loginUser == null) {
|
if (loginUser == null) {
|
||||||
String token = SecurityFrameworkUtils.obtainAuthorization(request,
|
token = SecurityFrameworkUtils.obtainAuthorization(request,
|
||||||
securityProperties.getTokenHeader(), securityProperties.getTokenParameter());
|
securityProperties.getTokenHeader(), securityProperties.getTokenParameter());
|
||||||
if (CharSequenceUtil.isNotEmpty(token)) {
|
if (CharSequenceUtil.isNotEmpty(token)) {
|
||||||
Integer userType = WebFrameworkUtils.getLoginUserType(request);
|
Integer userType = WebFrameworkUtils.getLoginUserType(request);
|
||||||
@@ -78,6 +84,14 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
|
|||||||
// 设置当前用户
|
// 设置当前用户
|
||||||
if (loginUser != null) {
|
if (loginUser != null) {
|
||||||
SecurityFrameworkUtils.setLoginUser(loginUser, request);
|
SecurityFrameworkUtils.setLoginUser(loginUser, request);
|
||||||
|
} else {
|
||||||
|
if (StringUtils.isNotEmpty(token)) {
|
||||||
|
OAuth2KickOutAccessTokenRespDTO kickoutToken = oauth2TokenApi.getKickoutToken(token).getCheckedData();
|
||||||
|
if (ObjectUtil.isNotNull(kickoutToken)) {
|
||||||
|
request.setAttribute(KICKOUT_IP, kickoutToken.getIp());
|
||||||
|
request.setAttribute(KICKOUT_TIME, kickoutToken.getKickTime());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// 继续过滤链
|
// 继续过滤链
|
||||||
chain.doFilter(request, response);
|
chain.doFilter(request, response);
|
||||||
|
|||||||
+20
@@ -1,5 +1,6 @@
|
|||||||
package com.cf.imes.framework.security.core.handler;
|
package com.cf.imes.framework.security.core.handler;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import com.cf.imes.framework.common.exception.enums.GlobalErrorCodeConstants;
|
import com.cf.imes.framework.common.exception.enums.GlobalErrorCodeConstants;
|
||||||
import com.cf.imes.framework.common.pojo.CommonResult;
|
import com.cf.imes.framework.common.pojo.CommonResult;
|
||||||
import com.cf.imes.framework.common.util.i18n.core.util.I18nUtils;
|
import com.cf.imes.framework.common.util.i18n.core.util.I18nUtils;
|
||||||
@@ -14,7 +15,10 @@ import jakarta.servlet.FilterChain;
|
|||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import static com.cf.imes.framework.common.exception.enums.GlobalErrorCodeConstants.BE_KICKED_OUT;
|
||||||
import static com.cf.imes.framework.common.exception.enums.GlobalErrorCodeConstants.UNAUTHORIZED;
|
import static com.cf.imes.framework.common.exception.enums.GlobalErrorCodeConstants.UNAUTHORIZED;
|
||||||
|
import static com.cf.imes.framework.security.core.filter.TokenAuthenticationFilter.KICKOUT_IP;
|
||||||
|
import static com.cf.imes.framework.security.core.filter.TokenAuthenticationFilter.KICKOUT_TIME;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 访问一个需要认证的 URL 资源,但是此时自己尚未认证(登录)的情况下,返回 {@link GlobalErrorCodeConstants#UNAUTHORIZED} 错误码,从而使前端重定向到登录页
|
* 访问一个需要认证的 URL 资源,但是此时自己尚未认证(登录)的情况下,返回 {@link GlobalErrorCodeConstants#UNAUTHORIZED} 错误码,从而使前端重定向到登录页
|
||||||
@@ -29,9 +33,25 @@ public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {
|
|||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private I18nUtils i18nUtils;
|
private I18nUtils i18nUtils;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) {
|
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) {
|
||||||
log.debug("[commence][访问 URL({}) 时,没有登录]", request.getRequestURI(), e);
|
log.debug("[commence][访问 URL({}) 时,没有登录]", request.getRequestURI(), e);
|
||||||
|
Object kickoutIpObj = request.getAttribute(KICKOUT_IP);
|
||||||
|
Object kickoutTimeObj = request.getAttribute(KICKOUT_TIME);
|
||||||
|
|
||||||
|
// 被踢下线
|
||||||
|
if (ObjectUtil.isAllNotEmpty(kickoutIpObj, kickoutTimeObj)) {
|
||||||
|
String kickMsg = i18nUtils.getMessage(
|
||||||
|
BE_KICKED_OUT.getMsg(),
|
||||||
|
kickoutTimeObj,
|
||||||
|
kickoutIpObj
|
||||||
|
);
|
||||||
|
|
||||||
|
ServletUtils.writeJSON(response, CommonResult.error(UNAUTHORIZED.getCode(), kickMsg, i18nUtils.getMessage(UNAUTHORIZED.getMsg())));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 返回 401
|
// 返回 401
|
||||||
ServletUtils.writeJSON(response, CommonResult.error(UNAUTHORIZED.getCode(), i18nUtils.getMessage(UNAUTHORIZED.getMsg())));
|
ServletUtils.writeJSON(response, CommonResult.error(UNAUTHORIZED.getCode(), i18nUtils.getMessage(UNAUTHORIZED.getMsg())));
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -12,6 +12,7 @@ global.error.request.organId.notExist=請求的組織標識未傳遞,請進行
|
|||||||
global.org.product.not.exist=帳號登入異常,套餐資訊不存在,請重新登入
|
global.org.product.not.exist=帳號登入異常,套餐資訊不存在,請重新登入
|
||||||
global.org.product.expired=組織產品套餐已過期,請續費後繼續使用
|
global.org.product.expired=組織產品套餐已過期,請續費後繼續使用
|
||||||
global.not.login=賬號未登錄
|
global.not.login=賬號未登錄
|
||||||
|
be.kicked.out=您的賬號於 {0} 在別處登錄,已被迫下線。登錄IP:[{1}]
|
||||||
|
|
||||||
order.not.exists=訂單不存在
|
order.not.exists=訂單不存在
|
||||||
parent.order.not.exists=上級訂單{0}不存在
|
parent.order.not.exists=上級訂單{0}不存在
|
||||||
|
|||||||
+1
@@ -12,6 +12,7 @@ global.error.request.organId.notExist=The requested organization ID was not prov
|
|||||||
global.org.product.not.exist=Account login exception: product package does not exist, please log in again
|
global.org.product.not.exist=Account login exception: product package does not exist, please log in again
|
||||||
global.org.product.expired=The organization's product package has expired, please renew before continuing to use the service
|
global.org.product.expired=The organization's product package has expired, please renew before continuing to use the service
|
||||||
global.not.login=The account is not logged in
|
global.not.login=The account is not logged in
|
||||||
|
be.kicked.out=Your account was logged in elsewhere at {0} and has been forced to log off. Login IP: [{1}]
|
||||||
|
|
||||||
order.not.exists=Order does not exist
|
order.not.exists=Order does not exist
|
||||||
parent.order.not.exists=Parent order {0} does not exist
|
parent.order.not.exists=Parent order {0} does not exist
|
||||||
|
|||||||
+1
@@ -12,6 +12,7 @@ global.error.request.organId.notExist=请求的组织标识未传递,请进行
|
|||||||
global.org.product.not.exist=账号登录异常,套餐信息不存在,请重新登陆
|
global.org.product.not.exist=账号登录异常,套餐信息不存在,请重新登陆
|
||||||
global.org.product.expired=组织产品套餐已过期,请续费后继续使用
|
global.org.product.expired=组织产品套餐已过期,请续费后继续使用
|
||||||
global.not.login=账号未登录
|
global.not.login=账号未登录
|
||||||
|
be.kicked.out=您的账号于 {0} 在别处登录,已被迫下线。登录IP:[{1}]
|
||||||
|
|
||||||
order.not.exists=订单不存在
|
order.not.exists=订单不存在
|
||||||
parent.order.not.exists=上级订单{0}不存在
|
parent.order.not.exists=上级订单{0}不存在
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
report.template.not_exists=報表模板資訊不存在
|
report.template.not_exists=報表模板資訊不存在
|
||||||
+1
-1
@@ -1 +1 @@
|
|||||||
report.template.not_exists=Report template does not exist
|
report.template.not_exists=Report template does not exist
|
||||||
+1
-1
@@ -1 +1 @@
|
|||||||
report.template.not_exists=报表模板信息不存在
|
report.template.not_exists=报表模板信息不存在
|
||||||
+4
@@ -4,6 +4,7 @@ import com.cf.imes.framework.common.pojo.CommonResult;
|
|||||||
import com.cf.imes.module.system.api.oauth2.dto.OAuth2AccessTokenCheckRespDTO;
|
import com.cf.imes.module.system.api.oauth2.dto.OAuth2AccessTokenCheckRespDTO;
|
||||||
import com.cf.imes.module.system.api.oauth2.dto.OAuth2AccessTokenCreateReqDTO;
|
import com.cf.imes.module.system.api.oauth2.dto.OAuth2AccessTokenCreateReqDTO;
|
||||||
import com.cf.imes.module.system.api.oauth2.dto.OAuth2AccessTokenRespDTO;
|
import com.cf.imes.module.system.api.oauth2.dto.OAuth2AccessTokenRespDTO;
|
||||||
|
import com.cf.imes.module.system.api.oauth2.dto.OAuth2KickOutAccessTokenRespDTO;
|
||||||
import com.cf.imes.module.system.enums.ApiConstants;
|
import com.cf.imes.module.system.enums.ApiConstants;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
@@ -64,4 +65,7 @@ public interface OAuth2TokenApi {
|
|||||||
@Operation(summary = "请求忽略令牌数据源")
|
@Operation(summary = "请求忽略令牌数据源")
|
||||||
CommonResult<String> getIgnoreTokenSource();
|
CommonResult<String> getIgnoreTokenSource();
|
||||||
|
|
||||||
|
@GetMapping(PREFIX + "/kickout/{token}")
|
||||||
|
@Operation(summary = "获取当前token对应的被踢除token")
|
||||||
|
CommonResult<OAuth2KickOutAccessTokenRespDTO> getKickoutToken(@PathVariable("token") String token);
|
||||||
}
|
}
|
||||||
|
|||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
package com.cf.imes.module.system.api.oauth2.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@Schema(description = "RPC 服务 - OAuth2 被踢除令牌的信息 Response DTO")
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class OAuth2KickOutAccessTokenRespDTO implements Serializable {
|
||||||
|
|
||||||
|
@Schema(description = "登录ip")
|
||||||
|
private String ip;
|
||||||
|
|
||||||
|
@Schema(description = "踢除时间")
|
||||||
|
private String kickTime;
|
||||||
|
|
||||||
|
}
|
||||||
+7
@@ -5,6 +5,7 @@ import com.cf.imes.framework.common.util.object.BeanUtils;
|
|||||||
import com.cf.imes.module.system.api.oauth2.dto.OAuth2AccessTokenCheckRespDTO;
|
import com.cf.imes.module.system.api.oauth2.dto.OAuth2AccessTokenCheckRespDTO;
|
||||||
import com.cf.imes.module.system.api.oauth2.dto.OAuth2AccessTokenCreateReqDTO;
|
import com.cf.imes.module.system.api.oauth2.dto.OAuth2AccessTokenCreateReqDTO;
|
||||||
import com.cf.imes.module.system.api.oauth2.dto.OAuth2AccessTokenRespDTO;
|
import com.cf.imes.module.system.api.oauth2.dto.OAuth2AccessTokenRespDTO;
|
||||||
|
import com.cf.imes.module.system.api.oauth2.dto.OAuth2KickOutAccessTokenRespDTO;
|
||||||
import com.cf.imes.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
|
import com.cf.imes.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
|
||||||
import com.cf.imes.module.system.service.oauth2.OAuth2TokenService;
|
import com.cf.imes.module.system.service.oauth2.OAuth2TokenService;
|
||||||
import com.cf.imes.module.system.service.organ.OrganService;
|
import com.cf.imes.module.system.service.organ.OrganService;
|
||||||
@@ -68,4 +69,10 @@ public class OAuth2TokenApiImpl implements OAuth2TokenApi {
|
|||||||
public CommonResult<String> getIgnoreTokenSource() {
|
public CommonResult<String> getIgnoreTokenSource() {
|
||||||
return success(oauth2TokenService.getIgnoreTokenSource());
|
return success(oauth2TokenService.getIgnoreTokenSource());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommonResult<OAuth2KickOutAccessTokenRespDTO> getKickoutToken(String token) {
|
||||||
|
OAuth2AccessTokenDO kickOutAccessToken = oauth2TokenService.getKickOutAccessToken(token);
|
||||||
|
return success(BeanUtils.toBean(kickOutAccessToken, OAuth2KickOutAccessTokenRespDTO.class));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+12
@@ -117,4 +117,16 @@ public class OAuth2AccessTokenDO extends BaseDO {
|
|||||||
*/
|
*/
|
||||||
@TableField(exist = false)
|
@TableField(exist = false)
|
||||||
private Long productId;
|
private Long productId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ip地址
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String ip;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 踢出时间
|
||||||
|
*/
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String kickTime;
|
||||||
}
|
}
|
||||||
|
|||||||
+5
@@ -69,6 +69,11 @@ public class RedisKeyConstants {
|
|||||||
*/
|
*/
|
||||||
public static final String OAUTH2_ACCESS_TOKEN = "oauth2_access_token:%s";
|
public static final String OAUTH2_ACCESS_TOKEN = "oauth2_access_token:%s";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 踢出令牌的缓存,记录当前登录ip
|
||||||
|
*/
|
||||||
|
public static final String OAUTH2_KICKOUT_TOKEN = "oauth2_kickout_token:%s";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 站内信模版的缓存
|
* 站内信模版的缓存
|
||||||
* <p>
|
* <p>
|
||||||
|
|||||||
+34
@@ -1,5 +1,7 @@
|
|||||||
package com.cf.imes.module.system.dal.redis.oauth2;
|
package com.cf.imes.module.system.dal.redis.oauth2;
|
||||||
|
|
||||||
|
import cn.hutool.core.date.DatePattern;
|
||||||
|
import cn.hutool.core.date.DateUtil;
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
import com.cf.imes.framework.common.util.collection.CollectionUtils;
|
import com.cf.imes.framework.common.util.collection.CollectionUtils;
|
||||||
import com.cf.imes.framework.common.util.json.JsonUtils;
|
import com.cf.imes.framework.common.util.json.JsonUtils;
|
||||||
@@ -11,10 +13,14 @@ import org.springframework.data.redis.core.StringRedisTemplate;
|
|||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import static com.cf.imes.framework.common.util.servlet.ServletUtils.getClientIP;
|
||||||
import static com.cf.imes.module.system.dal.redis.RedisKeyConstants.OAUTH2_ACCESS_TOKEN;
|
import static com.cf.imes.module.system.dal.redis.RedisKeyConstants.OAUTH2_ACCESS_TOKEN;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -39,6 +45,17 @@ public class OAuth2AccessTokenRedisDAO {
|
|||||||
return JsonUtils.parseObject(stringRedisTemplate.opsForValue().get(redisKey), OAuth2AccessTokenDO.class);
|
return JsonUtils.parseObject(stringRedisTemplate.opsForValue().get(redisKey), OAuth2AccessTokenDO.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* key获取被踢除token
|
||||||
|
*
|
||||||
|
* @param accessToken
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public OAuth2AccessTokenDO getKickOut(String accessToken) {
|
||||||
|
String redisKey = formatKickOutKey(accessToken);
|
||||||
|
return JsonUtils.parseObject(stringRedisTemplate.opsForValue().get(redisKey), OAuth2AccessTokenDO.class);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 带key获取,oauth2_access_token:xxxx
|
* 带key获取,oauth2_access_token:xxxx
|
||||||
*
|
*
|
||||||
@@ -98,6 +115,10 @@ public class OAuth2AccessTokenRedisDAO {
|
|||||||
return String.format(RedisKeyConstants.OAUTH2_ACCESS_TOKEN, accessToken);
|
return String.format(RedisKeyConstants.OAUTH2_ACCESS_TOKEN, accessToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String formatKickOutKey(String accessToken) {
|
||||||
|
return String.format(RedisKeyConstants.OAUTH2_KICKOUT_TOKEN, accessToken);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建token游标,使用时轮训一次获取两百个token
|
* 构建token游标,使用时轮训一次获取两百个token
|
||||||
*
|
*
|
||||||
@@ -106,4 +127,17 @@ public class OAuth2AccessTokenRedisDAO {
|
|||||||
public Cursor<String> scan(){
|
public Cursor<String> scan(){
|
||||||
return stringRedisTemplate.scan(ScanOptions.scanOptions().match(String.format(OAUTH2_ACCESS_TOKEN, "*")).count(200).build());
|
return stringRedisTemplate.scan(ScanOptions.scanOptions().match(String.format(OAUTH2_ACCESS_TOKEN, "*")).count(200).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建被踢除的token
|
||||||
|
*
|
||||||
|
* @param accessToken
|
||||||
|
*/
|
||||||
|
public void createKickOutToken(String accessToken) {
|
||||||
|
String redisKey = formatKickOutKey(accessToken);
|
||||||
|
OAuth2AccessTokenDO kickOutToken = new OAuth2AccessTokenDO();
|
||||||
|
kickOutToken.setIp(getClientIP()).setKickTime(DateUtil.format(LocalDateTime.now(), DatePattern.NORM_DATETIME_PATTERN));
|
||||||
|
// 创建被踢除的token,设置有效期为5分钟
|
||||||
|
stringRedisTemplate.opsForValue().set(redisKey, JSON.toJSONString(kickOutToken), Duration.ofMinutes(5));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
@@ -104,4 +104,12 @@ public interface OAuth2TokenService {
|
|||||||
* @param userId
|
* @param userId
|
||||||
*/
|
*/
|
||||||
void kickOut(Long userId);
|
void kickOut(Long userId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取被剔除的token信息
|
||||||
|
*
|
||||||
|
* @param token
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
OAuth2AccessTokenDO getKickOutAccessToken(String token);
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-8
@@ -25,12 +25,10 @@ import org.apache.commons.lang3.StringUtils;
|
|||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.data.redis.core.Cursor;
|
import org.springframework.data.redis.core.Cursor;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
|
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static com.cf.imes.framework.common.exception.util.ServiceExceptionUtil.exception0;
|
import static com.cf.imes.framework.common.exception.util.ServiceExceptionUtil.exception0;
|
||||||
@@ -78,7 +76,6 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
|
|||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
|
||||||
public OAuth2AccessTokenDO createAccessToken(Long userId, Integer userType, String clientId, List<String> scopes,Boolean large, Integer dbNo, Integer tableNo, String dataCode, Long organId, String nickname) {
|
public OAuth2AccessTokenDO createAccessToken(Long userId, Integer userType, String clientId, List<String> scopes,Boolean large, Integer dbNo, Integer tableNo, String dataCode, Long organId, String nickname) {
|
||||||
OAuth2ClientDO clientDO = oauth2ClientService.validOAuthClientFromCache(clientId);
|
OAuth2ClientDO clientDO = oauth2ClientService.validOAuthClientFromCache(clientId);
|
||||||
// 填入访问令牌包含的字段
|
// 填入访问令牌包含的字段
|
||||||
@@ -243,11 +240,8 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
|
|||||||
accessTokenDO.setAccessToken(currentToken);
|
accessTokenDO.setAccessToken(currentToken);
|
||||||
// 记录到 Redis 中
|
// 记录到 Redis 中
|
||||||
oauth2AccessTokenRedisDAO.set(accessTokenDO);
|
oauth2AccessTokenRedisDAO.set(accessTokenDO);
|
||||||
// 异步踢出其他token
|
// 踢出其他token
|
||||||
CompletableFuture.runAsync(() -> kickOut(userId, currentToken)).exceptionally(e -> {
|
kickOut(userId, currentToken);
|
||||||
log.error("[OAuth2TokenServiceImpl] redis 清空token失败, 用户id:{}, 异常:{}", userId, e);
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
return accessTokenDO;
|
return accessTokenDO;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,6 +260,8 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
|
|||||||
if (ObjectUtil.equal(userId, oAuth2AccessTokenDO.getUserId()) && !StringUtils.equals(currentToken, oAuth2AccessTokenDO.getAccessToken()) && Boolean.FALSE.equals(oAuth2AccessTokenDO.getIsSupAdmin())) {
|
if (ObjectUtil.equal(userId, oAuth2AccessTokenDO.getUserId()) && !StringUtils.equals(currentToken, oAuth2AccessTokenDO.getAccessToken()) && Boolean.FALSE.equals(oAuth2AccessTokenDO.getIsSupAdmin())) {
|
||||||
// 用户id匹配、非当次登录token、非超管 -> 删除redis中的token缓存
|
// 用户id匹配、非当次登录token、非超管 -> 删除redis中的token缓存
|
||||||
oauth2AccessTokenRedisDAO.deleteWithKey(key);
|
oauth2AccessTokenRedisDAO.deleteWithKey(key);
|
||||||
|
// 创建踢除的token
|
||||||
|
oauth2AccessTokenRedisDAO.createKickOutToken(oAuth2AccessTokenDO.getAccessToken());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cursor.close();
|
cursor.close();
|
||||||
@@ -295,4 +291,9 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
|
|||||||
}
|
}
|
||||||
cursor.close();
|
cursor.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OAuth2AccessTokenDO getKickOutAccessToken(String token) {
|
||||||
|
return oauth2AccessTokenRedisDAO.getKickOut(token);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user