diff --git a/pig-gateway/src/main/java/com/pig4cloud/pig/gateway/filter/PasswordDecoderFilter.java b/pig-gateway/src/main/java/com/pig4cloud/pig/gateway/filter/PasswordDecoderFilter.java index eebf13e9..d94e9c0e 100755 --- a/pig-gateway/src/main/java/com/pig4cloud/pig/gateway/filter/PasswordDecoderFilter.java +++ b/pig-gateway/src/main/java/com/pig4cloud/pig/gateway/filter/PasswordDecoderFilter.java @@ -16,9 +16,9 @@ package com.pig4cloud.pig.gateway.filter; -import cn.hutool.core.codec.Base64; -import cn.hutool.core.text.CharSequenceUtil; +import cn.hutool.core.net.URLEncodeUtil; import cn.hutool.core.util.CharsetUtil; +import cn.hutool.core.util.StrUtil; import cn.hutool.crypto.Mode; import cn.hutool.crypto.Padding; import cn.hutool.crypto.symmetric.AES; @@ -29,15 +29,28 @@ import lombok.RequiredArgsConstructor; 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.http.server.reactive.ServerHttpRequest; -import org.springframework.web.util.UriComponentsBuilder; +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.net.URI; -import java.nio.charset.StandardCharsets; +import java.nio.charset.Charset; +import java.util.List; import java.util.Map; +import java.util.function.Function; /** * @author lengleng @@ -47,52 +60,102 @@ import java.util.Map; @RequiredArgsConstructor public class PasswordDecoderFilter extends AbstractGatewayFilterFactory { + private static final List> messageReaders = HandlerStrategies.withDefaults().messageReaders(); + private static final String PASSWORD = "password"; private static final String KEY_ALGORITHM = "AES"; - private final GatewayConfigProperties configProperties; + private final GatewayConfigProperties gatewayConfig; @Override public GatewayFilter apply(Object config) { return (exchange, chain) -> { ServerHttpRequest request = exchange.getRequest(); - - // 不是登录请求,直接向下执行 - if (!CharSequenceUtil.containsAnyIgnoreCase(request.getURI().getPath(), - SecurityConstants.OAUTH_TOKEN_URL)) { + // 1. 不是登录请求,直接向下执行 + 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.CHARSET_UTF_8); - - String password = paramMap.get(PASSWORD); - if (CharSequenceUtil.isNotBlank(password)) { - try { - password = decrypt(password, configProperties.getEncodeKey()); - } - catch (Exception e) { - log.error("密码解密失败:{}", password); - return Mono.error(e); - } - paramMap.put(PASSWORD, password.trim()); + // 2. 刷新token类型,直接向下执行 + String grantType = request.getQueryParams().getFirst("grant_type"); + if (StrUtil.equals(SecurityConstants.REFRESH_TOKEN, grantType)) { + return chain.filter(exchange); } - URI newUri = UriComponentsBuilder.fromUri(uri).replaceQuery(HttpUtil.toParams(paramMap)).build(true) - .toUri(); + // 3. 前端加密密文解密逻辑 + Class inClass = String.class; + Class outClass = String.class; + ServerRequest serverRequest = ServerRequest.create(exchange, messageReaders); - ServerHttpRequest newRequest = exchange.getRequest().mutate().uri(newUri).build(); - return chain.filter(exchange.mutate().request(newRequest).build()); + // 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()); + })); }; } - private static String decrypt(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); + /** + * 原文解密 + * @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 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 getBody() { + return outputMessage.getBody(); + } + }; } }