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

SpringBoot實(shí)現(xiàn)接口參數(shù)加密解密的示例代碼

 更新時(shí)間:2022年09月06日 16:34:37   作者:沒對(duì)象的指針  
加密解密本身并不是難事,問題是在何時(shí)去處理?SpringMVC?中給我們提供了?ResponseBodyAdvice?和?RequestBodyAdvice,利用這兩個(gè)工具可以對(duì)請(qǐng)求和響應(yīng)進(jìn)行預(yù)處理,非常方便。廢話不多說,我們一起來學(xué)習(xí)一下

加密解密本身并不是難事,問題是在何時(shí)去處理?定義一個(gè)過濾器,將請(qǐng)求和響應(yīng)分別攔截下來進(jìn)行處理也是一個(gè)辦法,這種方式雖然粗暴,但是靈活,因?yàn)榭梢阅玫揭皇值恼?qǐng)求參數(shù)和響應(yīng)數(shù)據(jù)。不過 SpringMVC 中給我們提供了 ResponseBodyAdvice 和 RequestBodyAdvice,利用這兩個(gè)工具可以對(duì)請(qǐng)求和響應(yīng)進(jìn)行預(yù)處理,非常方便。

所以今天這篇文章有兩個(gè)目的:

  • 分享參數(shù)/響應(yīng)加解密的思路。
  • 分享 ResponseBodyAdvice 和 RequestBodyAdvice 的用法。

好了,那么接下來就不廢話了,我們一起來看下。

1. 開發(fā)加解密 starter

為了讓我們開發(fā)的這個(gè)工具更加通用,也為了復(fù)習(xí)一下自定義 Spring Boot Starter,這里我們就將這個(gè)工具做成一個(gè) stater,以后在 Spring Boot 項(xiàng)目中直接引用就可以。

1.1 創(chuàng)建項(xiàng)目

首先我們創(chuàng)建一個(gè) Spring Boot 項(xiàng)目,引入 spring-boot-starter-web 依賴:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <scope>provided</scope>
    <version>2.4.3</version>
</dependency>

因?yàn)槲覀冞@個(gè)工具是為 Web 項(xiàng)目開發(fā)的,以后必然使用在 Web 環(huán)境中,所以這里添加依賴時(shí) scope 設(shè)置為 provided。

1.2 加密工具類

依賴添加完成后,我們先來定義一個(gè)加密工具類備用,加密這塊有多種方案可以選擇,對(duì)稱加密、非對(duì)稱加密,其中對(duì)稱加密又可以使用 AES、DES、3DES 等不同算法,這里我們使用 Java 自帶的 Cipher 來實(shí)現(xiàn)對(duì)稱加密,使用 AES 算法:

public class AESUtils {

    private static final String AES_ALGORITHM = "AES/ECB/PKCS5Padding";

    // 獲取 cipher
    private static Cipher getCipher(byte[] key, int model) throws Exception {
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
        Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
        cipher.init(model, secretKeySpec);
        return cipher;
    }

    // AES加密
    public static String encrypt(byte[] data, byte[] key) throws Exception {
        Cipher cipher = getCipher(key, Cipher.ENCRYPT_MODE);
        return Base64.getEncoder().encodeToString(cipher.doFinal(data));
    }

    // AES解密
    public static byte[] decrypt(byte[] data, byte[] key) throws Exception {
        Cipher cipher = getCipher(key, Cipher.DECRYPT_MODE);
        return cipher.doFinal(Base64.getDecoder().decode(data));
    }
}

這個(gè)工具類比較簡(jiǎn)單,不需要多解釋。需要說明的是,加密后的數(shù)據(jù)可能不具備可讀性,因此我們一般需要對(duì)加密后的數(shù)據(jù)再使用 Base64 算法進(jìn)行編碼,獲取可讀字符串。換言之,上面的 AES 加密方法的返回值是一個(gè) Base64 編碼之后的字符串,AES 解密方法的參數(shù)也是一個(gè) Base64 編碼之后的字符串,先對(duì)該字符串進(jìn)行解碼,然后再解密。

1.3 響應(yīng)工具類

接下來我們封裝一個(gè)響應(yīng)工具類備用:

public class RespBean {
    private Integer status;
    private String msg;
    private Object obj;

