精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Spring Cloud Gateway 數字簽名、URL動態(tài)加密這樣設計真優(yōu)雅!

安全 數據安全
在網絡傳遞數據的時候,為了防止數據被篡改,我們會選擇對數據進行加密,數據加密分為對稱加密和非對稱加密。其中RSA和AES,TLS等加密算法是比較常用的。

在網絡傳遞數據的時候,為了防止數據被篡改,我們會選擇對數據進行加密,數據加密分為對稱加密和非對稱加密。其中RSA和AES,TLS等加密算法是比較常用的。

對稱加密

對稱加密是指加密和解密使用相同的密鑰的加密方法。其基本流程包括以下步驟

  1. 密鑰生成:雙方協商生成一個共享密鑰或由一方生成密鑰并安全地傳輸給另一方。
  2. 加密:使用共享密鑰對原始數據進行加密,得到加密后的數據。
  3. 傳輸:將加密后的數據傳輸給另一方。
  4. 解密:接收方使用相同的共享密鑰對加密數據進行解密,得到原始數據。

非對稱加密

非對稱加密是指加密和解密使用不同的密鑰的加密方法,通常稱為公鑰和私鑰。其基本流程包括以下步驟:

  1. 密鑰對生成:生成一對密鑰,一個是公鑰,另一個是私鑰。公鑰可以公開,而私鑰需要保密。
  2. 公鑰分發(fā):將公鑰發(fā)送給需要加密數據的一方。
  3. 加密:使用公鑰對原始數據進行加密,得到加密后的數據。
  4. 傳輸:將加密后的數據傳輸給另一方。
  5. 解密:接收方使用私鑰對加密數據進行解密,得到原始數據。

結合使用:

在實際應用中,對稱加密和非對稱加密通常會結合使用以達到安全和效率的平衡。例如:

  1. 使用非對稱加密交換對稱密鑰。
  2. 使用對稱密鑰進行數據加密和解密。

什么是數字簽名

再上面我們了解了RSA對稱加密,那么當我們進行數據交換的時候,如下:

假設有AB兩個人,假設前面他們已經交換完畢了公鑰。

那么此時當A使用B的公鑰加密原始數據然后發(fā)送數據給B的時候,它可以再數據的后面再攜帶上一個原始數據hash計算之后得到的hash值,然后用自己的私鑰進行加密。

A將數據發(fā)送到B之后,由于數據使用的是B的公鑰加密,B可以用私鑰解密之后,得到A發(fā)送消息的原本內容,然后,B可以使用A的公鑰對額外的數字簽名進行校驗,因為它假設這個數據是A發(fā)送的,那么用A的公鑰就應該可以解密成功,所以如果數據解密成功之后與A發(fā)送的原始消息經過一樣的Hash運算之后相等,那么說明沒有被篡改,而如果不一致,那么就說明被篡改了。因為第三方是不知道A的私鑰信息的,所以他是用自己的私鑰去加密,得到的hash會與A進行hash之后的值不同,從而判斷數據被篡改了。關注公眾號:碼猿技術專欄,回復關鍵詞:1111 獲取阿里內部Java性能調優(yōu)手冊!

HTTPS與CA

https其實不是一個單獨的協議,而是數據傳輸的時候使用TLS/SSL進行了加密而已。而TLS就是一個非常典型的非對稱加密,其兼顧了AES和RSA的安全性和速度。

上面我們已經聊完了一個加密數據的交換過程,那么如果有些人就是偽造了一些域名讓你去訪問怎么辦呢?

HTTPS (HTTP Secure) 是一個安全的 HTTP 通道,它通過 SSL/TLS 協議來保證數據的安全傳輸。在 HTTPS 請求的過程中,證書頒發(fā)機構 (CA, Certificate Authority) 扮演了重要的角色。以下是 CA 在保證 HTTPS 請求過程中數據安全交換的方式:

  1. 證書頒發(fā):CA 為服務器頒發(fā)一個數字證書。這個證書包含了服務器的公鑰和一些識別服務器身份的信息。數字證書是由 CA 簽名的,以驗證證書的真實性和完整性。
  2. 建立安全連接:當客戶端第一次連接到服務器時,服務器會發(fā)送其數字證書給客戶端。客戶端會驗證數字證書的合法性,比如檢查證書是否由一個受信任的 CA 簽名,檢查證書是否在有效期內等。一旦證書驗證通過,客戶端就能確認它是與正確的服務器進行通信,而不是被中間人攻擊。
  3. 密鑰交換:客戶端和服務器會使用 SSL/TLS 協議中的密鑰交換機制來協商一個會話密鑰(通常是一個對稱密鑰)。一種常見的方法是客戶端生成一個隨機的對稱密鑰,然后用服務器的公鑰加密它,再發(fā)送給服務器。服務器用自己的私鑰解密得到對稱密鑰。
  4. 數據加密和傳輸:一旦會話密鑰被協商好,客戶端和服務器就會用這個密鑰來加密和解密傳輸的數據。這樣,即使數據在傳輸過程中被截獲,攻擊者也無法解讀數據的內容,因為他們沒有會話密鑰。
  5. 完整性校驗:SSL/TLS 協議還提供了數據完整性校驗。它會為傳輸的數據生成一個 MAC (Message Authentication Code),以確保數據在傳輸過程中沒有被篡改。

