欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringCloud?Gateway實現(xiàn)請求解密和響應(yīng)加密的過程解析

 更新時間:2023年11月03日 17:11:04   作者:你家寶寶  
這篇文章主要介紹了SpringCloud?Gateway實現(xiàn)請求解密和響應(yīng)加密的相關(guān)知識,本文環(huán)境使用比較新的?Java?17?和?SpringBoot?3.1.5,對應(yīng)到Spring的版本是?6.0.13,本文重心是網(wǎng)關(guān)項目,需要的朋友可以參考下

前言

本文環(huán)境使用比較新的 Java 17 和 SpringBoot 3.1.5,對應(yīng)到Spring的版本是 6.0.13
使用到的三方插件有:

  • lombok
  • gson
  • hutool

本文注重實現(xiàn)請求的解密和響應(yīng)的加密,加解密使用的是 Hutool 中的工具類,加解密算法目前提供了AES的方式,其余方式也可兼容擴展。
完整代碼倉庫:https://gitee.com/fengsoshuai/springcloud-gateway-feng-demo

借用網(wǎng)關(guān)中的過濾器GlobalFilter來實現(xiàn)這一功能。
本文只粘貼一些重點文件內(nèi)容。

正文

一、項目簡介

在聚合項目中,有兩個核心模塊,feng-server提供了 rest 接口,供網(wǎng)關(guān)使用。
feng-gateway 是核心實現(xiàn)的網(wǎng)關(guān)項目,實現(xiàn)了自定義過濾器,以及增加了一些基本配置功能。本文重心是網(wǎng)關(guān)項目。

二、核心代碼

2.1 自定義過濾器

package org.feng.fenggateway.filters;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.feng.fenggateway.config.SecureProperties;
import org.feng.fenggateway.dto.ResponseDto;
import org.feng.fenggateway.secure.SecureComponent;
import org.feng.fenggateway.secure.SecureComponentFactory;
import org.feng.fenggateway.util.GsonUtil;
import org.reactivestreams.Publisher;
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.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
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.StandardCharsets;
import java.util.Objects;
import java.util.Set;
/**
 * 自定義密文過濾器
 *
 * @author feng
 */