    public static RespBean build() {
        return new RespBean();
    }

    public static RespBean ok(String msg) {
        return new RespBean(200, msg, null);
    }

    public static RespBean ok(String msg, Object obj) {
        return new RespBean(200, msg, obj);
    }

    public static RespBean error(String msg) {
        return new RespBean(500, msg, null);
    }

    public static RespBean error(String msg, Object obj) {
        return new RespBean(500, msg, obj);
    }

    private RespBean() {
    }

    private RespBean(Integer status, String msg, Object obj) {
        this.status = status;
        this.msg = msg;
        this.obj = obj;
    }

    public Integer getStatus() {
        return status;
    }

    public RespBean setStatus(Integer status) {
        this.status = status;
        return this;
    }

    public String getMsg() {
        return msg;
    }

    public RespBean setMsg(String msg) {
        this.msg = msg;
        return this;
    }

    public Object getObj() {
        return obj;
    }

    public RespBean setObj(Object obj) {
        this.obj = obj;
        return this;
    }
}

1.4 定義注解

接下來我們定義兩個(gè)注解 @Decrypt 和 @Encrypt

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.PARAMETER})
public @interface Decrypt {
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Encrypt {
}

這兩個(gè)注解就是兩個(gè)標(biāo)記,在以后使用的過程中,哪個(gè)接口方法添加了 @Encrypt 注解就對(duì)哪個(gè)接口的數(shù)據(jù)加密返回,哪個(gè)接口/參數(shù)添加了 @Decrypt 注解就對(duì)哪個(gè)接口/參數(shù)進(jìn)行解密。這個(gè)定義也比較簡(jiǎn)單,沒啥好說的,需要注意的是 @Decrypt比 @Encrypt 多了一個(gè)使用場(chǎng)景就是 @Decrypt 可以用在參數(shù)上。

1.5 定義一個(gè) EncryptProperties 類來讀取用戶配置的 key

考慮到用戶可能會(huì)自己配置加密的 key,因此我們?cè)賮矶x一個(gè) EncryptProperties 類來讀取用戶配置的 key:

@ConfigurationProperties(prefix = "spring.encrypt")
public class EncryptProperties {
    private final static String DEFAULT_KEY = "www.itboyhub.com";
    private String key = DEFAULT_KEY;

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }
}

這里我設(shè)置了默認(rèn)的 key 是 www.itboyhub.com,key 是 16 位字符串,這個(gè)網(wǎng)站地址剛好滿足。以后如果用戶想自己配置 key,只需要在 application.properties 中配置 spring.encrypt.key=xxx 即可。

所有準(zhǔn)備工作做完了,接下來就該正式加解密了。

因?yàn)檫@篇文章一個(gè)很重要的目的是想和大家分享 ResponseBodyAdvice 和 RequestBodyAdvice 的用法,RequestBodyAdvice 在做解密的時(shí)候倒是沒啥問題,而 ResponseBodyAdvice 在做加密的時(shí)候則會(huì)有一些局限,不過影響不大,還是我前面說的,如果想非常靈活的掌控一切,那還是自定義過濾器吧。這里我就先用這兩個(gè)工具來實(shí)現(xiàn)了。

另外還有一點(diǎn)需要注意,ResponseBodyAdvice 在你使用了 @ResponseBody 注解的時(shí)候才會(huì)生效,RequestBodyAdvice 在你使用了 @RequestBody 注解的時(shí)候才會(huì)生效,換言之,前后端都是 JSON 交互的時(shí)候,這兩個(gè)才有用。不過一般來說接口加解密的場(chǎng)景也都是前后端分離的時(shí)候才可能有的事。

1.6 接口加密

先來看接口加密:

@EnableConfigurationProperties(EncryptProperties.class)
@ControllerAdvice
public class EncryptResponse implements ResponseBodyAdvice<RespBean> {
    private ObjectMapper om = new ObjectMapper();
    @Autowired
    EncryptProperties encryptProperties;
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return returnType.hasMethodAnnotation(Encrypt.class);
    }

    @Override
    public RespBean beforeBodyWrite(RespBean body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        byte[] keyBytes = encryptProperties.getKey().getBytes();
        try {
            if (body.getMsg()!=null) {
                body.setMsg(AESUtils.encrypt(body.getMsg().getBytes(),keyBytes));
            }
            if (body.getObj() != null) {
                body.setObj(AESUtils.encrypt(om.writeValueAsBytes(body.getObj()), keyBytes));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return body;
    }
}

我們自定義 EncryptResponse 類實(shí)現(xiàn) ResponseBodyAdvice接口,泛型表示接口的返回類型,這里一共要實(shí)現(xiàn)兩個(gè)方法:

  • supports:這個(gè)方法用來判斷什么樣的接口需要加密,參數(shù) returnType 表示返回類型,我們這里的判斷邏輯就是方法是否含有 @Encrypt 注解,如果有,表示該接口需要加密處理,如果沒有,表示該接口不需要加密處理。
  • beforeBodyWrite:這個(gè)方法會(huì)在數(shù)據(jù)響應(yīng)之前執(zhí)行,也就是我們先對(duì)響應(yīng)數(shù)據(jù)進(jìn)行二次處理,處理完成后,才會(huì)轉(zhuǎn)成 json 返回。我們這里的處理方式很簡(jiǎn)單,RespBean 中的 status 是狀態(tài)碼就不用加密了,另外兩個(gè)字段重新加密后重新設(shè)置值即可。
  • 另外需要注意,自定義的 ResponseBodyAdvice 需要用 @ControllerAdvice 注解來標(biāo)記。

1.7 接口解密

再來看接口解密:

@EnableConfigurationProperties(EncryptProperties.class)
@ControllerAdvice
public class DecryptRequest extends RequestBodyAdviceAdapter {
    @Autowired
    EncryptProperties encryptProperties;
    @Override
    public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return methodParameter.hasMethodAnnotation(Decrypt.class) || methodParameter.hasParameterAnnotation(Decrypt.class);
    }

    @Override
    public HttpInputMessage beforeBodyRead(final HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
        byte[] body = new byte[inputMessage.getBody().available()];
        inputMessage.getBody().read(body);
        try {
            byte[] decrypt = AESUtils.decrypt(body, encryptProperties.getKey().getBytes());
            final ByteArrayInputStream bais = new ByteArrayInputStream(decrypt);
            return new HttpInputMessage() {
                @Override
                public InputStream getBody() throws IOException {
                    return bais;
                }

                @Override
                public HttpHeaders getHeaders() {
                    return inputMessage.getHeaders();
                }
            };
        } catch (Exception e) {
            e.printStackTrace();
        }
        return super.beforeBodyRead(inputMessage, parameter, targetType, converterType);
    }
}
  • 首先大家注意,DecryptRequest 類我們沒有直接實(shí)現(xiàn) RequestBodyAdvice 接口,而是繼承自 RequestBodyAdviceAdapter 類,該類是 RequestBodyAdvice 接口的子類,并且實(shí)現(xiàn)了接口中的一些方法,這樣當(dāng)我們繼承自 RequestBodyAdviceAdapter 時(shí),就只需要根據(jù)自己實(shí)際需求實(shí)現(xiàn)某幾個(gè)方法即可。
  • supports:該方法用來判斷哪些接口需要處理接口解密,我們這里的判斷邏輯是方法上或者參數(shù)上含有 @Decrypt 注解的接口,處理解密問題。
  • beforeBodyRead:這個(gè)方法會(huì)在參數(shù)轉(zhuǎn)換成具體的對(duì)象之前執(zhí)行,我們先從流中加載到數(shù)據(jù),然后對(duì)數(shù)據(jù)進(jìn)行解密,解密完成后再重新構(gòu)造 HttpInputMessage 對(duì)象返回。

1.8 定義一個(gè)自動(dòng)化配置類

接下來,我們?cè)賮矶x一個(gè)自動(dòng)化配置類,如下:

@Configuration
@ComponentScan("org.javaboy.encrypt.starter")
public class EncryptAutoConfiguration {

}

這個(gè)也沒啥好說的,比較簡(jiǎn)單。

最后,resources 目錄下定義 META-INF,然后再定義 spring.factories 文件,內(nèi)容如下:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.javaboy.encrypt.starter.autoconfig.EncryptAutoConfiguration

這樣當(dāng)項(xiàng)目啟動(dòng)時(shí),就會(huì)自動(dòng)加載該配置類。

至此,我們的 starter 就開發(fā)完成啦。

