再破博客園登陸

同步發表於http://avenwu.net/2015/04/12/cnblogs_login_fix/javascript

4月以來博客園悄然修改了登錄的接口,致使我等屁民開發的客戶端生生登錄不了。趁着週末從新對登錄進行了抓包分析,總算搞定,能夠歇一口氣:)html

分析

截止目前登錄頁面地址是這樣的http://passport.cnblogs.com/user/signin?ReturnUrl=http%3A%2F%2Fwww.cnblogs.com%2Fjava

眼尖的園友應該發現了第一個變化,即登錄地址成了use/signin。固然確定不止這一出修改。android

以前登錄採用的是表單提交,如今登錄請求採用了ajax,利用post方式將加密後的用戶名,密碼拼接成json串發給服務器。git

處理

我是怎麼知道的,固然不是猜的,web

詳細的js代碼能夠直接看到:ajax

var encrypt = new JSEncrypt();
            encrypt.setPublicKey('MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCp0wHYbg/NOPO3nzMD3dndwS0MccuMeXCHgVlGOoYyFwLdS24Im2e7YyhB0wrUsyYf0/nhzCzBK8ZC9eCWqd0aHbdgOQT6CuFQBMjbyGYvlVYU2ZP7kG9Ft6YV6oc9ambuO7nPZh+bvXH0zDKfi02prknrScAKC0XhadTHT3Al0QIDAQAB');
            var encrypted_input1 = encrypt.encrypt($('#input1').val());
            var encrypted_input2 = encrypt.encrypt($('#input2').val());
            var ajax_data = {
                input1: encrypted_input1,
                input2: encrypted_input2,
                remember: $('#remember_me').prop('checked')
            };

            if(enable_captcha){
                var captchaObj = $("#captcha_code_input").get(0).Captcha;
                ajax_data.captchaId = captchaObj.Id;
                ajax_data.captchaInstanceId = captchaObj.InstanceId;
                ajax_data.captchaUserInput = $("#captcha_code_input").val();
            }
            is_in_progress = true;
            $.ajax({
                url: ajax_url,
                type: 'post',
                data: JSON.stringify(ajax_data),
                contentType: 'application/json; charset=utf-8',
                dataType: 'json',
                headers: {
                    'VerificationToken': 'cZ0PISksjsWGEbj4IhANzxSXoXmLr9zNWVBzTNuy5khrwm0akh5Eo9XTrmoHt_RzFOKbWD2jOaibj7r_bZZlPLAx81c1:G8OVJmEYy2z1FJJUwvvy_mS3HLR-AYitaaf3eCXHUI8LIwAPjxnpkXqgR32zqSRli_gid77jDtaUlOjGgob8TjdIOq41'
                },
                success: function (data) {                    
                    if (data.success) {
                        $('#tip_btn').html('登陸成功,正在重定向...');
                        location.href = return_url;
                    } else {
                        $('#tip_btn').html(data.message + "<br/><br/>聯繫 contact@cnblogs.com");
                        is_in_progress = false;
                        if(enable_captcha)
                        {
                            captchaObj.ReloadImage();
                        }
                    }
                },
                error: function (xhr) {
                    is_in_progress = false;
                    $('#tip_btn').html('抱歉!出錯!聯繫 contact@cnblogs.com');
                }
            });

用戶名,密碼加密處理

雖然不是很懂js,可是這並不妨礙分析,這裏利用了裏面的加密函數,只知道大體用的是RSA加密,公鑰的話js中已經給出了,直接拿來用就能夠。原本想用java實現,加密函數內容太多,實現不容易,因此仍然用的這段原生的js加密、解密函數;json

如今java中js庫須要解決,查了下相關資料Moliza出了一個引擎Rhino,文檔到是不少,執行一個簡單的js語句應該沒問題,但這裏須要導入整個js庫文件,沒找的簡單可行的方法,只能迂迴前進,直接用weview來加密,寫一段js代碼,調用上面提到的js加密庫,效果也不錯,能夠正常解析:服務器

private final static String ENCRYPT = "javascript:encryptLoginInfo('%s','%s')";

    public void login() {
        WebView webview = new WebView(mContext);
        webview.getSettings().setJavaScriptEnabled(true);
        webview.addJavascriptInterface(new Android(), "android");
        webview.loadUrl(PAGE);
        webview.setWebViewClient(new WebViewClient() {
            @Override
            public void onPageFinished(WebView view, String url) {
                Logger.d("HTML URL=" + url);
                if (PAGE.equals(url) && !isLogining) {
                    view.loadUrl(String.format(ENCRYPT, mListener.setName(), mListener.setPassword()));
                }
            }
        });
    }

抓取頭部數據

除了用戶數據加密外,這裏還須要添加一個頭部VerificationToken,僞造是不可能了,只能訪問登錄頁,利用正則匹配出來。app

Document doc = Jsoup.parse(html);
            String url = doc.select("#c_login_logincaptcha_CaptchaImage").attr("src");
            if (!TextUtils.isEmpty(url)) {
                Logger.d("skip auto login as captcha is needed");
                return "";
            }
            Element script = doc.select("script").get(2);
            Pattern p = Pattern.compile("(?is)'VerificationToken': '(.+?)'"); // Regex for the value of the key
            Matcher m = p.matcher(script.html()); // you have to use html here and NOT text! Text will drop the 'key' part
            String VerificationToken;
            if (m.find()) {
                VerificationToken = m.group(1);
            } else {
                return "";
            }

驗證碼抓取

同理夜間的驗證碼登錄也須要調整,由於這個也變了,但原理是同樣的,分析html內的標籤找到驗證碼所在的img標籤,獲取src即圖片生成地址。

Document doc = Jsoup.parse(html);
            String url = doc.select("#LoginCaptcha_CaptchaImage").attr("src");
            if (TextUtils.isDigitsOnly(url)) {
                return params;
            }
            //BotDetect.Init('LoginCaptcha', '6c5b12a021d8460fa8bc87cdf96f0d80', 'captcha_code_input', true, true, true, true, 1200, 7200, 0, true);
            Matcher matcher = Pattern.compile("(?is)BotDetect.Init\\('LoginCaptcha', '(.+?)',").matcher(html);
            if (matcher.find()) {
                params.captchaInstanceId = matcher.group(1);
            }

結語

基本上每一個細節點都變了,因此須要正對登錄環節從新分析,分析出每個所需的元素後想辦法模擬出來。

相關文章
相關標籤/搜索