gateway對請求引數加解密
阿新 • • 發佈:2021-06-24
1.需要將body複製到exchange,便於之後使用
import io.netty.buffer.UnpooledByteBufAllocator; import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferUtils; import org.springframework.core.io.buffer.NettyDataBufferFactory; import org.springframework.http.HttpHeaders; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpRequestDecorator; import org.springframework.stereotype.Component; 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; @Slf4j @Component public class GlobalCacheRequestBodyFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { log.info("GlobalCacheRequestBodyFilter ..."); // // 將 request body 中的內容 copy 一份,記錄到 exchange 的一個自定義屬性中 // Object cachedRequestBodyObject = exchange.getAttributeOrDefault(ConstantFilter.CACHED_REQUEST_BODY_OBJECT_KEY, null); // // 如果已經快取過,略過 // if (cachedRequestBodyObject != null) { // return chain.filter(exchange); // } // // 如果沒有快取過,獲取位元組陣列存入 exchange 的自定義屬性中 // return DataBufferUtils.join(exchange.getRequest().getBody()) // .map(dataBuffer -> { // byte[] bytes = new byte[dataBuffer.readableByteCount()]; // dataBuffer.read(bytes); // DataBufferUtils.release(dataBuffer); // return bytes; // }).defaultIfEmpty(new byte[0]) // .doOnNext(bytes -> exchange.getAttributes().put(ConstantFilter.CACHED_REQUEST_BODY_OBJECT_KEY, bytes)) // .then(chain.filter(exchange)); // ServerHttpRequest serverHttpRequest = exchange.getRequest(); String method = serverHttpRequest.getMethodValue(); String contentType = serverHttpRequest.getHeaders().getFirst(HttpHeaders.CONTENT_TYPE); // 將 request body 中的內容 copy 一份,記錄到 exchange 的一個自定義屬性中 Object cachedRequestBodyObject = exchange.getAttributeOrDefault(ConstantFilter.CACHED_REQUEST_BODY_OBJECT_KEY, null); // 如果已經快取過,略過 if (cachedRequestBodyObject != null) { return chain.filter(exchange); } if ("POST".equalsIgnoreCase(method)) { ServerRequest serverRequest = ServerRequest.create(exchange, HandlerStrategies.withDefaults().messageReaders()); Mono<String> bodyToMono = serverRequest.bodyToMono(String.class).defaultIfEmpty(""); try { return bodyToMono.flatMap(body -> { exchange.getAttributes().put(ConstantFilter.CACHED_REQUEST_BODY_OBJECT_KEY, body); ServerHttpRequest newRequest = new ServerHttpRequestDecorator(serverHttpRequest) { @Override public HttpHeaders getHeaders() { HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.putAll(super.getHeaders()); httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked"); return httpHeaders; } @Override public Flux<DataBuffer> getBody() { NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(new UnpooledByteBufAllocator(false)); DataBuffer bodyDataBuffer = nettyDataBufferFactory.wrap(body.getBytes()); return Flux.just(bodyDataBuffer); } }; return chain.filter(exchange.mutate().request(newRequest).build()); }); }catch (Exception e){ log.info("post body filter err!"); log.info(e.getMessage(),e); } } return chain.filter(exchange); } @Override public int getOrder() { return -30; } }
2.請求引數解密
import cn.hutool.core.collection.CollectionUtil; import com.alibaba.fastjson.JSONObject; import com.msl.mafa.common.BaseCode; import com.msl.mafa.util.AESCBC; import com.msl.mafa.util.RSAUtil; import com.msl.mafa.utils.BusinessException; import lombok.extern.slf4j.Slf4j; import okhttp3.internal.http2.ErrorCode; import org.apache.commons.lang3.StringUtils; import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferFactory; import org.springframework.http.HttpHeaders; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpRequestDecorator; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.net.URI; import java.util.List; import java.util.Map; @Slf4j @Component public class AppReqDecryptFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 設定是否加密標識 List<String> paramEncryptHeaders = exchange.getRequest().getHeaders().get(ConstantFilter.PARAM_ENCRYPT); if(CollectionUtil.isEmpty(paramEncryptHeaders)||!ConstantFilter.REQ_RES_ENCRYPT.equals(paramEncryptHeaders.get(0))){ return chain.filter(exchange); } exchange.getAttributes().put(ConstantFilter.PARAM_ENCRYPT, paramEncryptHeaders.get(0)); // 獲取請求的方法 ServerHttpRequest oldRequest = exchange.getRequest(); String method = oldRequest.getMethodValue(); URI uri = oldRequest.getURI(); ServerHttpRequest serverHttpRequest = exchange.getRequest(); String contentType = serverHttpRequest.getHeaders().getFirst(HttpHeaders.CONTENT_TYPE); if ("POST".equals(method)&&"application/json".equals(contentType)){ // 嘗試從 exchange 的自定義屬性中取出快取到的 body Object cachedRequestBodyObject = exchange.getAttributeOrDefault(ConstantFilter.CACHED_REQUEST_BODY_OBJECT_KEY, null); if (cachedRequestBodyObject != null) { byte[] decrypBytes; try { // byte[] body = (byte[]) cachedRequestBodyObject; String rootData = (String) cachedRequestBodyObject; // 客戶端傳過來的資料 // decrypBytes = rootData.getBytes(); JSONObject jsonObject = JSONObject.parseObject(rootData); String encryptKey = (String) jsonObject.get("k"); String encryptData = (String) jsonObject.get("v"); String key = RSAUtil.decode(encryptKey); String decryptData = AESCBC.decrypt(encryptData, key); decrypBytes = decryptData.getBytes(); }catch (Exception e){ log.error("客戶端資料解析異常:{}", e.toString()); log.error(e.getMessage(),e); throw new BusinessException(BaseCode.BAD_REQUEST.getCode(), "客戶端資料解析異常"); } // 根據解密後的引數重新構建請求 DataBufferFactory dataBufferFactory = exchange.getResponse().bufferFactory(); Flux<DataBuffer> bodyFlux = Flux.just(dataBufferFactory.wrap(decrypBytes)); ServerHttpRequest newRequest = oldRequest.mutate().uri(uri).build(); newRequest = new ServerHttpRequestDecorator(newRequest) { @Override public Flux<DataBuffer> getBody() { return bodyFlux; } }; // 構建新的請求頭 HttpHeaders headers = new HttpHeaders(); headers.putAll(exchange.getRequest().getHeaders()); // 由於修改了傳遞引數,需要重新設定CONTENT_LENGTH,長度是位元組長度,不是字串長度 int length = decrypBytes.length; headers.remove(HttpHeaders.CONTENT_LENGTH); headers.setContentLength(length); headers.set(ConstantFilter.PARAM_ENCRYPT,ConstantFilter.REQ_RES_ENCRYPT); // headers.set(HttpHeaders.CONTENT_TYPE, "application/json"); newRequest = new ServerHttpRequestDecorator(newRequest) { @Override public HttpHeaders getHeaders() { return headers; } }; // 把解密後的資料重置到exchange自定義屬性中,在之後的日誌GlobalLogFilter從此處獲取請求引數列印日誌 exchange.getAttributes().put(ConstantFilter.CACHED_REQUEST_BODY_OBJECT_KEY, decrypBytes); exchange.getAttributes().put(ConstantFilter.PARAM_ENCRYPT,ConstantFilter.REQ_RES_ENCRYPT); return chain.filter(exchange.mutate().request(newRequest).build()); } }else if("GET".equals(method)){ // todo 暫不處理 Map requestQueryParams = oldRequest.getQueryParams(); return chain.filter(exchange); } return chain.filter(exchange); } @Override public int getOrder() { return -22; } }
3.返回引數加密
import com.alibaba.fastjson.JSONObject; import com.msl.mafa.util.AESCBC; import com.msl.mafa.util.RSAUtil; import org.reactivestreams.Publisher; import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferFactory; import org.springframework.core.io.buffer.DataBufferUtils; import org.springframework.core.io.buffer.DefaultDataBufferFactory; import org.springframework.http.HttpHeaders; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.http.server.reactive.ServerHttpResponseDecorator; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.nio.charset.Charset; import java.util.List; @Component public class AppRespEncryptFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // String str=exchange.getAttribute(ConstantFilter.PARAM_ENCRYPT); List<String> paramEncryptHeaders = exchange.getRequest().getHeaders().get(ConstantFilter.PARAM_ENCRYPT); // String bgDebug = exchange.getAttributeOrDefault(ConstantFilter.PARAM_ENCRYPT, ConstantFilter.REQ_RES_NOT_ENCRYPT); String bgDebug =paramEncryptHeaders!=null?paramEncryptHeaders.get(0):ConstantFilter.REQ_RES_NOT_ENCRYPT; ServerHttpResponse originalResponse = exchange.getResponse(); DataBufferFactory bufferFactory = originalResponse.bufferFactory(); ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) { @Override public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) { if(!ConstantFilter.REQ_RES_ENCRYPT.equals(bgDebug)){ return super.writeWith(body); } if (body instanceof Flux) { Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body; return super.writeWith(fluxBody.buffer().map(dataBuffer -> { DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory(); DataBuffer join = dataBufferFactory.join(dataBuffer); byte[] content = new byte[join.readableByteCount()]; join.read(content); //釋放掉記憶體 DataBufferUtils.release(join); // 正常返回的資料 String rootData = new String(content, Charset.forName("UTF-8")); byte[] respData = rootData.getBytes(); if(ConstantFilter.REQ_RES_ENCRYPT.equals(bgDebug)){ // 對資料進行加密 // String randomKey = AESCBC.getRandomKey(); String encryptData = AESCBC.encrypt(rootData); String encryptRandomKey = RSAUtil.encrypt("Manulife123"); JSONObject json = new JSONObject(); json.put("k", encryptRandomKey); json.put("v", encryptData); respData = json.toJSONString().getBytes(); } // 加密後的資料返回給客戶端 byte[] uppedContent = new String(respData, Charset.forName("UTF-8")).getBytes(); originalResponse.getHeaders().setContentLength(uppedContent.length); return bufferFactory.wrap(uppedContent); })); } return super.writeWith(body); } }; return chain.filter(exchange.mutate().response(decoratedResponse).build()); } @Override public int getOrder() { return -25; } }
4.注意事項
1)多個filter的載入順序 |
---|
2)加解密之後CONTENT_LENGTH長度變化需要重置 |