java安全入門篇之接口驗籤(原創)

文章大綱

一、加密與驗籤介紹
二、接口驗籤實操
三、項目源碼下載

 

一、加密與驗籤介紹

  大多數公共網絡是不安全的,一切基於HTTP協議的請求/響應(Request or Response)都是可以被截獲的、篡改、重放(重發)的。因此我們需要考慮以下幾點內容:

  1. 防僞裝攻擊(案例:在公共網絡環境中,第三方 有意或惡意 的調用我們的接口)
  2. 防篡改攻擊(案例:在公共網絡環境中,請求頭/查詢字符串/內容 在傳輸過程被修改)
  3. 防重放攻擊(案例:在公共網絡環境中,請求被截獲,稍後被重放或多次重放)
  4. 防數據信息泄漏(案例:截獲用戶登錄請求,截獲到賬號、密碼等)

二、接口驗籤實操

1. 實操說明

  接口加密與驗籤的方法有非常多,比如RSA(後期進行講解),基於token等方式,而對於普通項目,我認爲最重要的是防僞裝攻擊、防篡改攻擊、防重放攻擊。因爲接下來的實操,主要圍繞以下幾點進行。

2. 邏輯講解

客戶端操作
(1)用戶登錄成功後,會接收到對應的key值和key過期時間,該key是經過32位小寫加密,且編碼格式爲UTF-8
(2)接口請求時,將請求的參數,通過key-value方式進行字典升序,且編碼成UTF-8形式
(3)將key值拼接在升序且編碼後的字符串前面,進行MD32位小寫加密,其編碼成UTF-8形式形成簽名,連同請求參數一同發送至後臺
(4)退出登錄時,需要通知後臺失效該用戶的key
(5)補充說明1:對於登錄接口,如果檢測到用戶賬號密碼錯誤,則判斷錯誤次數後,在一定時間內進行登錄禁止,如果登錄禁止解除後,用戶再次出現錯誤,則延長限制時間
(6)補充說明2:對於無需登錄接口,需要限制客戶端請求次數,進行接口防刷保護

服務端操作
(1)當用戶登錄成功時,生成與該用戶對應的key值返回給用戶端,同時將id與key緩存在redis中
(2)當接收到請求時,根據請求id去redis查詢對應key是多少,查不到則代表沒有請求權限,將該用戶系統信息,請求項目名、接口名,請求時間、錯誤類型(用戶信息不正確/參數與簽名不一致)存進redis(緩存進磁盤),當ip錯誤次數超過一定次數後,限制ip訪問項目
(3)將key和請求參數按客戶端同樣方式進行簽名,與請求的sign進行比較
(4)如果驗籤不一致,將該用戶系統信息,請求項目名、接口名,請求時間、錯誤類型(用戶信息不正確/參數與簽名不一致)存進redis,當ip錯誤次數超過一定次數時,限制ip訪問所有項目,若驗籤通過,則進行接口放行,且將用戶系統信息,請求項目名、接口名,請求時間緩存進日誌中(存進磁盤)

Redis參數需記錄信息
1.用戶信息:id,用戶key,客戶端請求系統信息
2.驗籤錯誤信息:用戶系統信息,請求項目名、接口名、請求時間、錯誤類型(用戶信息不正確/參數與簽名不一致)

日誌緩存信息
接口請求成功信息:用戶系統信息,請求項目名、接口名,請求時間

3.代碼講解

用戶登錄成功、生成key參數

//模擬用戶登錄成功
public String getMd5Key() {

        return "de456878b58568e29773e6a53b39d6ef";
    }

獲取客戶端信息

/**
 * 獲取客戶端的信息
 * @author 吳曉暢
 *
 */
