Skip to content

Commit

Permalink
♻️ Refactoring code. 重构网关Form Body read 逻辑提升性能
Browse files Browse the repository at this point in the history
  • Loading branch information
lbw committed Jan 28, 2024
1 parent 44dffda commit b58d5ab
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 177 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,6 @@ public interface SecurityConstants {
*/
String FROM = "from";

/**
* 请求header
*/
String HEADER_FROM_IN = FROM + "=" + FROM_IN;

/**
* 默认登录URL
*/
Expand Down Expand Up @@ -82,11 +77,6 @@ public interface SecurityConstants {
*/
String NOOP = "{noop}";

/***
* 资源服务器默认bean名称
*/
String RESOURCE_SERVER_CONFIGURER = "resourceServerConfigurerAdapter";

/**
* 用户名
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.pig4cloud.pig.gateway.handler.GlobalExceptionHandler;
import com.pig4cloud.pig.gateway.handler.ImageCodeHandler;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.gateway.filter.factory.rewrite.ModifyRequestBodyGatewayFilterFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
Expand All @@ -20,27 +21,56 @@
@EnableConfigurationProperties(GatewayConfigProperties.class)
public class GatewayConfiguration {

/**
* 创建密码解码器过滤器
* @param modifyRequestBodyGatewayFilterFactory 修改请求体网关过滤器工厂
* @param configProperties 配置属性
* @return 密码解码器过滤器
*/
@Bean
public PasswordDecoderFilter passwordDecoderFilter(GatewayConfigProperties configProperties) {
return new PasswordDecoderFilter(configProperties);
public PasswordDecoderFilter passwordDecoderFilter(
ModifyRequestBodyGatewayFilterFactory modifyRequestBodyGatewayFilterFactory,
GatewayConfigProperties configProperties) {
return new PasswordDecoderFilter(modifyRequestBodyGatewayFilterFactory, configProperties);
}

/**
* 创建PigRequest全局过滤器
* @return PigRequest全局过滤器
*/
@Bean
public PigRequestGlobalFilter pigRequestGlobalFilter() {
return new PigRequestGlobalFilter();
}

/**
* 创建验证码网关过滤器
* @param configProperties 配置属性
* @param objectMapper 对象映射器
* @param redisTemplate Redis模板
* @return 验证码网关过滤器
*/
@Bean
public ValidateCodeGatewayFilter validateCodeGatewayFilter(GatewayConfigProperties configProperties,
ObjectMapper objectMapper, RedisTemplate redisTemplate) {
return new ValidateCodeGatewayFilter(configProperties, objectMapper, redisTemplate);
}

/**
* 创建全局异常处理程序
* @param objectMapper 对象映射器
* @return 全局异常处理程序
*/
@Bean
public GlobalExceptionHandler globalExceptionHandler(ObjectMapper objectMapper) {
return new GlobalExceptionHandler(objectMapper);
}

/**
* 创建图片验证码处理器
* @param redisTemplate Redis模板
* @return 图片验证码处理器
*/
@Bean
public ImageCodeHandler imageCodeHandler(RedisTemplate redisTemplate) {
return new ImageCodeHandler(redisTemplate);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,28 +29,14 @@
import lombok.extern.slf4j.Slf4j;
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.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.codec.HttpMessageReader;
import org.springframework.cloud.gateway.filter.factory.rewrite.ModifyRequestBodyGatewayFilterFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.HandlerStrategies;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

/**
* @author lengleng
Expand All @@ -60,107 +46,55 @@
@RequiredArgsConstructor
public class PasswordDecoderFilter extends AbstractGatewayFilterFactory {

private static final List<HttpMessageReader<?>> messageReaders = HandlerStrategies.withDefaults().messageReaders();

private static final String PASSWORD = "password";

private static final String KEY_ALGORITHM = "AES";

private final GatewayConfigProperties gatewayConfig;

static {
// 关闭hutool 强制关闭Bouncy Castle库的依赖
SecureUtil.disableBouncyCastle();
}

@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);
}

// 2. 不是密码登录模式直接跳过
String grantType = request.getQueryParams().getFirst("grant_type");
if (!StrUtil.equals(SecurityConstants.PASSWORD, grantType)) {
return chain.filter(exchange);
}

// 3. 前端加密密文解密逻辑
Class inClass = String.class;
Class outClass = String.class;
ServerRequest serverRequest = ServerRequest.create(exchange, messageReaders);

// 4. 解密生成新的报文
Mono<?> modifiedBody = serverRequest.bodyToMono(inClass).flatMap(decryptAES());

BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, outClass);
HttpHeaders headers = new HttpHeaders();
headers.putAll(exchange.getRequest().getHeaders());
headers.remove(HttpHeaders.CONTENT_LENGTH);

headers.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);
return bodyInserter.insert(outputMessage, new BodyInserterContext()).then(Mono.defer(() -> {
ServerHttpRequest decorator = decorate(exchange, headers, outputMessage);
return chain.filter(exchange.mutate().request(decorator).build());
}));
};
}

/**
* 原文解密
*
* @return
*/
private Function decryptAES() {
return s -> {
// 构建前端对应解密AES 因子
AES aes = new AES(Mode.CFB, Padding.NoPadding,
new SecretKeySpec(gatewayConfig.getEncodeKey().getBytes(), KEY_ALGORITHM),
new IvParameterSpec(gatewayConfig.getEncodeKey().getBytes()));

// 获取请求密码并解密
Map<String, String> inParamsMap = HttpUtil.decodeParamMap((String) s, CharsetUtil.CHARSET_UTF_8);
if (inParamsMap.containsKey(PASSWORD)) {
String password = aes.decryptStr(inParamsMap.get(PASSWORD));
// 返回修改后报文字符
inParamsMap.put(PASSWORD, password);
} else {
log.error("非法请求数据:{}", s);
}
return Mono.just(HttpUtil.toParams(inParamsMap, Charset.defaultCharset(), true));
};
}

/**
* 报文转换
*
* @return
*/
private ServerHttpRequestDecorator decorate(ServerWebExchange exchange, HttpHeaders headers,
CachedBodyOutputMessage outputMessage) {
return new ServerHttpRequestDecorator(exchange.getRequest()) {
@Override
public HttpHeaders getHeaders() {
long contentLength = headers.getContentLength();
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.putAll(super.getHeaders());
if (contentLength > 0) {
httpHeaders.setContentLength(contentLength);
} else {
httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
}
return httpHeaders;
}

@Override
public Flux<DataBuffer> getBody() {
return outputMessage.getBody();
}
};
}
private final ModifyRequestBodyGatewayFilterFactory modifyRequestBodyFilter;

private static final String PASSWORD = "password";

private static final String KEY_ALGORITHM = "AES";

private final GatewayConfigProperties gatewayConfig;

static {
// 关闭hutool 强制关闭Bouncy Castle库的依赖
SecureUtil.disableBouncyCastle();
}

@Override
public GatewayFilter apply(Object config) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
// 不是登录请求,直接向下执行
if (!StrUtil.containsAnyIgnoreCase(request.getURI().getPath(), SecurityConstants.OAUTH_TOKEN_URL)) {
return chain.filter(exchange);
}

return modifyRequestBodyFilter
.apply(new ModifyRequestBodyGatewayFilterFactory.Config().setRewriteFunction(String.class, String.class,
(webExchange, body) -> Mono.just(modifyRequestPassword(body))))
.filter(exchange, chain);
};
}

/**
* 修改请求报文的密码密文为名为
* @param requestBody 请求报文
* @return 修改后的报文
*/
private String modifyRequestPassword(String requestBody) {
// 构建前端对应解密AES 因子
AES aes = new AES(Mode.CFB, Padding.NoPadding,
new SecretKeySpec(gatewayConfig.getEncodeKey().getBytes(), KEY_ALGORITHM),
new IvParameterSpec(gatewayConfig.getEncodeKey().getBytes()));

// 获取请求密码并解密
Map<String, String> inParamsMap = HttpUtil.decodeParamMap(requestBody, CharsetUtil.CHARSET_UTF_8);
if (inParamsMap.containsKey(PASSWORD)) {
String password = aes.decryptStr(inParamsMap.get(PASSWORD));
// 返回修改后报文字符
inParamsMap.put(PASSWORD, password);
}

return HttpUtil.toParams(inParamsMap, Charset.defaultCharset(), true);
}

}
Loading

0 comments on commit b58d5ab

Please sign in to comment.