1. 程式人生 > >自建加密解密傳輸&kaptcha驗證碼使用次數限制思路

自建加密解密傳輸&kaptcha驗證碼使用次數限制思路

序:

      剛來第一天環境剛搞好,svn剛裝完,賬號都還沒有,員工手冊還沒有學習完,就被要求開始擼程式碼。還好同事之間溝通狠順暢,直接可以找到人問東西。

     今天主要比較緊急的待修復項:

     1.驗證碼只能使用一次的問題

     2.登入/修改密碼過程中資料傳輸簡單的decode不滿足安全性要求

     首先說下這邊的驗證碼是怎麼生成的。一般我們已說到驗證碼只能用一次想到的肯定是利用redis儲存生成的驗證碼,設定時效性。但是這裡生成驗證碼用的是一個元件kaptcha,這個元件的核心思路就是生成了驗證碼放session裡,沒有設定有效時長這個說法。此外非同步校驗也是每次從session取比對,程式碼如下:

   String  captchaCode = (String) request.getSession().getAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);

        if(captchaCode.equalsIgnoreCase(verificationCode)){

            return "true";

        }

     如果比對一致就表示校驗通過。這裡許可權是使用shiro,登陸也校驗驗證碼,這時候是從token裡取比對。重點來了,我處理這裡保證驗證碼只能使用一次也是在這裡做的邏輯,也就是在shiro的繼承AuthorizingRealm裡的doGetAuthenticationInfo方法裡做文章,思路:就是在校驗token裡的驗證碼比對時從session刪除驗證碼,這樣再次傳這個驗證碼就過不了了,具體邏輯:

String captcha = (String) SecurityUtils

                .getSubject()

                .getSession()

                .getAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);

//刪除session裡的驗證碼        SecurityUtils.getSubject().getSession().removeAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);

        if(StringUtils.isBlank(captcha)) {

            throw new IncorrectCaptchaException("驗證碼失效");

        }

//比對token裡與session裡的驗證碼

        if (captcha != null && !captcha.equalsIgnoreCase(token.getCaptcha()) ){

            throw new IncorrectCaptchaException("驗證碼錯誤");

        }

另外做以上操作,注意需要配合頁面的重新整理做,比如頁面提示驗證失效或錯誤了需要重新獲取ajax生成(保證新生成一個放session裡備用)

     接下來該說第二個問題,那就是加密傳輸的問題。

     其實這裡的思路受起來也狠簡單,就是傳統的加密配合自己寫的加密使用。自己寫的加密的思路:

     每個輸入的字元生成一個的字串,而這個字串裡某位是角標,真正的charAt(角標)位置才是輸入內容,其他位則是隨機生成,這個角標也是隨機的,所以生成的串每次看起來不一樣,這樣抓到的包看到的就是每次傳送的都不一樣,讓黑客難以分析出規律。前臺自建加密js:

function randomsort(d, c) {

    return Math.random() > 0.5 ? -1 : 1

}

function encryption(e) {

    var g = e.split("");

    for (var f = 0; f < g.length; f++) {

        var h = Math.floor(Math.random() * 3) + 1;

        var c = (f + 1) + "0" + (h + 1);

        for (var d = 0; d < h; d++) {

            c += Math.floor(Math.random() * 9)

        }

        g[f] = c + g[f];

        var b = 9 - g[f].length;

        if (b > 2) {

            for (d = 0; d < b; d++) {

                g[f] += Math.floor(Math.random() * 9)

            }

        }

    }

    g.sort(randomsort);

    var a = "";

    for (f = 0; f < g.length; f++) {

        a += g[f] + "$"

    }

    return a

};

頁面使用也超級簡單:

var username = encode(encryption($("#username").val()));   

$("#sendUsername").val(username);

var password = encode(encryption($("#password").val()));

$("#sendPassword").val(password);

其實就是獲取使用者輸入的明文進行自建加密,然後再呼叫一次其他常規加密,這裡的encode就是其他的常規加密。

這樣因為自建加密生成的串隨機,那麼常規加密生成的就每次不一樣,儘管真正的每次使用者名稱密碼都一樣。

但是說實話,別人要是花店心思先獲取這個自建加密js(瀏覽器開啟debug就可以看到,不過這個js要想讓使用者看到但是不知道什麼意思除了變數定義用abcd外還可以加混淆,這樣真的絕大部分黑客就望而止步了),說遠了。

再來說說後臺的解密,其實根據這個js已經可以知道java的解密怎麼寫了,這裡貼出的我故意留個bug吧,可以用,但是會有問題,不能告訴大家見諒,如果真需要知道,可以單獨找我。

public static String decryp(String str){

        String[] strs = str.split("[$]");

        Map<Integer, String> map = new TreeMap<Integer, String>(

                new Comparator<Integer>() {

                    public int compare(Integer obj1, Integer obj2) {

                        return obj2.compareTo(obj1);

                    }

         });

        for(String temp : strs){

            Integer number = Integer.parseInt(temp.substring(0, temp.indexOf("0")));

            temp = temp.substring(temp.indexOf("0") + 1, temp.length());

            Integer index = Integer.parseInt(temp.substring(0, 1));

            String[] temps = temp.split("");

            String value = temps[index];

            map.put(number, value);

        }

        Set<Integer> keySet = map.keySet();

        Iterator<Integer> iter = keySet.iterator();

        String result = "";

        while (iter.hasNext()) {

            Integer key = iter.next();

            result = map.get(key) + result;

        }

        return result;

    }

這是自建加密的後臺解密,常規加密的解密就不說了。

到此私密資料的安全傳輸就提高一個檔次了,加個混淆安全性就更上一層樓了。