2.打包發(fā)布

我們可以將項(xiàng)目安裝到本地倉庫,也可以發(fā)布到線上供他人使用。

2.1 安裝到本地倉庫

安裝到本地倉庫比較簡(jiǎn)單,直接 mvn install,或者在 IDEA 中,點(diǎn)擊右邊的 Maven,然后雙擊 install,如下:

2.2 發(fā)布到線上

發(fā)不到線上我們可以使用 JitPack 來做。

首先我們?cè)?GitHub 上創(chuàng)建一個(gè)倉庫,將我們的代碼上傳上去,這個(gè)過程應(yīng)該不用我多說吧。

上傳成功后,點(diǎn)擊右邊的 Create a new release 按鈕,發(fā)布一個(gè)正式版,如下:

發(fā)布成功后,打開 jitpack,輸入倉庫的完整路徑,點(diǎn)擊 lookup 按鈕,查找到之后,再點(diǎn)擊 Get it 按鈕完成構(gòu)建,如下:

構(gòu)建成功后,JitPack 上會(huì)給出項(xiàng)目引用方式:

注意引用時(shí)將 tag 改成你具體的版本號(hào)。

至此,我們的工具就已經(jīng)成功發(fā)布了!小伙伴們可以通過如下方式引用這個(gè) starter:

<dependencies>
    <dependency>
        <groupId>com.github.lenve</groupId>
        <artifactId>encrypt-spring-boot-starter</artifactId>
        <version>0.0.3</version>
    </dependency>
</dependencies>
<repositories>
    <repository>
        <id>jitpack.io</id>
        <url>https://jitpack.io</url>
    </repository>
</repositories>

3.應(yīng)用

我們創(chuàng)建一個(gè)普通的 Spring Boot 項(xiàng)目,引入 web 依賴,再引入我們剛剛的 starter 依賴,如下:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.github.lenve</groupId>
        <artifactId>encrypt-spring-boot-starter</artifactId>
        <version>0.0.3</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>
<repositories>
    <repository>
        <id>jitpack.io</id>
        <url>https://jitpack.io</url>
    </repository>
</repositories>

然后再創(chuàng)建一個(gè)實(shí)體類備用:

public class User {
    private Long id;
    private String username;
    //省略 getter/setter
}

創(chuàng)建兩個(gè)測(cè)試接口:

@RestController
public class HelloController {
    @GetMapping("/user")
    @Encrypt
    public RespBean getUser() {
        User user = new User();
        user.setId((long) 99);
        user.setUsername("javaboy");
        return RespBean.ok("ok", user);
    }

    @PostMapping("/user")
    public RespBean addUser(@RequestBody @Decrypt User user) {
        System.out.println("user = " + user);
        return RespBean.ok("ok", user);
    }
}

第一個(gè)接口使用了 @Encrypt 注解,所以會(huì)對(duì)該接口的數(shù)據(jù)進(jìn)行加密(如果不使用該注解就不加密),第二個(gè)接口使用了 @Decrypt 所以會(huì)對(duì)上傳的參數(shù)進(jìn)行解密,注意 @Decrypt 注解既可以放在方法上也可以放在參數(shù)上。

接下來啟動(dòng)項(xiàng)目進(jìn)行測(cè)試。

首先測(cè)試 get 請(qǐng)求接口:

可以看到,返回的數(shù)據(jù)已經(jīng)加密。

再來測(cè)試 post 請(qǐng)求:

可以看到,參數(shù)中的加密數(shù)據(jù)已經(jīng)被還原了。

如果用戶想要修改加密密鑰,可以在 application.properties 中添加如下配置:

spring.encrypt.key=1234567890123456

加密數(shù)據(jù)到了前端,前端也有一些 js 工具來處理加密數(shù)據(jù)。

4.小結(jié)

好啦,今天這篇文章主要是想和大家聊聊 ResponseBodyAdvice 和 RequestBodyAdvice 的用法,一些加密思路,當(dāng)然 ResponseBodyAdvice 和 RequestBodyAdvice 還有很多其他的使用場(chǎng)景,小伙伴們可以自行探索~本文使用了對(duì)稱加密中的 AES 算法,大家也可以嘗試改成非對(duì)稱加密。