@Slf4j
@Component
public class CustomCipherTextFilter implements GlobalFilter, Ordered {
    @Resource
    private SecureProperties secureProperties;
    private SecureComponent secureComponent;
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 獲取請求體
        ServerHttpRequest request = exchange.getRequest();
        // 獲取響應(yīng)體
        ServerHttpResponse response = exchange.getResponse();
        // 請求頭
        HttpHeaders headers = request.getHeaders();
        // 請求方法
        HttpMethod method = request.getMethod();
        // 滿足條件,進行過濾
        if (isNeedFilterMethod(method) && isNeedFilterContentType(headers.getContentType())) {
            return DataBufferUtils.join(request.getBody())
                    .flatMap(dataBuffer -> {
                        try {
                            // 獲取請求參數(shù)
                            String originalRequestBody = getOriginalRequestBody(dataBuffer);
                            // 解密請求參數(shù)
                            String decryptRequestBody = decryptRequest(originalRequestBody);
                            // 裝飾新的請求體
                            ServerHttpRequestDecorator requestDecorator = serverHttpRequestDecorator(request, decryptRequestBody);
                            // 裝飾新的響應(yīng)體
                            ServerHttpResponseDecorator responseDecorator = serverHttpResponseDecorator(response);
                            // 使用新的請求和響應(yīng)轉(zhuǎn)發(fā)
                            ServerWebExchange serverWebExchange = exchange.mutate().request(requestDecorator).response(responseDecorator).build();
                            // 放行攔截
                            return chain.filter(serverWebExchange);
                        } catch (Exception e) {
                            log.error("密文過濾器加解密錯誤", e);
                            return Mono.empty();
                        } finally {
                            DataBufferUtils.release(dataBuffer);
                        }
                    });
        }
        return chain.filter(exchange);
    }
    private String decryptRequest(String originalRequestBody) {
        if (!secureProperties.enableDecryptRequestParam()) {
            log.info("請求參數(shù)解密,跳過");
            return originalRequestBody;
        }
        log.info("請求參數(shù)解密,原文:{}", originalRequestBody);
        String decrypted = getSecureComponent().decrypt(originalRequestBody);
        log.info("請求參數(shù)解密,明文:{}", decrypted);
        return decrypted;
    }
    private String encryptResponse(String originalResponseBody) {
        if (!secureProperties.enableEncryptResponseParam()) {
            log.info("響應(yīng)結(jié)果加密,跳過");
            return originalResponseBody;
        }
        ResponseDto responseDto = GsonUtil.fromJson(originalResponseBody, ResponseDto.class);
        // 只對data字段進行加密處理
        Object data = responseDto.getData();
        if (Objects.nonNull(data)) {
            responseDto.setData(getSecureComponent().encrypt(data.toString()));
        }
        log.info("響應(yīng)結(jié)果加密,原文:{}", originalResponseBody);
        String result = GsonUtil.toJson(responseDto);
        log.info("響應(yīng)結(jié)果加密,密文:{}", result);
        return result;
    }
    /**
     * 獲取原始的請求參數(shù)
     *
     * @param dataBuffer 數(shù)據(jù)緩沖
     * @return 原始的請求參數(shù)
     */
    private String getOriginalRequestBody(DataBuffer dataBuffer) {
        byte[] bytes = new byte[dataBuffer.readableByteCount()];
        dataBuffer.read(bytes);
        return new String(bytes, StandardCharsets.UTF_8);
    }
    private boolean isNeedFilterMethod(HttpMethod method) {
        return NEED_FILTER_METHOD_SET.contains(method);
    }
    private boolean isNeedFilterContentType(MediaType mediaType) {
        return NEED_FILTER_MEDIA_TYPE_SET.contains(mediaType) || "json".equals(mediaType.getSubtype());
    }
    private ServerHttpRequestDecorator serverHttpRequestDecorator(ServerHttpRequest originalRequest, String decryptRequestBody) {
        return new ServerHttpRequestDecorator(originalRequest) {
            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders httpHeaders = new HttpHeaders();
                httpHeaders.putAll(super.getHeaders());
                httpHeaders.remove(HttpHeaders.CONTENT_LENGTH);
                httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
                return httpHeaders;
            }
            @Override
            public Flux<DataBuffer> getBody() {
                byte[] bytes = decryptRequestBody.getBytes(StandardCharsets.UTF_8);
                return Flux.just(new DefaultDataBufferFactory().wrap(bytes));
            }
        };
    }
    private ServerHttpResponseDecorator serverHttpResponseDecorator(ServerHttpResponse originalResponse) {
        DataBufferFactory dataBufferFactory = originalResponse.bufferFactory();
        return new ServerHttpResponseDecorator(originalResponse) {
            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                if (body instanceof Flux<? extends DataBuffer> fluxBody) {
                    return super.writeWith(fluxBody.buffer().map(dataBuffers -> {
                        DataBuffer join = dataBufferFactory.join(dataBuffers);
                        byte[] byteArray = new byte[join.readableByteCount()];
                        join.read(byteArray);
                        DataBufferUtils.release(join);
                        String originalResponseBody = new String(byteArray, StandardCharsets.UTF_8);
                        //加密
                        byte[] encryptedByteArray = encryptResponse(originalResponseBody).getBytes(StandardCharsets.UTF_8);
                        originalResponse.getHeaders().setContentLength(encryptedByteArray.length);
                        originalResponse.getHeaders().setContentType(MediaType.APPLICATION_JSON_UTF8);
                        return dataBufferFactory.wrap(encryptedByteArray);
                    }));
                }
                return super.writeWith(body);
            }
            @Override
            public Mono<Void> writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> body) {
                return writeWith(Flux.from(body).flatMapSequential(p -> p));
            }
            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders headers = new HttpHeaders();
                headers.putAll(originalResponse.getHeaders());
                return headers;
            }
        };
    }
    private static final Set<HttpMethod> NEED_FILTER_METHOD_SET = Set.of(HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT);
    private static final Set<MediaType> NEED_FILTER_MEDIA_TYPE_SET = Set.of(MediaType.APPLICATION_JSON);
    @Override
    public int getOrder() {
        return -1;
    }
    public SecureComponent getSecureComponent() {
        if (Objects.isNull(secureComponent)) {
            secureComponent = SecureComponentFactory.get(secureProperties.getAlgorithm());
        }
        return secureComponent;
    }
}

2.2 網(wǎng)關(guān)配置

server:
  port: 10010 # 網(wǎng)關(guān)端口