圖片圖片

其實前面的第一和第二隨機數都是正常傳輸,預主密鑰的得到就是使用RSA了,此時只有客戶端和服務端知道預主密鑰,之后,對第一和第二隨機數使用預主密鑰的加密,就可以得到會話密鑰,此時加密交互完成。

并且會話密鑰只應用在當前會話,每個會話都會新生成一個,所以安全性大大增加。

只有前面的得到預主密鑰的過程用RSA,其他地方都是AES,因為,非對稱實在太慢了。

Gateway網關的過濾器鏈

我們知道,我們可以再Gateway網關中自定義過濾器,并且實現Ordered接口來對過濾器的執(zhí)行順序進行排序。如下圖我實現了三個自定義的全局過濾器。

圖片圖片

并且,當你實現全局過濾器接口的時候,你必須實現如下方法

public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain)

其中exchange參數非常重要,他就是你的請求以及對你請求的響應。而chain就是上面的過濾器鏈條。

圖片圖片

而我們的過濾器鏈的作用,其實就是對request和response這兩個重要的類進行操作。

比如我可以使用exchange.mutate方法來對request和response進行修改。

exchange = exchange.mutate().request(build -> {
            try {
                build.uri(
                        new URI("http://localhost:8080/v1/product?productId=1"))
                        .build();
            } catch (URISyntaxException e) {
                throw new RuntimeException(e);
            }
        }
).build();

如下是我修改request之前的請求體內容。

圖片圖片

如下就是我修改URI之后的request請求體的內容。

圖片圖片

在這里我們把這個reqeust給他修改有著重大意義,這意味著只要對加密后的數據進行解密后,去修改這個request中的內容,我們就能再一次成功的將我們的請求路由到我們指定的路徑。

圖片圖片

而之后,我們最終路由到的請求路徑位置,保存在了DefaultServerWebExchange的attributes中。

![ttps://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a9bf57e6a521475fb8e403526290533e~tplv-k3u1fbpfcp-jj-mark:3024:0:0:0:q75.awebp#?w=1766&h=1273&s=401141&e=png&b=f9f5f5)

最后只要進入到ForwardRoutingFilter

圖片圖片

在這里請求完成了最終的處理,然后進行轉發(fā),發(fā)送到對應的處理類去處理。

這時候我們看提供遠程服務調用的類的調用棧即可。

圖片圖片

如何對自己的路徑傳輸設定一個數字簽名?

上面我們已經聊到了,先使用RSA的方式傳遞對稱密鑰,然后之后的請求使用AES來進行加密解密。這樣子既保證了安全性也保證了請求的速度。

我就按照上面的說法,簡單的實現了一個數字簽名,大概方式如下:

公鑰獲取:
客戶端首先通過一個特定的接口從服務器獲取RSA公鑰。

對稱密鑰加密:
客戶端生成一個隨機的對稱密鑰,然后使用服務器的RSA公鑰對這個對稱密鑰進行加密。

發(fā)送加密的對稱密鑰:
客戶端將加密后的對稱密鑰發(fā)送到服務器。

對稱密鑰解密:
服務器使用自己的RSA私鑰解密客戶端發(fā)送的加密對稱密鑰,從而得到原始的對稱密鑰。

加密通信:
從現在開始,客戶端和服務器都會使用這個對稱密鑰來加密和解密他們之間的通信。這包括URL的動態(tài)加密、請求和響應的加密解密,以及數字簽名的驗證等。

數字簽名:
為了確保數據的完整性和非否認性,客戶端和/或服務器可以使用對稱密鑰來生成和驗證數字簽名。
這樣,雙方都可以確信接收到的數據沒有被篡改,并且確實來自預期的發(fā)送方。 

URL動態(tài)加密:
使用對稱密鑰對URL進行動態(tài)加密,以保護URL中的敏感信息,并防止未經授權的訪問。
這個流程確保了客戶端和服務器之間的通信安全,防止數據被截獲或篡改,同時也提供了一個有效的機制來驗證通信雙方的身份。


具體流程如下:
我們首先需要做的第一步是提供一個接口讓前端客戶端去訪問,
并且獲得到我們的公開的RSA公鑰,
然后前端拿到這個RSA公鑰之后加密自己的對稱密鑰,
然后再一次發(fā)送一個請求,
這個請求攜帶的是通過RSA公鑰加密過后的對稱密鑰,
然后服務端收到這個對稱密鑰之后,
通過RSA私鑰解密可以得到原本的前端發(fā)送的對稱密鑰。
此時,之后的URL動態(tài)加密所需要使用到的密鑰,
以及之后請求的數字簽名的加密,
都使用AES的方式,
并且使用這個解密后的對稱密鑰進行加密解密

前端獲取RSA公鑰

我們首先在gateway網關提供一個接口用于提供給前端獲取RSA公鑰。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;

import javax.annotation.PostConstruct;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

/**
 * @author: 公眾號:碼猿技術專欄
 * @date: 2023/10/2 15:13
 * SecurityConfig的作用是返回公鑰
 */
@Configuration
publicclass SecurityConfig {
    private KeyPair keyPair;

    @PostConstruct
    public void init() {
        // Generate RSA key pair
        KeyPairGenerator keyGen;
        try {
            keyGen = KeyPairGenerator.getInstance("RSA");
            keyGen.initialize(2048);
            keyPair = keyGen.genKeyPair();
        } catch (NoSuchAlgorithmException e) {
            thrownew RuntimeException("Failed to generate RSA key pair", e);
        }
    }

    /**
     * 提供給前端獲取RSA公鑰
     * @return
     */
    @Bean
    public RouterFunction<ServerResponse> publicKeyEndpoint() {
        return RouterFunctions.route()
                .GET("/public-key", req -> {
                    String publicKey = Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded());
                    return ServerResponse.ok().bodyValue(publicKey);
                })
                .build();
    }

    public KeyPair getKeyPair() {
        return keyPair;
    }

}