到此這篇關(guān)于SpringBoot實(shí)現(xiàn)接口參數(shù)加密解密的示例代碼的文章就介紹到這了,更多相關(guān)SpringBoot接口參數(shù)加密解密內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 解決Spring Security 用戶帳號(hào)已被鎖定問題

    解決Spring Security 用戶帳號(hào)已被鎖定問題

    這篇文章主要介紹了解決Spring Security 用戶帳號(hào)已被鎖定問題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-01-01
  • SpringBoot項(xiàng)目啟動(dòng)時(shí)如何讀取配置以及初始化資源

    SpringBoot項(xiàng)目啟動(dòng)時(shí)如何讀取配置以及初始化資源

    這篇文章主要給大家介紹了關(guān)于SpringBoot項(xiàng)目啟動(dòng)時(shí)如何讀取配置以及初始化資源的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者使用SpringBoot具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • Springboot實(shí)現(xiàn)郵件發(fā)送功能

    Springboot實(shí)現(xiàn)郵件發(fā)送功能

    這篇文章主要為大家詳細(xì)介紹了Springboot實(shí)現(xiàn)郵件發(fā)送功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-02-02
  • Java實(shí)現(xiàn)讀取及生成Excel文件的方法

    Java實(shí)現(xiàn)讀取及生成Excel文件的方法

    這篇文章主要介紹了Java實(shí)現(xiàn)讀取及生成Excel文件的方法,結(jié)合實(shí)例形式分析了java通過引入第三方j(luò)ar包poi-3.0.1-FINAL-20070705.jar實(shí)現(xiàn)針對(duì)Excel文件的讀取及生成功能,需要的朋友可以參考下
    2017-12-12
  • Java創(chuàng)建型設(shè)計(jì)模式之抽象工廠模式(Abstract?Factory)

    Java創(chuàng)建型設(shè)計(jì)模式之抽象工廠模式(Abstract?Factory)

    當(dāng)系統(tǒng)所提供的工廠所需生產(chǎn)的具體產(chǎn)品并不是一個(gè)簡(jiǎn)單的對(duì)象,而是多個(gè)位于不同產(chǎn)品等級(jí)結(jié)構(gòu)中屬于不同類型的具體產(chǎn)品時(shí)需要使用抽象工廠模式,抽象工廠模式是所有形式的工廠模式中最為抽象和最具一般性的一種形態(tài)
    2022-09-09
  • Java?Maven?Settings配置參考教程

    Java?Maven?Settings配置參考教程

    這篇文章主要介紹了Java?Maven?Settings配置參考,本文通過示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-09-09
  • Spring學(xué)習(xí)筆記之bean生命周期

    Spring學(xué)習(xí)筆記之bean生命周期

    Spring Bean是Spring應(yīng)用中最最重要的部分了。下面這篇文章主要給大家介紹了關(guān)于Spring學(xué)習(xí)筆記之bean生命周期的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-12-12
  • Java利用多線程和分塊實(shí)現(xiàn)快速讀取文件

    Java利用多線程和分塊實(shí)現(xiàn)快速讀取文件

    在工作中經(jīng)常會(huì)有接收文件并且讀取落庫的需求,讀取方式都是串行讀取,所以本文主要為大家介紹一下如何利用多線程和分塊實(shí)現(xiàn)快速讀取文件,希望對(duì)大家有所幫助
    2023-09-09
  • Spring常用數(shù)據(jù)源的xml配置詳解

    Spring常用數(shù)據(jù)源的xml配置詳解

    這篇文章主要介紹了Spring常用數(shù)據(jù)源的xml配置詳解,數(shù)據(jù)源是連接到數(shù)據(jù)庫的一類路徑,它包含了訪問數(shù)據(jù)庫的信息(地址、用戶名、密碼),數(shù)據(jù)源就像是排水管道,需要的朋友可以參考下
    2023-07-07
  • servlet的常見注冊(cè)方式總結(jié)

    servlet的常見注冊(cè)方式總結(jié)

    servlet大家都不陌生,當(dāng)開發(fā)?Web?應(yīng)用程序時(shí),注冊(cè)?Servlet?是一個(gè)常見的任務(wù),本文將介紹一些常見的?Servlet?注冊(cè)方法,希望對(duì)大家有所幫助
    2023-10-10

最新評(píng)論