天天看點

java資料安全_「Java程式設計」前後端API互動如何保證資料安全性

前言

前後端分離的開發方式,我們以接口為标準來進行推動,定義好接口,各自開發自己的功能,最後進行聯調整合。無論是開發原生的APP還是webapp還是PC端的軟體,隻要是前後端分離的模式,就避免不了調用後端提供的接口來進行業務互動。

網頁或者app,隻要抓下包就可以清楚的知道這個請求擷取到的資料,這樣的接口對爬蟲工程師來說是一種福音,要抓你的資料簡直輕而易舉。

資料的安全性非常重要,特别是使用者相關的資訊,稍有不慎就會被不法分子盜用,是以我們對這塊要非常重視,容不得馬虎。

如何保證API調用時資料的安全性?通信使用https

請求簽名,防止參數被篡改

身份确認機制,每次請求都要驗證是否合法

APP中使用ssl pinning防止抓包操作

對所有請求和響應都進行加解密操作

等等方案…….

對所有請求和響應都進行加解密操作

方案有很多種,當你做的越多,也就意味着安全性更高,今天我跟大家來介紹一下對所有請求和響應都進行加解密操作的方案,即使能抓包,即使能調用我的接口,但是我傳回的資料是加密的,隻要加密算法夠安全,你得到了我的加密内容也對我沒什麼影響。

像這種工作最好做成統一處理的,你不能讓每個開發都去關注這件事情,如果讓每個開發去關注這件事情就很麻煩了,傳回資料時還得手動調用下加密的方法,接收資料後還得調用下解密的方法。

為此,我基于Spring Boot封裝了一個Starter, 内置了AES加密算法。GitHub位址如下:https://github.com/yinjihuan/spring-boot-starter-encrypt

先來看看怎麼使用,可以下載下傳源碼,然後引入即可,然後在啟動類上增加@EnableEncrypt注解開啟加解密操作:@EnableEncrypt

@SpringBootApplication

public class App {

public static void main(String[] args) {

SpringApplication.run(App.class, args);

}

}

增加加密的key配置:spring.encrypt.key=abcdef0123456789

spring.encrypt.debug=false

spring.encrypt.key:加密key,必須是16位

spring.encrypt.debug:是否開啟調試模式,預設為false,如果為true則不啟用加解密操作

為了考慮通用性,不會對所有請求都執行加解密,基于注解來做控制

響應資料需要加密的話,就在Controller的方法上加@Encrypt注解即可。@Encrypt

@GetMapping("/list")

public Response queryNews(String city) {

return Response.ok(city);

}

當我們通路/list接口時,傳回的資料就是加密之後base64編碼的格式。

還有一種操作就是前段送出的資料,分為2種情況,一種是get請求,這種暫時沒處理,後面再考慮,目前隻處理的post請求,基于json格式送出的方式,也就是說背景需要用@RequestBody接收資料才行, 需要解密的操作我們加上@Decrypt注解即可。@Decrypt

@PostMapping("/save")

public Response savePageLog(@RequestBody PageLogParam logParam, HttpServletRequest request) {

pageLogService.save(logParam);

return Response.ok();

}

加了@Decrypt注解後,前端送出的資料需要按照AES加密算法,進行加密,然後送出到後端,後端這邊會自動解密,然後再映射到參數對象中。

上面講解的都是後端的代碼,前端使用的話我們以js來講解,當然你也能用别的語言來做,如果是原生的安卓app也是用java代碼來處理。

前端需要做的就2件事情:統一處理資料的響應,在渲染到頁面之前進行解密操作

當有POST請求的資料發出時,統一加密js加密檔案請參考我GitHub中encrypt中的aes.js,crypto-js.js,pad-zeropadding.js

我們以axios來作為請求資料的架構,用axios的攔截器來統一處理加密解密操作

首先還是要封裝一個js加解密的類,需要注意的是加密的key需要和背景的對上,不然無法互相解密,代碼如下:var key = CryptoJS.enc.Latin1.parse('abcdef0123456789');

var iv = CryptoJS.enc.Latin1.parse('abcdef0123456789');

// 加密

function EncryptData(data) {

var srcs = CryptoJS.enc.Utf8.parse(data);

var encrypted = CryptoJS.AES.encrypt(srcs, key, {

mode : CryptoJS.mode.ECB,

padding : CryptoJS.pad.Pkcs7

});

return encrypted.toString();

}

// 解密

