移動開發中必定會涉及後臺API如何訪問,如何控制訪問權限,保證系統安全等問題。
介紹一下我最近本身作的一個移動端訪問後端API的例子,例子寫的很粗糙,可是基本實現了以token auth 爲核心的demo。
在實現demo過程當中也找到了很多的好資料,這一篇《APP中用戶驗證方案》詳細的介紹了APP用戶驗證的不一樣方案。javascript
大致思路:html
APP登陸界面輸入用戶信息登陸;服務端驗證登陸信息,驗證成功,向APP端發送token,將token保存到數據庫或者緩存服務器中;APP登陸成功後,每次調用後端API時都須要帶着token、時間戳、sign(token+時間戳的md5字符串,sign能夠本身定義算法以防止被盜用)信息,以便後臺驗證訪問API權限;後端在接收到APP訪問請求時,比對APP發送來的token與緩存服務器中已經存在的token是否一致,並驗證token時效性。
好了,廢話少說,上代碼:前端
前端代碼(APP,最近寫的APP基本都是H5方式實現):java
<label>用戶名</label> <input type="text" name="username" id="username" value="" /> <br/> <label>密碼</label> <input type="password" name="password" id="password" value="" /> <input type="button" name="login" id="login" value="登陸" /> <script type="text/javascript"> document.getElementById("login").addEventListener('click',function () { //alert('hello'); //ajax提交驗證信息到後端 var username = document.getElementById("username").value; var password = document.getElementById("password").value; var data = "{\"username\":\"" + username +"\",\"password\":\"" + password + "\"}"; var url = "/AppTest/loginServlet"; var wd = "123"; var headData = ["123","123456789012345","gsfgfgbdf"]; postData(url,data, headData,function (backdata) { if(backdata != null){ console.log("返回的信息爲: " + backdata); } },wd); }); </script>
這裏的ostData使用的是封裝好的Ajax函數,下面一併放出代碼:web
ajax.js:ajax
/*對ajax進行簡易封裝,便於每次調用,省去參數設置*/ function postData(url, data, headData, callback, waitingDialog) { mui.ajax(url,{ data:data, dataType:'json', type:'post', headers: { "token" : headData[0], "timesamp" : headData[1], "sign" : headData[2] }, contentType:"application/x-www-form-urlencoded; charset=utf-8", timeout:20000, success:callback, error:function(xhr,type,errorThrown){ //waitingDialog.close(); alert("<網絡鏈接失敗,請從新嘗試一下>", "錯誤", "OK", null); } }); }
ajax中加上了headers參數,這裏加headers參數的目的是,後臺使用servlet+filter的方式控制訪問,若是將token等其餘參數直接和表單數據同時提交,filter在獲取token信息後,servlet就不能獲取到表單數據,這就比較尷尬了,或者後臺使用AOP的方式控制訪問權限 ,這個尚未來得及嘗試。若是我能把token信息加到於表單數據不一樣的存儲區域就行了,Google後找到了能夠把數據加到請求的header中能夠實現將token數據與表單數據分離的效果。
過濾器(Java實現 JDK1.8 Tomcat8.5.6):redis
import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import me.wlc.wx.web.tool.Md5; /** * 使用註解標註過濾器 * @WebFilter將一個實現了javax.servlet.Filte接口的類定義爲過濾器 * 屬性filterName聲明過濾器的名稱,可選 * 屬性urlPatterns指定要過濾 的URL模式,也可以使用屬性value來聲明.(指定要過濾的URL模式是必選屬性) * urlPatterns="/*" 表示過濾掉全部請求 */ @WebFilter(filterName="AccessFilter",urlPatterns="/*") public class AccessFilter implements Filter { @Override public void destroy() { System.out.println("過濾器銷燬"); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("執行過濾操做"); HttpServletRequest req = (HttpServletRequest) request; String uri = req.getRequestURI(); System.out.println("uri is : " + uri); //對請求的uri(即api)進行判斷,若是是登陸的uri則直接放行,若是是其餘api則對sign進行驗證操做 if( !uri.endsWith("loginServlet") ){ //從請求的url中取出token、時間戳、sign String token = req.getHeader("token"); String timesamp = req.getHeader("timesamp"); String sign = req.getHeader("sign"); System.out.println("sign is : " + sign); StringBuffer requestUrl = req.getRequestURL(); System.out.println("請求的Url是: " + requestUrl); //對token、timesamp 進行md5計算 String signMd5 = Md5.getMD5(token + timesamp); if(sign.equals(signMd5)){ //簽名經過 chain.doFilter(request, response); }else{ //簽名不經過,向app後端發送錯誤信息,提示從新登陸 } }else{ //登陸操做 chain.doFilter(request, response); } //請求經過 //chain.doFilter(request, response); } @Override public void init(FilterConfig arg0) throws ServletException { System.out.println("過濾器初始化"); } }
後端尚未實現驗證token時效性功能,也沒有將token存儲到緩存服務器中,這些須要具體方案定下來之後再加上了。再說一句,生成的token保證token值惟一便可,可使用最方便的UUID(Java中)。算法
大致就是這樣了,有問題隨時提給我哦!數據庫
PS:2018年9月28日更新
一直沒有登陸查看留言,給留言板的各位兄弟道個歉,沒有及時回覆
token存儲方案:json
方案一:使用session的超時時間來控制token的失效時間
方案二:使用redis存儲token的失效時間
方案一適用於單機環境,能夠做爲小型系統的token時效性驗證方案,此方案若處於分佈式環境會致使session在分佈式機器間的時間不一樣步,但跟組裏的其餘工程師溝通,在分佈式環境下是否是還能夠同步session?
方案二適用於大型系統,還要使用redis服務,使用redis存儲token但是實現快速讀取token時效數據,若將toke失效時間存儲到數據庫中,頻繁操做數據庫,會影響數據庫的響應時間,進而應用程序也會出現卡頓等狀況。
另,token的失效時間應該設置多長鬚要根據所在項目的實際狀況肯定。