圖片圖片

發(fā)送加密后對稱密鑰

前端使用得到的公鑰對自己的對稱密鑰進行加密,代碼如下:

package blossom.star.project.product;

import org.junit.jupiter.api.Test;

import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

//@SpringBootTest
class RSA {

    @Test
    void contextLoads() {
    }


    public static void main(String[] args) throws Exception {
        //TODO 2:這里得到的是獲取rsa的公鑰之后,對對稱密鑰進行加密,之后就是使用這個對稱密鑰進行
        //數據的加解密
        // Replace with your RSA public key
        String publicKeyPEM = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvXBSqSyOPb01/uOnhnFN8Hvaz1IQbXnxFzGp9rWBxRAI2p6o67Elr1+SW68JnXx4swq7+z0U+YZSuszsoqwIrn8XF75bpJ+NKLkH7Bpe5A+If78zTihsCoPs+x74FIaJTSiVCzWP9mCaDSVO2bPTwOvqMwQ7xlmTmN9QShCIJ6uBXaggB5aWdpkh/IsIsZXIlzFB5HxA8AYj3u0AyWZO+pNS1fwq2Q7GPwWG7Zl7bCrUjIbG40k/Ef1BjdJBhQakMUq3Zqx+LJP37Tk4FzW47bwD9AiSL4DAXT+sc+Hw1fNspd2qFZBN94h5Pxkxoc9ZBMWB2bFBdRb6zkEg0/2OwwIDAQAB" ;

        // Replace with your symmetric key
        String symmetricKey = "zhangjinbiao6666";

        // Converting PEM to PublicKey
        byte[] decoded = Base64.getDecoder().decode(publicKeyPEM);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decoded);
        PublicKey publicKey = keyFactory.generatePublic(keySpec);

        // Encrypting symmetric key
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] encryptedSymmetricKey = cipher.doFinal(symmetricKey.getBytes());
        String encryptedSymmetricKeyBase64 = Base64.getEncoder().encodeToString(encryptedSymmetricKey);

        // Printing encrypted symmetric key
        System.out.println(encryptedSymmetricKeyBase64);
    }

}

后端接收當前會話對稱密鑰并保存

這里由于我沒有前端,不好操作,我就直接暫時寫死了,但是具體的實現邏輯就是與前端制定一個唯一的會話id,然后之后只要是同一個會話就可以使用同一個對稱密鑰,這樣子才能進一步保證安全,而不是一直使用同一個對稱密鑰。

package blossom.star.project.gateway.filter;

import blossom.star.framework.common.constant.HttpStatus;
import blossom.star.project.gateway.config.SecurityConfig;
import blossom.star.project.gateway.util.GatewayUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.util.Base64;


/**
 * @author 公眾號:碼猿技術專欄
 * 對稱密鑰保存過濾器
 * 當前過濾器首先會先獲取請求頭中的對稱密鑰
 * 如果有,那么獲取對稱密鑰并且保存到Redis中
 */