spring:
  application:
    name: gateway # 服務(wù)名稱
  cloud:
    gateway:
      routes: # 網(wǎng)關(guān)路由配置
        - id: feng-server1 # 路由id,自定義,只要唯一即可
          uri: http://127.0.0.1:8081 # 路由的目標地址 http就是固定地址
          predicates: # 路由斷言,也就是判斷請求是否符合路由規(guī)則的條件
            - Path=/server/list/server1/** # 這個是按照路徑匹配,只要以/user/開頭就符合要求
        - id: feng-server2
          uri: http://127.0.0.1:8082
          predicates:
            - Path=/server/list/server2/**
# 自定義配置
feng:
  gateway:
    secure:
      request-switch:
        enable: false
      response-switch:
        enable: true
      algorithm: aes

2.3 自定義配置類

package org.feng.fenggateway.config;
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.feng.fenggateway.secure.SecureComponentFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.Objects;
/**
 * 加解密屬性配置
 *
 * @author feng
 */
@Slf4j
@Data
@ConfigurationProperties(prefix = SecureProperties.SECURE_PROPERTIES_PREFIX)
public class SecureProperties {
    public static final String SECURE_PROPERTIES_PREFIX = "feng.gateway.secure";
    /**
     * 算法
     */
    private SymmetricAlgorithm algorithm;
    /**
     * 請求開關(guān)
     */
    private SecureSwitch requestSwitch;
    /**
     * 響應(yīng)開關(guān)
     */
    private SecureSwitch responseSwitch;
    public void checkSupportedAlgorithm() {
        log.info("校驗是否支持算法:{}", algorithm);
        if (Objects.isNull(algorithm)) {
            return;
        }
        boolean supportedAlgorithm = SecureComponentFactory.isSupportedAlgorithm(algorithm);
        if (!supportedAlgorithm) {
            throw new UnsupportedOperationException("不支持的算法");
        }
        log.info("校驗是否支持算法:校驗通過");
    }
    /**
     * 是否啟用解密請求參數(shù)
     *
     * @return 默認為否,其他情況看配置
     */
    public boolean enableDecryptRequestParam() {
        if (Objects.isNull(requestSwitch)) {
            return false;
        }
        return requestSwitch.getEnable();
    }
    /**
     * 是否啟用加密響應(yīng)參數(shù)
     *
     * @return 默認為否,其他情況看配置
     */
    public boolean enableEncryptResponseParam() {
        if (Objects.isNull(responseSwitch)) {
            return false;
        }
        return responseSwitch.getEnable();
    }
}

2.4 加密組件接口

這個可以用來擴展支持其他加密算法,目前實現(xiàn)類只有AES。

package org.feng.fenggateway.secure;
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import jakarta.annotation.PostConstruct;
/**
 * 加解密組件
 *
 * @author feng
 */
public interface SecureComponent {
    /**
     * 加密
     *
     * @param originalText 原文
     * @return 密文
     */
    String encrypt(String originalText);
    /**
     * 解密
     *
     * @param encryptedText 密文
     * @return 解密后的明文
     */
    String decrypt(String encryptedText);
    /**
     * 獲取加解密算法類型
     *
     * @return 加解密算法類型
     */
    SymmetricAlgorithm getAlgorithmType();
    @PostConstruct
    default void registerToFactory() {
        SecureComponentFactory.registerBean(this);
    }
}

2.5 加密組件實現(xiàn),AES算法

package org.feng.fenggateway.secure;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets;
/**
 * AES加解密組件
 *
 * @author feng
 */
@Component
public class SecureAESComponent implements SecureComponent {
    /**
     * 生成密鑰,16、24、32位都行
     */
    private final static byte[] SECURE_KEY = "r4oz0f3kfk5tgyui".getBytes(StandardCharsets.UTF_8);
    /**
     * 偏移量,必須16位
     */
    private final static String IV = "r21g95kdsd423gy6";
    private final static AES AES_INSTANCE = new AES(Mode.CTS, Padding.PKCS5Padding, SECURE_KEY, IV.getBytes(StandardCharsets.UTF_8));
    @Override
    public String encrypt(String originalText) {
        return AES_INSTANCE.encryptHex(originalText);
    }
    @Override
    public String decrypt(String encryptedText) {
        return AES_INSTANCE.decryptStr(encryptedText);
    }
    @Override
    public SymmetricAlgorithm getAlgorithmType() {
        return SymmetricAlgorithm.AES;
    }
}

2.6 啟動類,校驗支持的算法配置

package org.feng.fenggateway;
import jakarta.annotation.Resource;
import org.feng.fenggateway.config.SecureProperties;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
@ConfigurationPropertiesScan
@SpringBootApplication
public class FengGatewayApplication implements CommandLineRunner {
    @Resource
    private SecureProperties secureProperties;
    public static void main(String[] args) {
        SpringApplication.run(FengGatewayApplication.class, args);
    }
    @Override
    public void run(String... args) {
        secureProperties.checkSupportedAlgorithm();
    }
}

