先後端API交互如何保證數據安全性

先後端分離的開發方式,咱們以接口爲標準來進行推進,定義好接口,各自開發本身的功能,最後進行聯調整合。不管是開發原生的APP仍是webapp仍是PC端的軟件,只要是先後端分離的模式,就避免不了調用後端提供的接口來進行業務交互。css

網頁或者app,只要抓下包就能夠清楚的知道這個請求獲取到的數據,這樣的接口對爬蟲工程師來講是一種福音,要抓你的數據簡直垂手可得。前端

數據的安全性很是重要,特別是用戶相關的信息,稍有不慎就會被不法分子盜用,因此咱們對這塊要很是重視,容不得馬虎。java

如何保證API調用時數據的安全性?

  1. 通訊使用httpsios

  2. 請求籤名,防止參數被篡改git

  3. 身份確認機制,每次請求都要驗證是否合法github

  4. APP中使用ssl pinning防止抓包操做web

  5. 對全部請求和響應都進行加解密操做算法

  6. 等等方案…….spring

對全部請求和響應都進行加解密操做

方案有不少種,當你作的越多,也就意味着安全性更高,今天我跟你們來介紹一下對全部請求和響應都進行加解密操做的方案,即便能抓包,即便能調用個人接口,可是我返回的數據是加密的,只要加密算法夠安全,你獲得了個人加密內容也對我沒什麼影響。數據庫

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

爲此,我基於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件事情:

  1. 統一處理數據的響應,在渲染到頁面以前進行解密操做

  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慢的緣由都是由於須要讓客戶端與服務器端安全地協商出一個對稱加密算法。剩下的就是通訊時雙方使用這個對稱加密算法進行加密解密。

  1. 客戶端啓動,發送請求到服務端,服務端用RSA算法生成一對公鑰和私鑰,咱們簡稱爲pubkey1,prikey1,將公鑰pubkey1返回給客戶端。

  2. 客戶端拿到服務端返回的公鑰pubkey1後,本身用RSA算法生成一對公鑰和私鑰,咱們簡稱爲pubkey2,prikey2,並將公鑰pubkey2經過公鑰pubkey1加密,加密以後傳輸給服務端。

  3. 此時服務端收到客戶端傳輸的密文,用私鑰prikey1進行解密,由於數據是用公鑰pubkey1加密的,經過解密就能夠獲得客戶端生成的公鑰pubkey2

  4. 而後本身在生成對稱加密,也就是咱們的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 {
   /**
    * 配置請求解密
    * @return
    */
   @Bean
   public EncryptResponseBodyAdvice encryptResponseBodyAdvice() {
       return new EncryptResponseBodyAdvice();
   }
   /**
    * 配置請求加密
    * @return
    */
   @Bean
   public EncryptRequestBodyAdvice encryptRequestBodyAdvice() {
       return new EncryptRequestBodyAdvice();
   }
}

經過RequestBodyAdvice和ResponseBodyAdvice就能夠對請求響應作處理了,大概的原理就是這麼多了

相關文章
相關標籤/搜索