//@Component
publicclass SymmetricKeyFilter implements GlobalFilter, Ordered {

    @Autowired
    private SecurityConfig securityConfig;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;


    //TODO 3:這里會把加密好的對稱密鑰 解密 然后放入到redis中
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String encryptedSymmetricKey = exchange.getRequest().getHeaders().getFirst("X-Encrypted-Symmetric-Key");
        if (encryptedSymmetricKey != null) {
            try {
                Cipher cipher = Cipher.getInstance("RSA");
                cipher.init(Cipher.DECRYPT_MODE, securityConfig.getKeyPair().getPrivate());
                byte[] decryptedKeyBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedSymmetricKey));
                //得到對稱密鑰
                String symmetricKey = new String(decryptedKeyBytes, StandardCharsets.UTF_8);

                ////在非阻塞上下文中阻塞調用可能會導致線程饑餓
                ////TODO 需要優(yōu)化一下這里 來確保每個請求可以唯一對應一個加密密鑰
                //String sessionId = exchange.getSession().block().getId();
                //stringRedisTemplate.opsForValue().set(sessionId, symmetricKey);
                String redisSymmetricKey = "symmetric:key:"+1;
                stringRedisTemplate.opsForValue().set(redisSymmetricKey, symmetricKey);

            } catch (Exception e) {
                e.printStackTrace();
                String responseBody = "there are something wrong occurs when decrypt your key!!!";
                GatewayUtil.responseMessage(exchange,responseBody);

                // 獲取響應對象
                //ServerHttpResponse response = exchange.getResponse();
                ////處理對稱密鑰出現了問題
                //response.setRawStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
                //response.getHeaders().setContentType(MediaType.TEXT_PLAIN);
                //
                //// 返回你想要的字符串
                //return response.writeWith(
                //        Mono.just(response.bufferFactory().wrap(responseBody.getBytes())));
            }
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return -300;
    }

}

前端發(fā)送AES加密請求

比如這里的請求參數為productId=1,然后我們額外發(fā)送一個signature=wHYOLLkTn00DVrcmuCFzFQ==,signature的值就是對這個參數productId=1進行AES加密之后得到的數據。

然后我們再一次對String plaintext = "productId=1&signature=wHYOLLkTn00DVrcmuCFzFQ==";來進行加密,然后發(fā)送的請求以這個為參數。

也就是發(fā)送:

http://localhost:8080/v1/product/encrypt/8lPoJ5k/aHpfgKlxB5A9eUXqZ4MvgpFqN/SwDBVwDbERjBkQw62kfAmfsDW2Bngm

只要后端檢測到這個路徑有任何一點不對勁,就會直接報錯返回。

package blossom.star.project.product;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

/**
 * @author: 公眾號:碼猿技術專欄
 * @date: 2023/10/2 17:32
 * AES類
 */
publicclass AES {
    //1:首先讓前端對請求路徑傳輸進行AES的加密 密鑰已經傳遞
    //比如productId=1 ---》wHYOLLkTn00DVrcmuCFzFQ==
    //如果有多個 就直接 & 的方式進行拼接然后AES加密即可
    //2:signature=wHYOLLkTn00DVrcmuCFzFQ==
    //3:然后在對整個URL進行加密傳輸,傳輸方式為 /encrypt +
    // /5s7/98nWOXAJKujQ7nj66ZhohFdur/pPBzd3Y9kZqeIrZmPvTegG8
    // +OYwY6IMr9dXtK9vmZvJoEEsWZT+LLBCQ==
    //其中 + 后面的就是我們aes加密后的url ,/encrypt用于表示進行前端的路由

    public static void main(String[] args) throws Exception {
        //TODO 1:首先設定一下加密的內容 這里直接用java代碼加密
        String plaintext = "productId=1";
        //String plaintext = "productId=1&signature=wHYOLLkTn00DVrcmuCFzFQ==";
        String symmetricKey = "zhangjinbiao6666";  // Ensure this key has 16 bytes

        String encryptedText = encryptUrl(plaintext, symmetricKey);
        System.out.println(encryptedText);
    }

    public static String encryptUrl(String url, String symmetricKey) throws Exception {
        SecretKeySpec keySpec = new SecretKeySpec(symmetricKey.getBytes(StandardCharsets.UTF_8), "AES");
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.ENCRYPT_MODE, keySpec);
        byte[] encryptedBytes = cipher.doFinal(url.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(encryptedBytes);
    }

}

驗證請求

而如果請求的參數被篡改了,比如上面的productId=2,那么有如下圖情況:

圖片圖片

此時驗證請求是否被修改的方法就會報錯。

圖片圖片

下面再驗證請求是否被篡改的過程中,代碼寫的可能有一點丑陋。

package blossom.star.project.gateway.filter;

