| 
 | 
 
本文章实现的是 网关中的 参数解密、响应数据体加密功能。 
 
  
 
1 集成 commons-codec 
 
commons-codec 是Apache开源组织提供的用于摘要运算、编码解码的包。常见的编码解码工具Base64、MD5、Hex、SHA1、DES等。 
   <dependency> 
       <groupId>commons-codec</groupId> 
       <artifactId>commons-codec</artifactId> 
       <version>1.15</version> 
   </dependency>本项目中集成RSA 非对称算法,RSAUtils 工具类 
import lombok.extern.slf4j.Slf4j; 
import org.apache.commons.codec.binary.Base64; 
 
import javax.crypto.BadPaddingException; 
import javax.crypto.Cipher; 
import javax.crypto.IllegalBlockSizeException; 
import java.nio.charset.StandardCharsets; 
import java.security.KeyFactory; 
import java.security.KeyPair; 
import java.security.KeyPairGenerator; 
import java.security.SecureRandom; 
import java.security.interfaces.RSAPrivateKey; 
import java.security.interfaces.RSAPublicKey; 
import java.security.spec.PKCS8EncodedKeySpec; 
import java.security.spec.X509EncodedKeySpec; 
import java.util.Arrays; 
import java.util.HashMap; 
import java.util.Map; 
 
@Slf4j 
public class RSAUtils { 
 
    public static final String PUBLIC_KEY = &#34;public_key&#34;; 
 
