APP接口開發token安全之請求校驗規則

移動應用開發過程當中請求服務端採用token(在計算機身份認證中是令牌(臨時))方式請求方式進行,get請求方式下token直接暴露在請求路徑很容易被別人利用進行篡改進行重複提交等,怎樣保證移動端安全成爲後臺開發者所面臨的問題,,由於涉及敏感行業數據APP接口開發過程當中安全性成爲要求,在網上看了不少資料最後選擇採用token+time+nonce+sign方式在過濾器層進行校驗,APP進行拼接加密,後端Filter進行解密校驗,優勢實現簡單,可以很好保證請求過程當中APP端到服務器端安全性,所以此種校驗方式被不少互聯網公司所採用。redis

1、技術實現原理:

token: token爲用戶登陸時獲取的臨時令牌
time: APP獲取到的當前時間,形式爲long類型,time時間可進行匹配APP獲取時間和服務器時間差若是大於某個時間則失效(如連接超過60秒失效,具體可參考源碼),可防止別人截取數據後修改數據內容進行提交
nonce:APP接口經過UUID等形式獲取的隨機不重複內容,能夠講nonce存放在session或redis中並設置合適的超時時間,而後下次請求時經過nonce取session或redis中是否存在,若是存在則認爲重複提交請求,防止被別人篡改後提交數據
sign: 經過token+time+nonce三個參數加密後的密文(sign加密可以使用使用ase,md5等加密方式,須要注意md5加密不可逆,實現過程有所區別),sign主要校驗token、time、nonce有沒有被別人篡改數據後端

2、技術實現過程:

1在過濾層實現SecurityFilter類

 public class SecurityFilter extends Filter {

private static String secretKey = ApplicationConfig.secretKey;
private static String ivParameter = ApplicationConfig.ivParameter ;
// 本文采用ase加密 將加密secretKey及ivParameter 的保存在properties文件中,使用公共接口加載,用戶能夠選擇其餘加密方式

@Override
protected void doDestroy() {
    // TODO Auto-generated method stub

}

@Override
protected void doFilter(HttpServletRequest request,
        HttpServletResponse response, FilterChain chain) throws Throwable {
    String requestUri = request.getRequestURI();
    //登陸接口則直接不適用
    if(requestUri.indexOf("applogin")>-1){
          chain.doFilter(request, response);
    } else {
        MsgEntity msg = new MsgEntity();
        // 獲取request的URL,用於判斷該請求是否超時
        String time = request.getParameter("time");
        // 獲取請求nonce,用於判斷是否用戶重複提交
        String nonce = request.getParameter("nonce");
        // 獲取token碼,實際傳入token值
        String token = request.getParameter("token");
        // 獲取sign,經過time,nonce,token合併進行加密後密文,須要注意加密順序,平臺必須與APP一致
        String sign = request.getParameter("sign");
        // 判斷數據是否存在
        if (StringUtils.isEmpty(time) || StringUtils.isEmpty(nonce)
                || StringUtils.isEmpty(token) || StringUtils.isEmpty(sign)) {
            msg.setMsg("登陸失敗");
            msg.setState(false);
            HttpRequestUtils.writeResponseJsonString(response, msg);
            return;
        }
        //在實際開發過程當中傳入數據發現存在%號沒法進行解密,須要進行URLDecoder.decode進行解碼
        if (sign.indexOf("%") > -1) {
            sign = URLDecoder.decode(sign, "UTF-8");
        }
        // 判斷請求是否超過60秒,超過60秒則次請求連接失效,返回登陸失敗,可根據網絡狀況適當延長或者縮短期
        if (difference(time) > 60) {
            msg.setMsg("登陸失敗");
            msg.setState(false);
            HttpRequestUtils.writeResponseJsonString(response, msg);
            return;
        }
        // 判斷是不是重複請求,若是請求nonce存在則登陸失敗,不存在則將nonce存在redis中或session中並設置合適超時時間
        if (JedisUtils.exists(nonce)) {
            msg.setState(false);
            msg.setMsg("登陸失敗");
            HttpRequestUtils.writeResponseJsonString(response, msg);
            return;
        } else {
            JedisUtils.set(nonce, "0", 70);
        }
         
        // aes 解密,sign和token + time + nonce進行比較判斷數據數據是否存在篡改
        String mingwei = "";
        try {
            mingwei = AesUtils.decrypt(sign);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        if (!mingwei.equals(token + time + nonce)) {
            msg.setState(false);
            msg.setMsg("登陸失敗");
            HttpRequestUtils.writeResponseJsonString(response, msg);
            return;
        }
         //若是請求所有驗證經過則放行
        chain.doFilter(request, response);
    }

3、使用到的工具方法

//主要用於系統時間-APP發送數據時生成日期獲得當前差值(秒)
public int difference(String past) {
    long newTimestamp = new Date().getTime();
    return Math.abs((int) (newTimestamp - Long.parseLong(past)) / 1000);
}
   }
    
        /**
 * aes 解密
 * 
 * @param sSrc
 * @return
 * @throws Exception
 */
public static String decrypt(String sSrc) throws Exception {
    try {
        byte[] raw = secretKey.getBytes("ASCII");
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance("AES / CBC / PKCS5Padding");
        IvParameterSpec iv = new IvParameterSpec(ivParameter.getBytes());
        cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
        byte[] encrypted1 = new BASE64Decoder().decodeBuffer(sSrc);// 先用base64解密
        byte[] original = cipher.doFinal(encrypted1);
        String originalString = new String(original, "UTF-8");
        return originalString;
    } catch (Exception ex) {
        return null;
    }
}

以上爲過濾實現token校驗規則代碼,在開發過程當中能夠參考此代碼進行適當修改進行使用安全

相關文章
相關標籤/搜索