團隊開發的項目,前端基於Bootstrap+AngularJS,後端Spring MVC以RESTful接口給前端調用。開發和部署都是先後端分離。項目簡單部署圖以下,由於後臺同時採用微服務的方式,因此後臺不止3個,畫圖示意。終極方案是採用Docker,在前端和後臺調用中間添加一層:API Gateway。javascript
由於考慮到和其餘系統集成的可能性,因此在登陸這一塊使用了Token來作登陸,認證服務器負責生成Token和驗證Token。由於客戶須要提升系統的安全性,須要在登陸頁添加一個驗證碼。可是由於項目是基於先後端分離的,因此添加驗證碼的功能仍是有一些不同。html
有經驗的開發者第一反應就是以前驗證碼怎麼添加的,如今在這裏也是一樣的道理,爲何不同呢?由於先後端分離,系統登陸使用的是Token,後臺再也不設置Session了。後臺必須保證當前用戶輸入的驗證碼是用戶開始請求頁面時候的驗證碼,必須保證驗證碼的惟一性。舉個例子:前端
A用戶看到的驗證碼是:ABC;B用戶看到的驗證碼是:DEF。後臺存儲了ABC和DEF這2個驗證碼,若是不限定A用戶輸入的驗證碼是ABC,那麼當A用戶碰巧輸入DEF,而後用戶名和密碼也是正確的話,A用戶也是能夠登陸系統的。java
在早期可使用Session系統中,後臺返回驗證碼信息同時寫入一個session,有一個SessionID的字段和當前這個驗證碼對應。因此當用戶輸入用戶名、密碼和驗證碼的時候,瀏覽器自動把存有session信息的cookie發送到服務器,服務器基於Session能夠判斷當前這個驗證碼確實是A用戶應該要輸入的。angularjs
缺點:爲了考慮到後續和其餘系統集成,同時後臺部署是多臺服務器,採用的API網關。已經使用了Token,若是爲了驗證碼這個功能,引入Session,有點得不償失。後端
不能使用Session,那隻能考慮無Session的方案。要同時獲取驗證碼和驗證碼對應的一個id值。做爲前端的我,第一反應是經過AngularJS中的$http
請求去獲取。可是後臺驗證碼是直接讀取圖片返回二進制流格式給到前端,因此不能額外返回一個ID字段。後端開發同事就說,那在Response Header裏面返回一個id的字段,和驗證碼的值相關聯起來。到如今聽起來一切都很順利。前端代碼以下:瀏覽器
//控制器層代碼 $scope.getCaptcha = function () { loginService.getCaptcha().success(function (response, status, headers) { $scope.id = headers().id; if (response.size > 0) { $scope.verificationImage = window.URL.createObjectURL(response); } else { toastr.error("獲取驗證碼失敗:" + response.message); } }).error(function (response) { console.log(response); }); } //服務層代碼 getCaptcha: function () { return $http({ method: "GET", responseType: "blob", url: AppConfig.userServerUrl + "/user/Captcha/request" }); },
前端AngularJS代碼沒法獲取header頭部額外字段,能獲取的字段以下:安全
在stackoverflow上搜索一番,解決辦法是後臺須要設置容許前端瀏覽器能獲取header頭部裏面的字段。後臺同事修改以後,response header裏面信息以下圖所示:服務器
同域和跨越解決辦法:How to read response headers in angularjs?cookie
覺得大功告成,而後在IE9瀏覽器上測試一下,發現沒法加載到驗證碼,並且控制檯報錯誤。折騰半天,發現IE9不支持window.URL.createObjectURL();
,並且AngularJS發送請求加載二進制流文件就報錯。
爲了支持IE9,目前解決方法是讓後臺不返回二進制流文件,而是返回base64編碼的字符串,這樣IE9也是支持的。
和以前同事交流一番,同事提出了一個可選的方案。由於咱們在請求驗證碼的時候有2個內容,一個是驗證碼id,一個驗證碼圖片。其實驗證碼id能夠在前端使用隨機數生成一個,而後前端把這個id傳入後臺,後臺根據這個id,而後加一些特殊字符,拼接以後一個惟一字符,同時生成一個圖片,這個惟一字符和這個驗證碼圖片關聯起來,而後將圖片返回base64編碼內容返回到前端。這種能夠不須要前端發送Ajax請求,直接在圖片上使用ng-src
。
<img ng-src="http://www.example.com?uid=1001cmss" >