diff --git a/pigx-gateway/src/main/java/com/pig4cloud/pigx/gateway/filter/PasswordDecoderFilter.java b/pigx-gateway/src/main/java/com/pig4cloud/pigx/gateway/filter/PasswordDecoderFilter.java
index 033c382fd..8c500bdc2 100644
--- a/pigx-gateway/src/main/java/com/pig4cloud/pigx/gateway/filter/PasswordDecoderFilter.java
+++ b/pigx-gateway/src/main/java/com/pig4cloud/pigx/gateway/filter/PasswordDecoderFilter.java
@@ -25,76 +25,137 @@ import cn.hutool.crypto.Padding;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.http.HttpUtil;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
-import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
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.http.server.reactive.ServerHttpRequest;
+import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
-import org.springframework.web.util.UriComponentsBuilder;
+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.net.URI;
import java.nio.charset.StandardCharsets;
-import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.function.Function;
/**
* @author lengleng
- * @date 2018/7/15
+ * @date 2020/1/8
* 密码解密工具类
+ *
+ * 参考 ModifyRequestBodyGatewayFilterFactory 实现
*/
@Slf4j
@Component
public class PasswordDecoderFilter extends AbstractGatewayFilterFactory {
+ private final List> messageReaders = HandlerStrategies.withDefaults().messageReaders();
+
private static final String PASSWORD = "password";
private static final String KEY_ALGORITHM = "AES";
@Value("${security.encode.key:1234567812345678}")
private String encodeKey;
- @SneakyThrows
- private static String decryptAES(String data, String pass) {
- AES aes = new AES(Mode.CBC,Padding.NoPadding,
- new SecretKeySpec(pass.getBytes(), KEY_ALGORITHM),
- new IvParameterSpec(pass.getBytes()));
- byte[] result = aes.decrypt(Base64.decode(data.getBytes(StandardCharsets.UTF_8)));
- return new String(result, StandardCharsets.UTF_8);
- }
@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);
}
- URI uri = exchange.getRequest().getURI();
- String queryParam = uri.getRawQuery();
- Map paramMap = HttpUtil.decodeParamMap(queryParam, CharsetUtil.UTF_8);
+ Class inClass = String.class;
+ Class outClass = String.class;
+ ServerRequest serverRequest = ServerRequest.create(exchange,
+ messageReaders);
- String password = paramMap.get(PASSWORD);
- if (StrUtil.isNotBlank(password)) {
- try {
- password = decryptAES(password, encodeKey);
- } catch (Exception e) {
- log.error("密码解密失败:{}", password);
- return Mono.error(e);
+ // 解密生成新的报文
+ 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.CBC, Padding.NoPadding,
+ new SecretKeySpec(encodeKey.getBytes(), KEY_ALGORITHM),
+ new IvParameterSpec(encodeKey.getBytes()));
+
+ // 获取请求密码并解密
+ HashMap inParamsMap = HttpUtil.decodeParamMap((String) s, CharsetUtil.UTF_8);
+ byte[] result = aes.decrypt(Base64.decode(inParamsMap.get(PASSWORD)
+ .getBytes(StandardCharsets.UTF_8)));
+ String password = new String(result, StandardCharsets.UTF_8);
+
+ // 返回修改后报文字符
+ inParamsMap.put(PASSWORD, password.trim());
+ return Mono.just(HttpUtil.toParams(inParamsMap));
+ };
+ }
+
+ /**
+ * 报文转换
+ *
+ * @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");
}
- paramMap.put(PASSWORD, password.trim());
+ return httpHeaders;
}
- URI newUri = UriComponentsBuilder.fromUri(uri)
- .replaceQuery(HttpUtil.toParams(paramMap))
- .build(true)
- .toUri();
-
- ServerHttpRequest newRequest = exchange.getRequest().mutate().uri(newUri).build();
- return chain.filter(exchange.mutate().request(newRequest).build());
+ @Override
+ public Flux getBody() {
+ return outputMessage.getBody();
+ }
};
}
}