import blossom.star.framework.common.constant.HttpStatus;
import blossom.star.project.gateway.util.CryptoHelper;
import blossom.star.project.gateway.util.GatewayUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.Ordered;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.net.URISyntaxException;

/**
 * @author 公眾號:碼猿技術專欄
 * 當前類首先會解析加密后的URL
 * 當前類用于解析參數 如果參數解密后和signature不一樣則返回
 * 并且會重新設定路由路徑
 */
@Component
publicclass CryptoFilter implements GlobalFilter, Ordered {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private CryptoHelper cryptoHelper;


    //TODO 4:在這里對加密的URL進行解密
    //并且會得到路徑的參數
    //然后對參數進行加密之后和signature比較判斷是否被修改
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //String sessionId = exchange.getSession().block().getId();
        String redisSymmetricKey = "symmetric:key:" + 1;
        //String symmetricKey = stringRedisTemplate.opsForValue().get(sessionId);
        String symmetricKey = stringRedisTemplate.opsForValue().get(redisSymmetricKey);
        if (symmetricKey == null) {
            return GatewayUtil.responseMessage(exchange, "this session has not symmetricKey!!!");
        }

        try {
            //URL動態(tài)加密  數字簽名 signature
            //如果URL已加密,則解密該URL
            //path:/v1/product/encrypt/WyYSV30Cor8QX/eWGsQ7yPD3EvNRRS0HF845UOb+KAdwHPKZByMa3250J/z2S4at
            //uri:http://localhost:8080/v1/product/encrypt/WyYSV30Cor8QX/eWGsQ7yPD3EvNRRS0HF845UOb+KAdwHPKZByMa3250J/z2S4at
            String encryptedUrl = exchange.getRequest().getURI().toString();
            String path = exchange.getRequest().getURI().getPath();
            String encryptPathParam = path.substring(path.indexOf("/encrypt/") + 9);
            String decryptedPathParam = cryptoHelper.decryptUrl(encryptPathParam, symmetricKey);
            String decryptedUri =
                    encryptedUrl.substring(0, encryptedUrl.indexOf("/encrypt/"))
                            .concat("?").concat(decryptedPathParam);
            //這個方法直接修改的是exchange里面的request
            exchange = exchange.mutate().request(build -> {
                try {
                    build.uri(new URI(decryptedUri));
                } catch (URISyntaxException e) {
                    thrownew RuntimeException(e);
                }
            }).build();
            //TODO 需要前端這里首先按照前后端約定的加密方式進行一次加密
            //然后得到一個signature,放在請求的末尾
            //然后對整個URL進行加密請求
            // 解析解密后的URL以獲取解密的查詢參數
            UriComponents uriComponents = UriComponentsBuilder.fromUriString(decryptedUri).build();
            MultiValueMap<String, String> decryptedQueryParams = uriComponents.getQueryParams();

            // 驗證請求參數的簽名
            String signature = decryptedQueryParams.getFirst("signature");
            if (!cryptoHelper.verifySignature(decryptedQueryParams, signature, symmetricKey)) {

                return GatewayUtil.responseMessage(exchange,
                        "the param has something wrong!!!");
            }

        } catch (Exception e) {
            return GatewayUtil.responseMessage(exchange,
                    "the internal server occurs an error!!!");
        }

        return chain.filter(exchange);
    }


    @Override
    public int getOrder() {
        return -200;
    }
}
package blossom.star.project.gateway.util;

import org.springframework.context.annotation.Configuration;
import org.springframework.util.MultiValueMap;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.List;
import java.util.Map;


/**
 * @author 公眾號:碼猿技術專欄
 * 密碼學工具包
 */
@Configuration
publicclass CryptoHelper {

    public String decryptUrl(String encryptedUrl, String symmetricKey) throws Exception {
        SecretKeySpec keySpec = new SecretKeySpec(symmetricKey.getBytes(StandardCharsets.UTF_8), "AES");
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.DECRYPT_MODE, keySpec);
        byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedUrl));
        returnnew String(decryptedBytes, StandardCharsets.UTF_8);
    }

    //解析路徑參數并且加密,后判斷是否和signature一樣
    public boolean verifySignature(MultiValueMap<String, String> queryParams, String signature, String symmetricKey) throws Exception {
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, List<String>> entry : queryParams.entrySet()) {
            //將簽名本身從要驗證的數據中排除
            if (!"signature".equals(entry.getKey())) {
                sb.append(entry.getKey()).append("=").append(String.join(",", entry.getValue())).append("&");
            }
        }
        sb.setLength(sb.length()-1);
        String computedSignature = encryptRequestParam(sb.toString(), symmetricKey);
        return computedSignature.equals(signature);
    }


    public static String encryptRequestParam(String requestParam, String symmetricKey) throws Exception {
        SecretKeySpec keySpec = new SecretKeySpec(symmetricKey.getBytes(StandardCharsets.UTF_8), "AES");
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.ENCRYPT_MODE, keySpec);
        byte[] encryptedBytes = cipher.doFinal(requestParam.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(encryptedBytes);
    }
}

