token驗證明現

移動開發中必定會涉及後臺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的失效時間應該設置多長鬚要根據所在項目的實際狀況肯定。

相關文章
相關標籤/搜索