public final class SystemUtils {
    /**
     * 獲取訪問者IP
     * 在一般情況下使用Request.getRemoteAddr()即可,但是經過nginx等反向代理軟件後,這個方法會失效。
     *
     * 本方法先從Header中獲取X-Real-IP,如果不存在再從X-Forwarded-For獲得第一個IP(用,分割),
     * 如果還不存在則調用Request .getRemoteAddr()。
     * @param request
     * @return
     */
    public  String getIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("X-Real-IP");
        if (ip!= null && !"".equals(ip) && !"unknown".equalsIgnoreCase(ip)) {
            return ip;
        }
        ip = request.getHeader("X-Forwarded-For");
        if (ip!= null && !"".equals(ip)  && !"unknown".equalsIgnoreCase(ip)) {
            // 多次反向代理後會有多個IP值,第一個爲真實IP。
            int index = ip.indexOf(',');
            if (index != -1) {
                return ip.substring(0, index);
            } else {
                return ip;
            }
        } else {
            return request.getRemoteAddr();
        }
    }

    /**
     * 獲取來訪者的瀏覽器版本
     * @param request
     * @return
     */
    public  String getRequestBrowserInfo(HttpServletRequest request){
        String browserVersion = null;
        String header = request.getHeader("user-agent");
        if(header == null || header.equals("")){
            return "";
        }
        if(header.indexOf("MSIE")>0){
            browserVersion = "IE";
        }else if(header.indexOf("Firefox")>0){
            browserVersion = "Firefox";
        }else if(header.indexOf("Chrome")>0){
            browserVersion = "Chrome";
        }else if(header.indexOf("Safari")>0){
            browserVersion = "Safari";
        }else if(header.indexOf("Camino")>0){
            browserVersion = "Camino";
        }else if(header.indexOf("Konqueror")>0){
            browserVersion = "Konqueror";
        }
        return browserVersion;
    }

    /**
     * 獲取系統版本信息
     * @param request
     * @return
     */
    public  String getRequestSystemInfo(HttpServletRequest request){
        String systenInfo = null;
        String header = request.getHeader("user-agent");
        if(header == null || header.equals("")){
            return "";
        }
        //得到用戶的操作系統
        if (header.indexOf("NT 6.0") > 0){
            systenInfo = "Windows Vista/Server 2008";
        } else if (header.indexOf("NT 5.2") > 0){
            systenInfo = "Windows Server 2003";
        } else if (header.indexOf("NT 5.1") > 0){
            systenInfo = "Windows XP";
        } else if (header.indexOf("NT 6.0") > 0){
            systenInfo = "Windows Vista";
        } else if (header.indexOf("NT 6.1") > 0){
            systenInfo = "Windows 7";
        } else if (header.indexOf("NT 6.2") > 0){
            systenInfo = "Windows Slate";
        } else if (header.indexOf("NT 6.3") > 0){
            systenInfo = "Windows 9";
        } else if (header.indexOf("NT 5") > 0){
            systenInfo = "Windows 2000";
        } else if (header.indexOf("NT 4") > 0){
            systenInfo = "Windows NT4";
        } else if (header.indexOf("Me") > 0){
            systenInfo = "Windows Me";
        } else if (header.indexOf("98") > 0){
            systenInfo = "Windows 98";
        } else if (header.indexOf("95") > 0){
            systenInfo = "Windows 95";
        } else if (header.indexOf("Mac") > 0){
            systenInfo = "Mac";
        } else if (header.indexOf("Unix") > 0){
            systenInfo = "UNIX";
        } else if (header.indexOf("Linux") > 0){
            systenInfo = "Linux";
        } else if (header.indexOf("SunOS") > 0){
            systenInfo = "SunOS";
        }
        return systenInfo;
    }

    /**
     * 獲取來訪者的主機名稱
     * @param ip
     * @return
     */
    public  String getHostName(String ip){
        InetAddress inet;
        try {
            inet = InetAddress.getByName(ip);
            return inet.getHostName();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * 命令獲取mac地址
     * @param cmd
     * @return
     */
    private  String callCmd(String[] cmd) {
        String result = "";
        String line = "";
        try {
            Process proc = Runtime.getRuntime().exec(cmd);
            InputStreamReader is = new InputStreamReader(proc.getInputStream());
            BufferedReader br = new BufferedReader (is);
            while ((line = br.readLine ()) != null) {
                result += line;
            }
        }catch(Exception e) {
            e.printStackTrace();
        }
        return result;
    }
    /**
     *
     *
     *
     * @param cmd
     *            第一個命令
     *
     * @param another
     *            第二個命令
     *
     * @return 第二個命令的執行結果
     *
     */

    private  String callCmd(String[] cmd,String[] another) {
        String result = "";
        String line = "";
        try {
            Runtime rt = Runtime.getRuntime();
            Process proc = rt.exec(cmd);
            proc.waitFor(); // 已經執行完第一個命令,準備執行第二個命令
            proc = rt.exec(another);
            InputStreamReader is = new InputStreamReader(proc.getInputStream());
            BufferedReader br = new BufferedReader (is);
            while ((line = br.readLine ()) != null) {
                result += line;
            }
        }catch(Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     *
     *
     *
     * @param ip
     *            目標ip,一般在局域網內
     *
     * @param sourceString
     *            命令處理的結果字符串
     *
     * @param macSeparator
     *            mac分隔符號
     *
     * @return mac地址,用上面的分隔符號表示
     *
     */

    private  String filterMacAddress(final String ip, final String sourceString,final String macSeparator) {
        String result = "";
        String regExp = "((([0-9,A-F,a-f]{1,2}" + macSeparator + "){1,5})[0-9,A-F,a-f]{1,2})";
        Pattern pattern = Pattern.compile(regExp);
        Matcher matcher = pattern.matcher(sourceString);
        while(matcher.find()){
            result = matcher.group(1);
            if(sourceString.indexOf(ip) <= sourceString.lastIndexOf(matcher.group(1))) {
                break; // 如果有多個IP,只匹配本IP對應的Mac.
            }
        }
        return result;
    }

    /**
     * @param ip
     *            目標ip
     * @return Mac Address
     *
     */

    private  String getMacInWindows(final String ip){
        String result = "";
        String[] cmd = {"cmd","/c","ping " + ip};
        String[] another = {"cmd","/c","arp -a"};
        String cmdResult = callCmd(cmd,another);
        result = filterMacAddress(ip,cmdResult,"-");
        return result;
    }
    /**
     *
     * @param ip
     *            目標ip
     * @return Mac Address
     *
     */
    private  String getMacInLinux(
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章