如果請求的過程中,請求的數據并沒有被修改,那么可以正確解析,如下:

圖片圖片

如何實現URL的動態(tài)加密?

動態(tài)加密其實在上面就已經說了。

可以發(fā)現我們發(fā)送的實際請求是下面這個,/encrypt/后面的就是我們約定好的加密參數。

http://localhost:8080/v1/product/encrypt/WLB8EDs2LNTsUJpS/aANt0XqZ4MvgpFqN/SwDBVwDbERjBkQw62kfAmfsDW2Bngm

實際再處理過程中會去掉/encrypt,他只是用于標識具體的加密參數位置而已。

責任編輯:武曉燕 來源: 碼猿技術專欄
相關推薦

2021-12-28 13:54:52

加密密鑰Java

2016-11-10 23:51:41

2009-03-02 16:42:33

2011-08-29 10:27:38

IT技術數字簽名數字證書

2009-08-14 13:40:17

數字簽名電子簽名安全體系結構

2010-06-04 09:21:51

身份認證數字簽名

2019-04-18 15:00:36

2010-09-17 20:20:14

2010-10-08 21:14:08

2010-09-02 21:10:13

2009-06-29 13:26:44

Java編程入門加密

2013-04-22 16:07:45

2019-08-22 09:55:17

RedisAPI數據

2011-01-19 17:27:21

Sylpheed

2009-07-19 21:44:39

2020-08-13 18:40:51

OpenSSL密碼學Linux

2011-06-20 15:12:48

微軟木馬

2022-01-07 07:29:08

Rbac權限模型

2022-07-28 10:27:58

量子計算

2023-03-08 09:03:55

點贊
收藏

51CTO技術棧公眾號

