一、接口為什么要加密
接口加密傳輸,主要作用:
- 敏感數(shù)據(jù)防止泄漏、
- 保護(hù)隱私、
- 防偽裝攻擊、
- 防篡改攻擊、
- 防重放攻擊
- 等等…
- 4個(gè)字概括:保護(hù)數(shù)據(jù)!
當(dāng)然不是說(shuō)接口加密后,就能完完全全的保護(hù)我們的數(shù)據(jù),但至少能防一部分人拿到我們的數(shù)據(jù)。
而且接口加密感覺(jué)逼格是不是高過(guò)一點(diǎn)!!!
二、加密思路
1、加密簡(jiǎn)介
加密算法有很多,在能加密又能解密的算法可分為:
- 非對(duì)稱加密算法,常見(jiàn):RSA、DSA、ECC
特點(diǎn):算法復(fù)雜,加解密速度慢,但安全性高,一般與對(duì)稱加密結(jié)合使用(對(duì)稱加密對(duì)內(nèi)容加密,非對(duì)稱對(duì)對(duì)稱所使用的密鑰加密) - 對(duì)稱加密算法,常見(jiàn):DES、3DES、AES、Blowfish、IDEA、RC5、RC6
特點(diǎn):加密解密效率高,速度快,適合進(jìn)行大數(shù)據(jù)量的加解密
2、加密流程
思路:
假設(shè)現(xiàn)在客戶端是A,服務(wù)端是B,現(xiàn)在A要去B請(qǐng)求接口
- 1、A要向B發(fā)送信息,A和B都要產(chǎn)生一對(duì)用于加密的非對(duì)稱加密公私鑰(AB各自生成自己的公私鑰)
- 2、A的私鑰保密,A的公鑰告訴B;B的私鑰保密,B的公鑰告訴A。(AB互換公鑰)
- 3、A要給B發(fā)送信息時(shí),A用B的公鑰加密信息,因?yàn)锳知道B的公鑰。(公鑰加密只有私鑰能解)
- 4、A將這個(gè)消息發(fā)給B(已經(jīng)用B的公鑰加密消息)。
- 5、B收到這個(gè)消息后,B用自己的私鑰解密A的消息。其他人收到這個(gè)報(bào)文都無(wú)法解密,因?yàn)橹挥蠦才有B的私鑰。
雖然這樣就實(shí)現(xiàn)了接口的加密方式,但是呢,非對(duì)稱加密的加解密速度相比對(duì)稱加密速度很慢,當(dāng)傳輸?shù)臄?shù)據(jù)很大時(shí)就更加明顯了。
所以我們對(duì)稱與非對(duì)稱一起用,理解上面的流程之后,我們?cè)谄浠A(chǔ)稍微改下:
- 在A給B發(fā)信息的時(shí)候,隨機(jī)生成一個(gè)對(duì)稱加密的密鑰,然后用剛生成的密鑰加密信息,然后用B的公鑰加密剛生成的對(duì)稱密鑰。
- A把加密的兩個(gè)信息發(fā)送給B。B收到數(shù)據(jù)之后,先用自己的私鑰解開(kāi)得到對(duì)稱密鑰,然后再用解開(kāi)的對(duì)稱密鑰解開(kāi)對(duì)稱加密的信息,最終得到A傳來(lái)的信息。
三、代碼實(shí)現(xiàn)
- 在當(dāng)下JAVA還是SpringBoot為主流框架工作面試必備,今天還是以它來(lái)舉例。
- 加解密代碼怎么寫(xiě),這個(gè)時(shí)候網(wǎng)上已經(jīng)有很多現(xiàn)成的庫(kù)了,不用我們操心,我們想的是如何在接口加解密的時(shí)候不影響我們自己的業(yè)務(wù),也就是不用更改我們已經(jīng)寫(xiě)好的代碼。
- 很多人的第一反應(yīng)應(yīng)該就是AOP吧,對(duì)的沒(méi)錯(cuò)可以使用AOP進(jìn)行環(huán)繞增強(qiáng)。也可以使用@ControllerAdvice 對(duì)Controller進(jìn)行增強(qiáng)(本文以它來(lái)做為例子)。
- Spring 提供兩個(gè)接口RequestBodyAdvice、ResponseBodyAdvice。實(shí)現(xiàn)它們,即可對(duì)Controller進(jìn)行增強(qiáng),第一個(gè)是在controller之前增強(qiáng),第二個(gè)就是對(duì)controller 的返回值進(jìn)行增強(qiáng)。
- 在spring啟動(dòng)的時(shí)候會(huì)對(duì)RequestMAppingHandlerAdapter的initControllerAdviceCache()方法進(jìn)行初始化。會(huì)去把有@ControllerAdvice的類進(jìn)行注入。
1、自定義類
下面就來(lái)實(shí)現(xiàn)上面的兩個(gè)接口實(shí)現(xiàn)類代碼
EncryptRequestAdvice.java
- 這個(gè)類的功能就是在請(qǐng)求到controller之前就把前端傳上來(lái)的數(shù)據(jù)解密好
- 我們還要校驗(yàn)是否有必要解密
|
- 在上面實(shí)現(xiàn)類中需要重寫(xiě):supports()、beforeBodyRead()、afterBodyRead()、handleEmptyBody() 方法
- 只有在supports() 返回true 后面的方法才會(huì)支持執(zhí)行。在RequestResponseBodyAdviceChain有判斷
- 我們可以在beforeBodyRead()這個(gè)方法進(jìn)行解密處理。
- 在上面的代碼中,我加了自定義注解,因?yàn)榭赡苄枨笫沁@樣的,有些接口加密有些接口不加密,用自定義注解比較方便。
- 然后DecodeInputMessage 這個(gè)類是自定義實(shí)現(xiàn)了HttpInputMessage接口,解碼邏輯都在里面。如下:
DecodeInputMessage.java
這個(gè)類就是具體的解碼邏輯了
|
- 上面的代碼注釋我覺(jué)得都寫(xiě)的清楚了,不多介紹。
EncryptResponseAdvice.java
- 這個(gè)類的主要功能就是對(duì)返回值進(jìn)行加密操作
- 直接在beforeBodyWrite()里面執(zhí)行具體的加密操作即可
- supports()方法也是需要返回true,在RequestResponseBodyAdviceChain.processBody()中有個(gè)判斷只有supports()返回true才會(huì)執(zhí)行beforeBodyWrite()
|
看代碼注釋,不說(shuō)了。
2、加密工具類
加密工具類,我在網(wǎng)上收集整理了一下,搞了個(gè)jar。直接在pom.xml 引入即可。如下:
|
自此核心代碼都講完了,這里只是給出了個(gè)demo,可以參考一下(代碼寫(xiě)的也不是很好,很多地方也沒(méi)有封裝),加密方式多種多樣,都是可以自由更改,這種加密方式不喜歡就改。
差點(diǎn)忘記了,前端代碼呢。
3、前端代碼
前端也是在Github分別找了兩個(gè)庫(kù):
- jsencrypt
這個(gè)是RSA加密庫(kù),這個(gè)是在原版的jsencrypt進(jìn)行增強(qiáng)修改,原版的我用過(guò)太長(zhǎng)數(shù)據(jù)加密失敗,多此加密解密失敗,所以就用了這個(gè)庫(kù)。 - CryptoJS
AES加密庫(kù),這個(gè)庫(kù)是google開(kāi)源的,有AES、MD5、SHA 等加密方法
然后我使用的是Vue寫(xiě)的簡(jiǎn)單頁(yè)面(業(yè)余前端)
html
|
主要看testRequest() 這個(gè)方法就行了,都有代碼注釋。
注意點(diǎn)
- 后端需要注意的就是,controller參數(shù)需要用@RequestBody包起來(lái),如下:
- @PostMapping("/test1")
@ResponseBody
public Object test1(@RequestBody(required = false) TestDto dto){
System.out.println("dto="+dto);
return Result.ok(dto);} - 而前端傳上來(lái)的時(shí)候header需要設(shè)置"Content-Type": "application/json;charset=utf-8"
最終效果
在上面的postman中
- data:里面的數(shù)據(jù)就是aes加密后的數(shù)據(jù)
- key:里面就是前端RSA公鑰加密后的AES密鑰(前端需要用私鑰解密得到aes密鑰,然后再用密鑰解開(kāi)data里面的數(shù)據(jù))
- status:這個(gè)是狀態(tài)碼,如果報(bào)錯(cuò)了就不是200,不然報(bào)錯(cuò)了返回的數(shù)據(jù),前端解幾百年都解不開(kāi)。
4、源碼地址
https://gitee.com/rstyro/spring-boot/tree/master/Springboot2-api-encrypt
- 作者:rstyro
- 來(lái)源: https://rstyro.github.io/blog/2020/10/22/Springboot2接口加解密全過(guò)程詳解(含前端代碼)/