三、請求報文示例

POST http://localhost:10010/server/list/server2/user?authorization=feng
Content-Type: application/json;charset=UTF-8

{
  "username": "fbb"
}

四、測試結(jié)果

4.1 網(wǎng)關(guān)項目啟動時

校驗結(jié)果正常:

4.2 發(fā)生請求時

可以看到data字段已經(jīng)加密響應(yīng)了。

請求和響應(yīng)結(jié)果:

到此這篇關(guān)于SpringCloud Gateway實現(xiàn)請求解密和響應(yīng)加密的文章就介紹到這了,更多相關(guān)SpringCloud Gateway內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • IntelliJ IDEA2020.3詳細安裝教程

    IntelliJ IDEA2020.3詳細安裝教程

    這篇文章主要介紹了IntelliJ IDEA2020.3詳細安裝教程,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-12-12
  • Java中System.setProperty()用法與實際應(yīng)用場景

    Java中System.setProperty()用法與實際應(yīng)用場景

    System.setProperty是Java中用于設(shè)置系統(tǒng)屬性的方法,它允許我們在運行時為Java虛擬機(JVM)或應(yīng)用程序設(shè)置一些全局的系統(tǒng)屬性,下面這篇文章主要給大家介紹了關(guān)于Java中System.setProperty()用法與實際應(yīng)用場景的相關(guān)資料,需要的朋友可以參考下
    2024-04-04
  • Java 正確終止線程的方法

    Java 正確終止線程的方法

    這篇文章主要介紹了Java 正確終止線程的方法,幫助大家更好的理解和學習java 多線程的相關(guān)知識,感興趣的朋友可以了解下
    2020-12-12
  • Java設(shè)計模式之java解釋器模式詳解

    Java設(shè)計模式之java解釋器模式詳解

    這篇文章主要介紹了Java設(shè)計模式之解釋器模式定義與用法,結(jié)合具體實例形式詳細分析了Java解釋器模式的概念、原理、定義及相關(guān)操作技巧,需要的朋友可以參考下
    2021-09-09
  • Mybatis多個字段模糊匹配同一個值的案例

    Mybatis多個字段模糊匹配同一個值的案例

    這篇文章主要介紹了Mybatis多個字段模糊匹配同一個值的案例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • Spring security權(quán)限配置與使用大全

    Spring security權(quán)限配置與使用大全

    Spring Security 本質(zhì)上是借助一系列的 Servlet Filter來提供各種安全性功能,但這并不需要我們手動去添加或者創(chuàng)建多個Filter,本文重點給大家介紹spring-security的配置與使用及實現(xiàn)方式,感興趣的朋友一起看看吧
    2021-09-09
  • SpringBoot詳解實現(xiàn)自定義異常處理頁面方法

    SpringBoot詳解實現(xiàn)自定義異常處理頁面方法

    SpringBoot是Spring全家桶的成員之一,是一種整合Spring技術(shù)棧的方式(或者說是框架),同時也是簡化Spring的一種快速開發(fā)的腳手架
    2022-06-06
  • idea注入mapper報錯報紅的幾種解決方案

    idea注入mapper報錯報紅的幾種解決方案

    相信大家在使用idea的時候一定會遇到這樣的問題,就是在service里注入mapper的時候,明明代碼沒有問題,也可以運行,但是idea它就是給你報個錯,有個紅色的波浪線在下面,所以本文將給大家介紹了idea注入mapper報錯報紅的幾種解決方案,需要的朋友可以參考下
    2023-12-12
  • 解決@Autowired報錯Could not autowire. No beans of ‘XXX‘ type found問題

    解決@Autowired報錯Could not autowire. No bea

    介紹了在IDEA中使用@Autowired報錯Couldnot autowire. No beans of 'XXX' type found的解決方法,原因是@Autowired在注入service時,由于service接口沒有實現(xiàn)類,而mybatis僅需提供Dao接口,導致@Autowired無法識別
    2024-12-12
  • Spring的循環(huán)依賴、三級緩存解決方案源碼詳細解析

    Spring的循環(huán)依賴、三級緩存解決方案源碼詳細解析

    這篇文章主要介紹了Spring的循環(huán)依賴、三級緩存解決方案源碼詳細解析,在Spring中,由于IOC的控制反轉(zhuǎn),創(chuàng)建對象不再是簡單的new出來,而是交給Spring去創(chuàng)建,會經(jīng)歷一系列Bean的生命周期才創(chuàng)建出相應(yīng)的對象,需要的朋友可以參考下
    2024-01-01

最新評論