function DecryptData(data) {

var stime = new Date().getTime();

var decrypt = CryptoJS.AES.decrypt(data, key, {

mode : CryptoJS.mode.ECB,

padding : CryptoJS.pad.Pkcs7

});

var result = JSON.parse(

CryptoJS.enc.Utf8.stringify(decrypt).toString()

);

var etime = new Date().getTime();

console.log("DecryptData Time:" + (etime - stime));

return result;

}

axios攔截器中統一處理代碼:// 添加請求攔截器

axios.interceptors.request.use(function (config) {

// 對所有POST請加密,必須是json資料送出,不支援表單

if (config.method == "post") {

config.data = EncryptData(JSON.stringify(config.data));

}

return config;

}, function (error) {

return Promise.reject(error);

});

// 添加響應攔截器

axios.interceptors.response.use(function (response) {

// 後端傳回字元串表示需要解密操作

if(typeof(response.data) == "string"){

response.data = DecryptData(response.data);

}

return response;

}, function (error) {

return Promise.reject(error);

});

到此為止,我們就為整個前後端互動的通信做了一個加密的操作,隻要加密的key不洩露,别人得到你的資料也沒用,問題是如何保證key不洩露呢?

服務端的安全性較高,可以存儲在資料庫中或者配置檔案中,畢竟在我們自己的伺服器上,最危險的其實就時前端了,app還好,可以打包,但是要防止反編譯等等問題。

如果是webapp則可以依賴于js加密來實作,下面我給大家介紹一種動态擷取加密key的方式,隻不過實作起來比較複雜,我們不上代碼,隻講思路:

加密算法有對稱加密和非對稱加密,AES是對稱加密,RSA是非對稱加密。之是以用AES加密資料是因為效率高,RSA運作速度慢,可以用于簽名操作。

我們可以用這2種算法互補,來保證安全性,用RSA來加密傳輸AES的秘鑰,用AES來加密資料,兩者互相結合,優勢互補。

其實大家了解了HTTPS的原理的話對于下面的内容應該是一看就懂的,HTTPS比HTTP慢的原因都是因為需要讓用戶端與伺服器端安全地協商出一個對稱加密算法。剩下的就是通信時雙方使用這個對稱加密算法進行加密解密。

用戶端啟動,發送請求到服務端,服務端用RSA算法生成一對公鑰和私鑰,我們簡稱為pubkey1,prikey1,将公鑰pubkey1傳回給用戶端。

用戶端拿到服務端傳回的公鑰pubkey1後,自己用RSA算法生成一對公鑰和私鑰,我們簡稱為pubkey2,prikey2,并将公鑰pubkey2通過公鑰pubkey1加密,加密之後傳輸給服務端。

此時服務端收到用戶端傳輸的密文,用私鑰prikey1進行解密,因為資料是用公鑰pubkey1加密的,通過解密就可以得到用戶端生成的公鑰pubkey2。然後,自己在生成對稱加密,也就是我們的AES,其實也就是相對于我們配置中的那個16的長度的加密key,生成了這個key之後我們就用公鑰pubkey2進行加密,傳回給用戶端,因為隻有用戶端有pubkey2對應的私鑰prikey2,隻有用戶端才能解密,用戶端得到資料之後,用prikey2進行解密操作,得到AES的加密key,最後就用加密key進行資料傳輸的加密,至此整個流程結束。

spring-boot-starter-encrypt原理

最後我們來簡單的介紹下spring-boot-starter-encrypt的原理吧,也讓大家能夠了解為什麼Spring Boot這麼友善,隻需要簡單的配置一下就可以實作很多功能。

啟動類上的@EnableEncrypt注解是用來開啟功能的,通過@Import導入自動配置類@Target({ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Inherited

@Import({EncryptAutoConfiguration.class})

public @interface EnableEncrypt {

}

EncryptAutoConfiguration中配置請求和響應的處理類,用的是Spring中的RequestBodyAdvice和ResponseBodyAdvice,在Spring中對請求進行統計處理比較友善。如果還要更底層去封裝那就要從servlet那塊去處理了。@Configuration

@Component

@EnableAutoConfiguration

@EnableConfigurationProperties(EncryptProperties.class)

public class EncryptAutoConfiguration {

@Bean

public EncryptResponseBodyAdvice encryptResponseBodyAdvice() {

return new EncryptResponseBodyAdvice();

}

@Bean

public EncryptRequestBodyAdvice encryptRequestBodyAdvice() {

return new EncryptRequestBodyAdvice();

}

}

通過RequestBodyAdvice和ResponseBodyAdvice就可以對請求響應做處理了,大概的原理就是這麼多了。

繼續閱讀