国产成人精品18p,天天干成人网,无码专区狠狠躁天天躁,美女脱精光隐私扒开免费观看

怎么實(shí)現 Spring Boot 接口參數加密解密

發(fā)布時(shí)間:2021-08-23 15:12 來(lái)源:億速云 閱讀:0 作者:chen 欄目: 網(wǎng)絡(luò )安全

本篇內容主要講解“怎么實(shí)現 Spring Boot 接口參數加密解密”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強。下面就讓小編來(lái)帶大家學(xué)習“怎么實(shí)現 Spring Boot 接口參數加密解密”吧!

加密解密本身并不是難事,問(wèn)題是在何時(shí)去處理?定義一個(gè)過(guò)濾器,將請求和響應分別攔截下來(lái)進(jìn)行處理也是一個(gè)辦法,這種方式雖然粗暴,但是靈活,因為可以拿到一手的請求參數和響應數據。不過(guò)  SpringMVC 中給我們提供了 ResponseBodyAdvice 和  RequestBodyAdvice,利用這兩個(gè)工具可以對請求和響應進(jìn)行預處理,非常方便。

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

  • 分享參數/響應加解密的思路。

  • 分享 ResponseBodyAdvice 和 RequestBodyAdvice 的用法。

好了,那么接下來(lái)就不廢話(huà)了,我們一起來(lái)看下。

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

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

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

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

因為我們這個(gè)工具是為 Web 項目開(kāi)發(fā)的,以后必然使用在 Web 環(huán)境中,所以這里添加依賴(lài)時(shí) scope 設置為 provided。

依賴(lài)添加完成后,我們先來(lái)定義一個(gè)加密工具類(lèi)備用,加密這塊有多種方案可以選擇,對稱(chēng)加密、非對稱(chēng)加密,其中對稱(chēng)加密又可以使用 AES、DES、3DES  等不同算法,這里我們使用 Java 自帶的 Cipher 來(lái)實(shí)現對稱(chēng)加密,使用 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è)工具類(lèi)比較簡(jiǎn)單,不需要多解釋。需要說(shuō)明的是,加密后的數據可能不具備可讀性,因此我們一般需要對加密后的數據再使用 Base64  算法進(jìn)行編碼,獲取可讀字符串。換言之,上面的 AES 加密方法的返回值是一個(gè) Base64 編碼之后的字符串,AES 解密方法的參數也是一個(gè) Base64  編碼之后的字符串,先對該字符串進(jìn)行解碼,然后再解密。

接下來(lái)我們封裝一個(gè)響應工具類(lèi)備用,這個(gè)大家如果經(jīng)??此筛缫曨l已經(jī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;     } }

接下來(lái)我們定義兩個(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è)標記,在以后使用的過(guò)程中,哪個(gè)接口方法添加了 @Encrypt 注解就對哪個(gè)接口的數據加密返回,哪個(gè)接口/參數添加了 @Decrypt  注解就對哪個(gè)接口/參數進(jìn)行解密。這個(gè)定義也比較簡(jiǎn)單,沒(méi)啥好說(shuō)的,需要注意的是 @Decrypt比 @Encrypt 多了一個(gè)使用場(chǎng)景就是 @Decrypt  可以用在參數上。

考慮到用戶(hù)可能會(huì )自己配置加密的 key,因此我們再來(lái)定義一個(gè) EncryptProperties 類(lèi)來(lái)讀取用戶(hù)配置的 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;     } }

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

所有準備工作做完了,接下來(lái)就該正式加解密了。

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

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

先來(lái)看接口加密:

@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 類(lèi)實(shí)現 ResponseBodyAdvice接口,泛型表示接口的返回類(lèi)型,這里一共要實(shí)現兩個(gè)方法:

  1. 鴻蒙官方戰略合作共建——HarmonyOS技術(shù)社區

  2. supports:這個(gè)方法用來(lái)判斷什么樣的接口需要加密,參數 returnType 表示返回類(lèi)型,我們這里的判斷邏輯就是方法是否含有 @Encrypt  注解,如果有,表示該接口需要加密處理,如果沒(méi)有,表示該接口不需要加密處理。

  3. beforeBodyWrite:這個(gè)方法會(huì )在數據響應之前執行,也就是我們先對響應數據進(jìn)行二次處理,處理完成后,才會(huì )轉成 json  返回。我們這里的處理方式很簡(jiǎn)單,RespBean 中的 status 是狀態(tài)碼就不用加密了,另外兩個(gè)字段重新加密后重新設置值即可。

  4. 另外需要注意,自定義的 ResponseBodyAdvice 需要用 @ControllerAdvice 注解來(lái)標記。

再來(lái)看接口解密:

@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);     } }
  1. 鴻蒙官方戰略合作共建——HarmonyOS技術(shù)社區

  2. 首先大家注意,DecryptRequest 類(lèi)我們沒(méi)有直接實(shí)現 RequestBodyAdvice 接口,而是繼承自  RequestBodyAdviceAdapter 類(lèi),該類(lèi)是 RequestBodyAdvice 接口的子類(lèi),并且實(shí)現了接口中的一些方法,這樣當我們繼承自  RequestBodyAdviceAdapter 時(shí),就只需要根據自己實(shí)際需求實(shí)現某幾個(gè)方法即可。

  3. supports:該方法用來(lái)判斷哪些接口需要處理接口解密,我們這里的判斷邏輯是方法上或者參數上含有 @Decrypt 注解的接口,處理解密問(wèn)題。

  4. beforeBodyRead:這個(gè)方法會(huì )在參數轉換成具體的對象之前執行,我們先從流中加載到數據,然后對數據進(jìn)行解密,解密完成后再重新構造  HttpInputMessage 對象返回。

接下來(lái),我們再來(lái)定義一個(gè)自動(dòng)化配置類(lèi),如下:

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

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

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

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

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

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

2.打包發(fā)布

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

2.1 安裝到本地倉庫

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

2.2 發(fā)布到線(xiàn)上

發(fā)不到線(xiàn)上我們可以使用 JitPack 來(lái)做。

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

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

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

構建成功后,JitPack 上會(huì )給出項目引用方式:

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

至此,我們的工具就已經(jīng)成功發(fā)布了!小伙伴們可以通過(guò)如下方式引用這個(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.應用

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

<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í)體類(lèi)備用:

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

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

@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ì )對該接口的數據進(jìn)行加密(如果不使用該注解就不加密),第二個(gè)接口使用了 @Decrypt  所以會(huì )對上傳的參數進(jìn)行解密,注意 @Decrypt 注解既可以放在方法上也可以放在參數上。

接下來(lái)啟動(dòng)項目進(jìn)行測試。

首先測試 get 請求接口:

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

再來(lái)測試 post 請求:

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

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

spring.encrypt.key=1234567890123456

加密數據到了前端,前端也有一些 js 工具來(lái)處理加密數據,這個(gè)后面有空再和大家說(shuō)說(shuō) js 的加解密。

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自互聯(lián)網(wǎng)轉載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權請聯(lián)系QQ:712375056 進(jìn)行舉報,并提供相關(guān)證據,一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容。

久久九九兔免费精品6| 中文字幕无码日韩专区| 每天更新的免费AV片在线观看| 五月天精品视频在线观看| 欧美在线 | 亚洲| YES4444视频在线观看|