    public static final String PRIVATE_KEY = &#34;private_key&#34;; 
 
 
    public static Map<String, String> generateRasKey() { 
        Map<String, String> rs = new HashMap<>(); 
        try { 
            // KeyPairGenerator类用于生成公钥和私钥对,基于RSA算法生成对象 
            KeyPairGenerator keyPairGen = null; 
            keyPairGen = KeyPairGenerator.getInstance(&#34;RSA&#34;); 
            keyPairGen.initialize(1024, new SecureRandom()); 
            // 生成一个密钥对,保存在keyPair中 
            KeyPair keyPair = keyPairGen.generateKeyPair(); 
            // 得到私钥 公钥 
            RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); 
            RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); 
            String publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded())); 
            // 得到私钥字符串 
            String privateKeyString = new String(Base64.encodeBase64((privateKey.getEncoded()))); 
            // 将公钥和私钥保存到Map 
            rs.put(PUBLIC_KEY, publicKeyString); 
            rs.put(PRIVATE_KEY, privateKeyString); 
        } catch (Exception e) { 
            throw new RuntimeException(&#34;RsaUtils 生成公钥失败...&#34;); 
        } 
        return rs; 
    } 
 
 
    public static String encrypt(String str, String publicKey) { 
        try { 
            //base64编码的公钥 
            byte[] decoded = Base64.decodeBase64(publicKey); 
            RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance(&#34;RSA&#34;).generatePublic(new X509EncodedKeySpec(decoded)); 
            //RSA加密 
            Cipher cipher = Cipher.getInstance(&#34;RSA&#34;); 
            cipher.init(Cipher.ENCRYPT_MODE, pubKey); 
            //当长度过长的时候,需要分割后加密 117个字节 
            byte[] resultBytes = getMaxResultEncrypt(str, cipher); 
            return Base64.encodeBase64String(resultBytes); 
        } catch (Exception e) { 
            log.error(&#34;加密失败:&#34;+e.getMessage()); 
            throw new RuntimeException(&#34;RsaUtils 加密失败&#34;); 
        } 
    } 
 
    private static byte[] getMaxResultEncrypt(String str,  Cipher cipher) throws IllegalBlockSizeException, BadPaddingException { 
        byte[] inputArray = str.getBytes(StandardCharsets.UTF_8); 
        int inputLength = inputArray.length; 
        log.info(&#34;{}|加密字节数|inputLength:{}&#34;,str, inputLength); 
        // 最大加密字节数,超出最大字节数需要分组加密 
        int MAX_ENCRYPT_BLOCK = 117; 
        // 标识 
        int offSet = 0; 
        byte[] resultBytes = {}; 
        byte[] cache = {}; 
        while (inputLength - offSet > 0) { 
            if (inputLength - offSet > MAX_ENCRYPT_BLOCK) { 
                cache = cipher.doFinal(inputArray, offSet, MAX_ENCRYPT_BLOCK); 
                offSet += MAX_ENCRYPT_BLOCK; 
            } else { 
                cache = cipher.doFinal(inputArray, offSet, inputLength - offSet); 
                offSet = inputLength; 
            } 
            resultBytes = Arrays.copyOf(resultBytes, resultBytes.length + cache.length); 
            System.arraycopy(cache, 0, resultBytes, resultBytes.length - cache.length, cache.length); 
        } 
        return resultBytes; 
    } 
    public static String decrypt(String str, String privateKey) { 
 
        try { 
            //64位解码加密后的字符串 
            byte[] inputByte = Base64.decodeBase64(str.getBytes(StandardCharsets.UTF_8)); 
            //base64编码的私钥 
            byte[] decoded = Base64.decodeBase64(privateKey); 
            RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance(&#34;RSA&#34;).generatePrivate(new PKCS8EncodedKeySpec(decoded)); 
            //RSA解密 
            Cipher cipher = Cipher.getInstance(&#34;RSA&#34;); 
            cipher.init(Cipher.DECRYPT_MODE, priKey); 
            return new String(cipher.doFinal(inputByte)); 
        } catch (Exception e) { 
            log.error(&#34;解密失败:&#34;+e.getMessage()); 
            throw new RuntimeException(&#34;RsaUtils 解密失败.&#34;); 
        } 
    } 
}然后创建一个测试类,生成一组公钥与私钥: 随机生成的公钥为: 
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCcGdZDJOJKjcfx0zlMJxAzcZb6Hozm51L+MCyvUGsa1jaz4NVkvKsdaVny3PcGDM/DUp6tR4rtzTLDG9QX/yQI32+L4dA9xhQIvizdQxFSwj/7rJ2ecze2MHTqRCjzhQqKuWGuf/lXGlbhXY/Uf9Nn+ZJBVsdKrXPzBPpLuadn5QIDAQAB随机生成的私钥为: 
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJwZ1kMk4kqNx/HTOUwnEDNxlvoejObnUv4wLK9QaxrWNrPg1WS8qx1pWfLc9wYMz8NSnq1Hiu3NMsMb1Bf/JAjfb4vh0D3GFAi+LN1DEVLCP/usnZ5zN7YwdOpEKPOFCoq5Ya5/+VcaVuFdj9R/02f5kkFWx0qtc/ME+ku5p2flAgMBAAECgYAUQP3zTvvViePhf/M1QEmdLdCAZNUKDgWkrtd9am/F1vmDXq68GAa+atxIOLIMej5oLMt4gYndz6bAeKyM7dvc3dGRZbVTR5lhYVj0nlPYwky90ZxruhRuEzIBY01yXj2HWoUq/7+dSmxKOASYDW+yKIUuE/4tZhoWZR0b24t42QJBAPb1NWe/zakFzHiTTbrffv9djLgeIuqar7B5pnZRnm/53otlsnLfDOkRLgCHnOHQp/xiHDpUtbfnxBKnx5skWnMCQQCh0QGKOCXdXzXyo1srX9Ya6LEd+gNgTpXBOn1Y3WdQ1p7kNZTcZJ61XodW4tgACv24NJUmWtEKwe/9PE8SteZHAkBy8xYlsaCf4SQYp7ARoMAzSy8Z8GUeQFwwz58NCdaulmbhCbgzQeF3htibxIPglEfs8RnkiNOAw69/Y3tEmnpDAkB6/rii7OarCzGgSlaD84Z0UaY+2Mg0LcdaZjDcmP1szpVbdPa/RqPzy/QnMKlp7vDHUQCFdMYr3RmjbHHWEPkFAkEA0e7TdHheSqyAnpy8TEXMJsmMHW/37RIVtY0OeQZz9TuXG6TtsjZIna0QviCFQtxg9Zz3oRfDIoM3IrasuDFrRA==然后保存在一个类中 
public class RSAConstant { 
    //私钥 
    public static final String PRIVATE_KEY = &#34;&#34;; 
    //公钥 
    public static final String PUBLICK_KEY = &#34;&#34;; 
} 
2 网关项目中创建 RequestEncryptFilter 
 
RequestEncryptFilter 在过滤器中获取请求的参数,解密后再将参数设置回去 
import com.alibaba.cloud.commons.lang.StringUtils; 
import com.biglead.common.utils.RSAUtils; 
import lombok.extern.slf4j.Slf4j; 
import org.springframework.cloud.gateway.filter.GatewayFilterChain; 
import org.springframework.cloud.gateway.filter.GlobalFilter; 
import org.springframework.cloud.gateway.filter.factory.rewrite.CachedBodyOutputMessage; 
import org.springframework.cloud.gateway.support.BodyInserterContext; 
import org.springframework.context.annotation.Configuration; 
import org.springframework.core.Ordered; 
import org.springframework.core.io.buffer.DataBuffer; 
import org.springframework.http.HttpHeaders; 
import org.springframework.http.HttpMethod; 
import org.springframework.http.MediaType; 
import org.springframework.http.ReactiveHttpOutputMessage; 
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 java.lang.reflect.Field; 
import java.net.URI; 
 
 
@Configuration 
@Slf4j 
public class RequestEncryptFilter implements GlobalFilter, Ordered { 
 
 
    @Override 
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { 
 
        log.info(&#34;============================RequestEncryptFilter start===================================&#34;); 
 
 
        ServerHttpRequest request = exchange.getRequest(); 
        URI uri = request.getURI(); 
        //请求方法 
        HttpMethod method = request.getMethod(); 
        MediaType mediaType = request.getHeaders().getContentType(); 
 
        String  sign = request.getHeaders().getFirst(&#34;encrypt&#34;); 
        if(StringUtils.isEmpty(sign)){ 
            log.info(&#34;不需要解密 &#34;); 
            return chain.filter(exchange); 
        } 
        log.info(&#34;需要解密数据 &#34;); 
 
        if (method == HttpMethod.GET) { 
            //1 修改请求参数,并获取请求参数 
            try { 
                updateRequestParam(exchange); 
            } catch (Exception e) { 
                return MonoUtils.invalidUrl(exchange); 
            } 
        } 
 
        if (method != HttpMethod.POST) { 
            log.info(&#34;非POST请求 不需要解密 &#34;); 
            return chain.filter(exchange); 
        } 
        //2 获取请求体,修改请求体 
        ServerRequest serverRequest = ServerRequest.create(exchange, HandlerStrategies.withDefaults().messageReaders()); 
 
        Mono<String> modifiedBody = serverRequest.bodyToMono(String.class).flatMap(body -> { 
            //解密请求体 
            String encrypt = RSAUtils.decrypt(body, RSAConstant.PRIVATE_KEY); 
            return Mono.just(encrypt); 
        }); 
 
        //3 创建BodyInserter修改请求体 
        BodyInserter<Mono<String>, ReactiveHttpOutputMessage> bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class); 
        HttpHeaders headers = new HttpHeaders(); 
        headers.putAll(exchange.getRequest().getHeaders()); 
        headers.remove(HttpHeaders.CONTENT_LENGTH); 
        //4 创建CachedBodyOutputMessage并且把请求param加入 
        CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers); 
        return bodyInserter.insert(outputMessage, new BodyInserterContext()).then(Mono.defer(() -> { 
            ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator(exchange.getRequest()) { 
                @Override 
                public Flux<DataBuffer> getBody() { 
                    return outputMessage.getBody(); 
                } 
            }; 
            return chain.filter(exchange.mutate().request(decorator).build()); 
        })); 
 
    } 
 
    /** 
     * 修改前端传的参数 
     */ 
    private void updateRequestParam(ServerWebExchange exchange) throws NoSuchFieldException, IllegalAccessException { 
        ServerHttpRequest request = exchange.getRequest(); 
        //请求链接 
        URI uri = request.getURI(); 
        //请求参数 
        String query = uri.getQuery(); 
        //判断是否有加密的参数 这里的约定是 param 
        if (StringUtils.isNotBlank(query) && query.contains(&#34;param&#34;)) { 
            String[] split = query.split(&#34;=&#34;); 
            String paramValue = split[1]; 
            //解密请求参数 
            String param = RSAUtils.decrypt(paramValue, RSAConstant.PRIVATE_KEY); 
            //使用反射强行拿出 URI 的 query 
            Field targetQuery = uri.getClass().getDeclaredField(&#34;query&#34;); 
            //授权 
            targetQuery.setAccessible(true); 
            //重新设置参数 
            targetQuery.set(uri, param); 
        } 
    } 
 
 
    @Override 
    public int getOrder() { 
        return -1; 
    } 
} 
3 ResponseEncryptFilter 响应数据加密 
 
ResponseEncryptFilter 主要是用来实现对响应数据体的加密 ,所以这里实现的思路是 : 
 
- 获取响应体数据
 
 - 获取加密标识 encrypt
 
 - 加密
 
  import com.alibaba.fastjson.JSON; 
import com.biglead.common.utils.RSAUtils; 
import com.google.common.base.Charsets; 
import lombok.extern.slf4j.Slf4j; 
import org.reactivestreams.Publisher; 
import org.springframework.cloud.gateway.filter.GatewayFilterChain; 
import org.springframework.cloud.gateway.filter.GlobalFilter; 
import org.springframework.context.annotation.Configuration; 
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.HttpStatus; 
import org.springframework.http.MediaType; 
import org.springframework.http.server.reactive.ServerHttpRequest; 
import org.springframework.http.server.reactive.ServerHttpResponse; 
import org.springframework.http.server.reactive.ServerHttpResponseDecorator; 
import org.springframework.web.server.ServerWebExchange; 
import reactor.core.publisher.Flux; 
import reactor.core.publisher.Mono; 
 
import java.net.URI; 
import java.util.Map; 
import java.util.Objects; 
 
@Configuration 
@Slf4j 
public class ResponseEncryptFilter implements GlobalFilter, Ordered { 
 
    @Override 
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { 
        log.info(&#34;============================ResponseEncryptFilter start===================================&#34;); 
 
        ServerHttpRequest request = exchange.getRequest(); 
        URI uri = request.getURI(); 
        String url = uri.getPath(); 
 
        HttpStatus statusCode = exchange.getResponse().getStatusCode(); 
        if(Objects.equals(statusCode, HttpStatus.BAD_REQUEST) || Objects.equals(statusCode, HttpStatus.TOO_MANY_REQUESTS)){ 
            // 如果是特殊的请求,已处理响应内容,这里不再处理 
            return chain.filter(exchange); 
        } 
 
        // 根据具体业务内容,修改响应体 
        return modifyResponseBody(exchange, chain); 
    } 
 
    /** 
     * 修改响应体 
     * @param exchange 
     * @param chain 
     * @return 
     */ 
    private Mono<Void> modifyResponseBody(ServerWebExchange exchange, GatewayFilterChain chain)  { 
        ServerHttpResponse originalResponse = exchange.getResponse(); 
        originalResponse.getHeaders().setContentType(MediaType.APPLICATION_JSON); 
        DataBufferFactory bufferFactory = originalResponse.bufferFactory(); 
        ServerHttpResponseDecorator response = buildResponse(originalResponse, bufferFactory); 
        return chain.filter(exchange.mutate().response(response).build()); 
    } 
 
 
    @Override 
    public int getOrder() { 
        return -1; 
    } 
    private ServerHttpResponseDecorator buildResponse(ServerHttpResponse originalResponse, DataBufferFactory bufferFactory) { 
        return new ServerHttpResponseDecorator(originalResponse) { 
            @Override 
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) { 
                if (getStatusCode().equals(HttpStatus.OK) && body instanceof Flux) { 
                    Flux<? extends DataBuffer> fluxBody = Flux.from(body); 
                    return super.writeWith(fluxBody.buffer().map(dataBuffers -> { 
                        DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory(); 
                        DataBuffer join = dataBufferFactory.join(dataBuffers); 
                        byte[] content = new byte[join.readableByteCount()]; 
                        join.read(content); 
                        DataBufferUtils.release(join); 
                        // 流转为字符串 
                        String responseData = new String(content, Charsets.UTF_8); 
                        System.out.println(responseData); 
 
                        Map map = JSON.parseObject(responseData); 
                        //处理返回的数据 
                        Object encrypt = map.get(&#34;encrypt&#34;); 
                        if(encrypt!=null){ 
                            log.info(&#34;加密响应数据 开始 :{}&#34;,responseData); 
                            //加密数据 
                            responseData = RSAUtils.encrypt(responseData,RSAConstant.PUBLICK_KEY); 
                            log.info(&#34;加密响应数据 完成 :{}&#34;,responseData); 
                        } 
 
                        byte[] uppedContent = responseData.getBytes(Charsets.UTF_8); 
                        originalResponse.getHeaders().setContentLength(uppedContent.length); 
                        return bufferFactory.wrap(uppedContent); 
                    })); 
                } else { 
                    log.error(&#34;获取响应体数据 :&#34;+getStatusCode()); 
                } 
                return super.writeWith(body); 
            } 
 
            @Override 
            public Mono<Void> writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> body) { 
                return writeWith(Flux.from(body).flatMapSequential(p -> p)); 
            } 
        }; 
    } 
} 
测试获取订单详情数据 - 未加密的数据  
 
  
 
测试获取加密的订单数据  
 
  
 对应的订单服务中的控制器 
@Slf4j 
@RestController 
@RequestMapping(&#34;/order&#34;) 
public class OrderController { 
 
    @Resource 
    private OrderService orderService; 
 
     @Value(&#34;${server.port}&#34;) 
     private String serverPort; 
 
    /** 
     * @param id 订单id 
     * @return 用户 
     */ 
    @GetMapping(value = &#34;/{id}&#34;) 
    public OrderInfo queryById(@PathVariable(&#34;id&#34;) Long id) { 
        log.info(&#34;查询订单信息 port {}&#34;,serverPort); 
        return orderService.queryById(id); 
    } 
 
    /** 
     * 返回加密的请求体 
     * @param id 
     * @return 
     */ 
    @GetMapping(value = &#34;/encrypt/{id}&#34;) 
    public Result queryEncryptById(@PathVariable(&#34;id&#34;) Long id) { 
        log.info(&#34;查询订单信息 port {}&#34;,serverPort); 
        OrderInfo orderInfo = orderService.queryById(id); 
        return Result.okEncryptData(orderInfo); 
    } 
} |   
 
 
 
 |