综合久久久久久| 青青草97国产精品免费观看 | 这里只有精品国产| 成人3d动漫在线观看| 亚洲天堂一区二区| 国产欧美不卡| 中文字幕v亚洲ⅴv天堂| 国产资源中文字幕| 毛片无码国产| 亚洲激情校园春色| 欧美一区激情视频在线观看| 国产日韩在线观看一区| 亚洲免费影视| 欧美精品情趣视频| 非洲一级黄色片| 日韩一级淫片| 欧美日本国产一区| 欧美亚洲另类色图| 成人在线视频亚洲| 久久亚洲免费视频| 国产精品区一区二区三在线播放| 国产乱码77777777| 影音先锋久久精品| 久久精品国产亚洲精品2020| 极品人妻一区二区三区| 日韩欧美高清一区二区三区| 日本精品免费观看高清观看| 韩日视频在线观看| 国产三级在线播放| 国产亚洲精品超碰| 久久久影院一区二区三区| 国产福利免费视频| 美女久久久精品| 欧美一级视频一区二区| 精品无码m3u8在线观看| 99九九热只有国产精品| 亚洲人成伊人成综合网久久久| 超级砰砰砰97免费观看最新一期| 国产在线|日韩| 一本一道久久a久久精品| 国产精品又粗又长| 黄色小说在线播放| 亚洲免费av高清| 椎名由奈jux491在线播放| av色图一区| 久久精品夜色噜噜亚洲a∨| 国产一区二区三区四区五区加勒比| 国产精品人人妻人人爽| 日韩va亚洲va欧美va久久| 日本精品久久电影| 日本视频www| 日韩一级在线| 久久久久久国产精品久久| 久久精品视频免费在线观看| 亚洲精品在线观看91| www.日韩系列| 亚洲国产123| 五月天久久777| 久久久999成人| 天天爽天天爽天天爽| 99久久精品网| 操日韩av在线电影| 黄色片在线观看网站| 在线中文一区| 欧美精品精品精品精品免费| 国产一级一片免费播放放a| 精品福利电影| 45www国产精品网站| 欧美日韩综合一区二区三区| 日韩国产欧美在线播放| 国产女人精品视频| av中文字幕免费在线观看| 国产a久久麻豆| 狠狠干一区二区| 日中文字幕在线| 欧美激情一区二区三区蜜桃视频| 手机成人在线| 性国产高清在线观看| 亚洲h精品动漫在线观看| 99视频在线免费播放| 欧美aa在线| 欧美视频完全免费看| 91小视频在线播放| 国产精品毛片久久久| 亚洲欧美日本另类| 中文字幕观看av| 黄色精品网站| 国产成人高清激情视频在线观看| 中文字幕在线网站| 成人午夜免费电影| 人偷久久久久久久偷女厕| 麻豆视频在线观看免费网站| 亚洲自拍偷拍九九九| 久久9精品区-无套内射无码| 日本中文字幕视频一区| 精品精品国产高清a毛片牛牛| 精品黑人一区二区三区观看时间| 国际精品欧美精品| 欧美另类在线播放| 免费一级a毛片| 福利一区二区在线| 神马一区二区影院| 麻豆蜜桃在线| 欧美男女性生活在线直播观看| 亚洲美女高潮久久久| 精品国产91| 久久免费国产视频| 91在线视频国产| 94色蜜桃网一区二区三区| 一区二区成人国产精品| 午夜av不卡| 日韩午夜av一区| 日韩福利在线视频| 国产精品久久777777毛茸茸| 亚洲在线观看视频网站| 成人在线二区| 欧美日韩精品在线播放| 少妇丰满尤物大尺度写真| 国产一区二区三区网| 欧美激情一区二区三区久久久| 中文字幕乱码中文字幕| 91浏览器在线视频| 激情小视频网站| 久久av网站| 中文字幕不卡av| 亚洲AV无码成人精品区东京热| 国产成a人无v码亚洲福利| 亚洲人成网站在线播放2019| 午夜精品久久久久久久久久蜜桃| 精品99一区二区三区| 欧美国产日韩在线观看成人| 另类成人小视频在线| 欧美日韩综合久久| 波多野结衣亚洲一二三| 亚洲成色999久久网站| 久久久久97国产| 国产精品羞羞答答xxdd| 看全色黄大色大片| 欧美一级做a| 久久韩剧网电视剧| 在线观看免费视频a| 国产欧美一区二区精品性色| 亚洲成熟丰满熟妇高潮xxxxx| 国偷自产av一区二区三区| 欧美大片在线看| 黑人乱码一区二区三区av| 亚洲精品五月天| 农村末发育av片一区二区| 中文字幕乱码亚洲无线精品一区| 成人在线视频网站| 大地资源网3页在线观看| 欧美一区二区精美| 欧美成人精品激情在线视频| 国产在线精品一区二区三区不卡| 异国色恋浪漫潭| 精品国产亚洲一区二区三区在线| 久久综合伊人77777尤物| av综合在线观看| 亚洲一区二区三区免费视频| 在线免费观看污视频| 999亚洲国产精| 青青成人在线| 日韩午夜视频在线| 不卡伊人av在线播放| 国产喷水福利在线视频| 《视频一区视频二区| 手机在线播放av| 欧美三区美女| 精品国产日本| av在线不卡精品| 日韩在线视频播放| 国产极品久久久| 午夜一区二区三区在线观看| 99久久国产精| 日韩成人av影视| 亚洲一区二区精品在线观看| 精品视频在线观看免费观看| 久久久免费电影| 极品美乳网红视频免费在线观看 | 成人网在线免费视频| 日本wwww视频| 91精品国产调教在线观看| 51精品国产人成在线观看| 高清在线视频不卡| 日韩在线小视频| 韩国av在线免费观看| 日本高清不卡在线观看| 色欲人妻综合网| 久久综合九色综合97婷婷女人| 污版视频在线观看| 亚洲黄色精品| 一区二区三区的久久的视频| 97久久综合精品久久久综合| 国产成人精品一区二区在线| 91麻豆一二三四在线| 精品一区精品二区| 国产深喉视频一区二区| 国产成人免费9x9x人网站视频| 久久久精品蜜桃| 亚洲综合伊人久久| 久久在线精品| 久久这里只有精品18| 精品视频国产| 国产精品毛片一区视频| 国产国产一区| 久久久久久久香蕉网| 午夜视频在线| 亚洲美女精品久久| 丰满熟女一区二区三区| 欧美日韩高清一区| 国产精品va无码一区二区三区| 亚洲靠逼com| 好吊视频在线观看| a在线播放不卡| 色姑娘综合天天| 日本午夜一区二区| 少妇高潮喷水在线观看| 欧美区日韩区| 一区二区三区四区不卡| 亚洲人成精品久久久| 国产精品sss| 国产一区二区三区视频在线| 国产精品va在线播放| 91在线超碰| 久久久久久美女| 亚洲电影视频在线| 欧美成aaa人片在线观看蜜臀| 国产女主播在线写真| 亚洲黄色www| 亚洲AV无码国产精品午夜字幕| 欧美日韩国产片| 中日韩在线观看视频| 色哟哟国产精品免费观看| 国产午夜激情视频| 亚洲在线视频网站| 91精品国产高清一区二区三蜜臀| 国产精品乱人伦一区二区| xxxx日本免费| 国产亚洲精品超碰| 波多野在线播放| 国产欧美1区2区3区| 伊人网伊人影院| 91美女片黄在线观看| 激情综合丁香五月| hitomi一区二区三区精品| 人妻 日韩 欧美 综合 制服| 国产精品亚洲人在线观看| 99国产精品免费视频| 国产不卡在线播放| av漫画在线观看| 成人精品高清在线| 国产黄色三级网站| 久久久精品国产免大香伊| 性少妇bbw张开| 国产欧美一二三区| 成人一级片免费看| 国产精品久久久久影院老司 | 日韩美女视频中文字幕| xx欧美视频| 国产精品毛片a∨一区二区三区|国| 日韩在线免费| 国产精品视频中文字幕91| 欧美成a人片免费观看久久五月天| 国产精品女主播视频| 永久免费观看精品视频| 91传媒视频免费| 国产精品毛片av| 欧美日韩一区二区三区在线视频| 精品国产一区二区三区av片| 亚洲欧洲中文| 欧美日韩亚洲一区在线观看| 黄色大片在线免费看| 老妇喷水一区二区三区| 在线免费看v片| av电影在线观看不卡| 美女被到爽高潮视频| 国产精品久久久久四虎| 欧美成人综合色| 欧美性xxxxxxxxx| 夜夜爽8888| 亚洲第一综合天堂另类专| 久青青在线观看视频国产| 久久久国产视频91| 蜜桃视频在线观看播放| 国产深夜精品福利| 超碰地址久久| 视频一区二区在线观看| 国产精品草草| 天天影视综合色| 粉嫩av一区二区三区在线播放| 蜜臀av一区二区三区有限公司| 国产精品区一区二区三| 国产精品成人aaaa在线| 欧美午夜免费电影| 成人免费视频国产| 中文字幕在线亚洲| zzzwww在线看片免费| 成人久久久久久| 美腿丝袜亚洲图片| 一区二区三区观看| 亚洲一区成人| 亚洲综合在线一区二区| 国产亚洲欧美中文| 国产一级一片免费播放放a| 欧美三级中文字幕在线观看| 日批视频免费播放| www.久久色.com| 波多野结衣亚洲| 国产精品日韩二区| 羞羞答答成人影院www| 97在线播放视频| 成人性生交大片免费看中文网站| 国产三级短视频| 欧美性xxxxxx| 香蕉久久国产av一区二区| 久久久91精品国产| 国产一区影院| 蜜桃传媒视频麻豆第一区免费观看 | 麻豆91在线观看| 欧美亚一区二区三区| 亚洲一区二区五区| 国产熟女一区二区三区四区| 中文字幕久精品免费视频| 在线最新版中文在线| 国产精品v欧美精品v日韩| 亚洲草久电影| 黄大色黄女片18第一次| 国产亚洲人成网站| 天码人妻一区二区三区在线看 | 亚洲最大激情中文字幕| 日韩激情在线| 久久久久免费精品| 久久女同性恋中文字幕| 九一国产在线观看| 精品动漫一区二区三区在线观看| 爆操欧美美女| 5566av亚洲| 午夜电影亚洲| 99久久综合网| 一区二区三区四区不卡视频| 亚洲视频在线免费播放| 最近2019中文字幕mv免费看 | 亚洲女同同性videoxma| 69亚洲乱人伦| 精品国产乱码久久久久久婷婷| 人妻少妇一区二区三区| 久久久久久久久电影| 99香蕉久久| 免费看毛片的网址| www.成人在线| wwwwww国产| 国产亚洲xxx| 国产一区二区三区四区五区3d| 亚洲国产高清国产精品| 久久精品国产亚洲a| 熟女av一区二区| 欧美一区二区日韩| 欧美人动性xxxxz0oz| 风间由美久久久| 日韩一级精品| 怡红院一区二区三区| 欧美日韩在线亚洲一区蜜芽| 日本在线免费看| 亚洲在线观看视频| 亚洲久久视频| 91激情视频在线观看| 777色狠狠一区二区三区| 亚洲精品一线| 另类欧美小说| 免费欧美在线视频| 青娱乐91视频| 日韩精品在线观看一区二区| 搜成人激情视频| 中文字幕一区二区三区乱码| 国产高清久久久久| 国产精品久免费的黄网站| 这里只有精品久久| 波多野结衣欧美| 久久精品香蕉视频| 亚洲色图欧美偷拍| 日本天堂影院在线视频| 国产美女久久精品| 国产综合自拍| 亚洲性猛交xxxx乱大交| 欧美高清性hdvideosex| 2001个疯子在线观看| 日韩欧美在线电影| 成人性生交大片免费| 国内自拍视频在线播放| 久久精品视频va| 亚洲人挤奶视频| 久久精品亚洲天堂| 欧美日韩国产一区二区三区| 午夜在线视频播放| 久久免费99精品久久久久久| 经典三级在线一区| 午夜影院免费在线观看| 九九久久国产精品| 精品久久久久久久| 美女黄色一级视频|