mirror of
http://git.pig4cloud.com/pig/pigx.git
synced 2026-11-12 05:39:00 +08:00
✨ Introducing new features. #2034 客户端管理 增加验证码、前端登录加密控制功能
This commit is contained in:
1 parent
8895e96f71
commit
96a5d4c3f5
14 files changed
+407
-53
No files matched your search
+50
-5
@@ -24,15 +24,23 @@ import cn.hutool.crypto.Mode;
|
||||
import cn.hutool.crypto.Padding;
|
||||
import cn.hutool.crypto.symmetric.AES;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.pig4cloud.pigx.common.core.constant.CacheConstants;
|
||||
import com.pig4cloud.pigx.common.core.constant.CommonConstants;
|
||||
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pigx.common.core.constant.enums.EncFlagTypeEnum;
|
||||
import com.pig4cloud.pigx.common.core.util.WebUtils;
|
||||
import com.pig4cloud.pigx.gateway.config.GatewayConfigProperties;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilter;
|
||||
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
|
||||
import org.springframework.cloud.gateway.filter.factory.rewrite.CachedBodyOutputMessage;
|
||||
import org.springframework.cloud.gateway.support.BodyInserterContext;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.HttpMessageReader;
|
||||
@@ -62,6 +70,8 @@ import java.util.function.Function;
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@SuppressWarnings("all")
|
||||
public class PasswordDecoderFilter extends AbstractGatewayFilterFactory {
|
||||
|
||||
private final List<HttpMessageReader<?>> messageReaders = HandlerStrategies.withDefaults().messageReaders();
|
||||
@@ -70,24 +80,31 @@ public class PasswordDecoderFilter extends AbstractGatewayFilterFactory {
|
||||
|
||||
private static final String KEY_ALGORITHM = "AES";
|
||||
|
||||
@Autowired
|
||||
private GatewayConfigProperties gatewayConfig;
|
||||
private final RedisTemplate redisTemplate;
|
||||
|
||||
private final GatewayConfigProperties gatewayConfig;
|
||||
|
||||
@Override
|
||||
public GatewayFilter apply(Object config) {
|
||||
return (exchange, chain) -> {
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
// 不是登录请求,直接向下执行
|
||||
// 1. 不是登录请求,直接向下执行
|
||||
if (!StrUtil.containsAnyIgnoreCase(request.getURI().getPath(), SecurityConstants.OAUTH_TOKEN_URL)) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
// 刷新token,直接向下执行
|
||||
// 2. 刷新token类型,直接向下执行
|
||||
String grantType = request.getQueryParams().getFirst("grant_type");
|
||||
if (StrUtil.equals(SecurityConstants.REFRESH_TOKEN, grantType)) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
// 3. 判断客户端是否需要解密,明文传输直接向下执行
|
||||
if (!isEncClient(request)) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
// 4. 前端加密密文解密逻辑
|
||||
Class inClass = String.class;
|
||||
Class outClass = String.class;
|
||||
ServerRequest serverRequest = ServerRequest.create(exchange, messageReaders);
|
||||
@@ -109,6 +126,34 @@ public class PasswordDecoderFilter extends AbstractGatewayFilterFactory {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据请求的clientId 查询客户端配置是否是加密传输
|
||||
* @param request 请求上下文
|
||||
* @return true 加密传输 、 false 原文传输
|
||||
*/
|
||||
private boolean isEncClient(ServerHttpRequest request) {
|
||||
String header = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
|
||||
String clientId = WebUtils.extractClientId(header).orElse(null);
|
||||
// 获取租户拼接区分租户的key
|
||||
String tenantId = request.getHeaders().getFirst(CommonConstants.TENANT_ID);
|
||||
String key = String.format("%s:%s:%s", StrUtil.isBlank(tenantId) ? CommonConstants.TENANT_ID_1 : tenantId,
|
||||
CacheConstants.CLIENT_FLAG, clientId);
|
||||
|
||||
redisTemplate.setKeySerializer(new StringRedisSerializer());
|
||||
Object val = redisTemplate.opsForValue().get(key);
|
||||
|
||||
// 当配置不存在时,默认需要解密
|
||||
if (val == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
JSONObject information = JSONUtil.parseObj(val.toString());
|
||||
if (StrUtil.equals(EncFlagTypeEnum.NO.getType(), information.getStr(CommonConstants.ENC_FLAG))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 原文解密
|
||||
* @return
|
||||
|
||||
+39
-25
@@ -18,6 +18,8 @@
|
||||
package com.pig4cloud.pigx.gateway.filter;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.anji.captcha.model.vo.CaptchaVO;
|
||||
import com.anji.captcha.service.CaptchaService;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
@@ -25,12 +27,11 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.pig4cloud.pigx.common.core.constant.CacheConstants;
|
||||
import com.pig4cloud.pigx.common.core.constant.CommonConstants;
|
||||
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pigx.common.core.constant.enums.LoginTypeEnum;
|
||||
import com.pig4cloud.pigx.common.core.constant.enums.CaptchaFlagTypeEnum;
|
||||
import com.pig4cloud.pigx.common.core.exception.ValidateCodeException;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
import com.pig4cloud.pigx.common.core.util.SpringContextHolder;
|
||||
import com.pig4cloud.pigx.common.core.util.WebUtils;
|
||||
import com.pig4cloud.pigx.gateway.config.GatewayConfigProperties;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -53,22 +54,21 @@ import reactor.core.publisher.Mono;
|
||||
@Slf4j
|
||||
@Component
|
||||
@AllArgsConstructor
|
||||
@SuppressWarnings("all")
|
||||
public class ValidateCodeGatewayFilter extends AbstractGatewayFilterFactory {
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
private final RedisTemplate redisTemplate;
|
||||
|
||||
private final GatewayConfigProperties gatewayConfig;
|
||||
|
||||
@Override
|
||||
public GatewayFilter apply(Object config) {
|
||||
return (exchange, chain) -> {
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
|
||||
// 不是登录请求,直接向下执行
|
||||
// 不是账号密码登录、短信登录,直接向下执行
|
||||
if (!StrUtil.containsAnyIgnoreCase(request.getURI().getPath(), SecurityConstants.OAUTH_TOKEN_URL,
|
||||
SecurityConstants.SMS_TOKEN_URL, SecurityConstants.SOCIAL_TOKEN_URL)) {
|
||||
SecurityConstants.SMS_TOKEN_URL)) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
@@ -78,25 +78,12 @@ public class ValidateCodeGatewayFilter extends AbstractGatewayFilterFactory {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
// 终端设置不校验, 直接向下执行
|
||||
// 判断客户端是否跳过检验
|
||||
if (!isCheckCaptchaClient(request)) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
try {
|
||||
String header = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
|
||||
String clientId = WebUtils.extractClientId(header).orElse(null);
|
||||
if (StrUtil.isNotBlank(clientId) && gatewayConfig.getIgnoreClients().contains(clientId)) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
// 如果是社交登录,判断是否包含SMS
|
||||
if (StrUtil.containsAnyIgnoreCase(request.getURI().getPath(), SecurityConstants.SOCIAL_TOKEN_URL)) {
|
||||
String mobile = request.getQueryParams().getFirst("mobile");
|
||||
if (StrUtil.containsAny(mobile, LoginTypeEnum.SMS.getType())) {
|
||||
throw new ValidateCodeException("验证码不合法");
|
||||
}
|
||||
else {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
}
|
||||
|
||||
// 校验验证码
|
||||
checkCode(request);
|
||||
}
|
||||
@@ -117,6 +104,34 @@ public class ValidateCodeGatewayFilter extends AbstractGatewayFilterFactory {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否需要校验客户端,根据client 查询客户端配置
|
||||
* @param request 请求
|
||||
* @return true 需要校验, false 不需要校验
|
||||
*/
|
||||
private boolean isCheckCaptchaClient(ServerHttpRequest request) {
|
||||
String header = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
|
||||
String clientId = WebUtils.extractClientId(header).orElse(null);
|
||||
// 获取租户拼接区分租户的key
|
||||
String tenantId = request.getHeaders().getFirst(CommonConstants.TENANT_ID);
|
||||
String key = String.format("%s:%s:%s", StrUtil.isBlank(tenantId) ? CommonConstants.TENANT_ID_1 : tenantId,
|
||||
CacheConstants.CLIENT_FLAG, clientId);
|
||||
|
||||
redisTemplate.setKeySerializer(new StringRedisSerializer());
|
||||
Object val = redisTemplate.opsForValue().get(key);
|
||||
|
||||
// 当配置不存在时,默认需要校验
|
||||
if (val == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
JSONObject information = JSONUtil.parseObj(val.toString());
|
||||
if (StrUtil.equals(CaptchaFlagTypeEnum.OFF.getType(), information.getStr(CommonConstants.CAPTCHA_FLAG))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查code
|
||||
* @param request
|
||||
@@ -139,7 +154,6 @@ public class ValidateCodeGatewayFilter extends AbstractGatewayFilterFactory {
|
||||
if (!captchaService.verification(vo).isSuccess()) {
|
||||
throw new ValidateCodeException("验证码不能为空");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user