Android開發登錄博客園的正確方式

      首先吐點苦水,做者的博客園—Android客戶端已經中止維護半年多了,明知登錄有問題但卻沒動力去改進,想使用MD規範+RxJava重構卻苦於接口問題,實在太不規範,並且還不穩定...javascript

      進入正題吧,第一種方法,JS注入登錄。html

      首先咱們看看下圖博客園Web接口的登錄界面:簡簡單單,兩個輸入框,一個按鈕java

       

      檢查一下它的屬性:這裏只看看密碼這個屬性,id="input2",用戶名id="input1",登錄的id="signin"web

       

       接下來就好辦了,寫一個這樣的佈局,很是簡單:固然真正的開發界面不能這樣醜,並且webview應該隱藏起來。ajax

       

       

       獲取兩個EditText的輸入文本爲String name,pwd以後,咱們爲webview注入用戶名密碼,並觸發登錄按鈕click:json

 
默認加載這個頁面,也就是登錄頁面。
1 webview.loadUrl("https://passport.cnblogs.com/user/signin?ReturnUrl=http%3A%2F%2Fwww.cnblogs.com%2F");
2 String js = "javascript:document.getElementById('input1').setAttribute('value','" + name + "');" + "document.getElementById('input2').setAttribute('value','" + pwd + "');" + "document.getElementById('signin').click();"; 3 webview.loadUrl(js);
 

 


這樣就能登錄了。固然成功與否能夠給webview添加client,而後在onPageFinished裏面判斷,而後獲取cookie,這個cookie就能用來作各類用戶操做了。

第二種登錄方法:模擬登錄。第一種方法畢竟很繁瑣,沒什麼技術含量,咱們知道這個登錄接口是用到RSA加密的,只不過咱們巧妙地避開了加密,直接拿到cookie而已。一塊兒看看第二種加密方式吧:
說到模擬登錄,首先就離不開抓包。不知爲什麼,去年還能使用fiddler抓到登錄接口,如今卻不行了,可是畢竟抓包工具這麼多,仍是有辦法的。咱們就是使用IE+HttpWatch來看看吧

登錄界面的源碼是這樣的,很快就發現了公鑰publicKey以及它的加密流程了,對用戶名進行公鑰加密,對密碼進行公鑰加密,還有個remenber=true|false,這是ajax登錄的json數據

   再往下看發現了VerificationToken這個頭部,起初我覺得它有什麼用,便寫個正則取出它的值:實際並沒什麼用處
 
1  Pattern pattern = Pattern.compile("'VerificationToken'\\s*:\\s*'([^\\s\\n]+)'");
2  Matcher matcher = pattern.matcher(s);
 

 

 
   咱們繼續抓包:輸入用戶名密碼以後,打開Record記錄,點擊登錄:看到如下的數據:登錄接口+POST json的數據 {input1:"RSA加密的用戶名",input2:"RSA加密的密碼",remenber:true}

 
 切換到Headers看看裏面的數據:


如咱們期待通常,有VerificationToken和Ajax異步請求的頭部:X-Requested-With,還有兩個重要的header,Content-Type和Cookie,AspxAutoDetectCookieSupport=1是固定的,
SERVERID來自登錄界面,可自行獲取,至此,咱們要模擬登錄的流程和數據都搞清楚了。下面是模擬登錄的代碼:使用AsyncHttpClient,不必Retrofit或者OKHttp了。
 1 AsyncHttpClient client = new AsyncHttpClient();
 2 client.get("https://passport.cnblogs.com/user/signin?ReturnUrl=http%3A%2F%2Fwww.cnblogs.com%2F", new TextHttpResponseHandler() {
 3     @Override
 4     public void onFailure(int i, Header[] headers, String s, Throwable throwable) {
 5 
 6     }
 7 
 8     @Override
 9     public void onSuccess(int i, Header[] headers, String s) {
10         String token = "";
11         Pattern pattern = Pattern.compile("'VerificationToken'\\s*:\\s*'([^\\s\\n]+)'");
12         Matcher matcher = pattern.matcher(s);
13         if (matcher.find()) {
14                 token = matcher.group(1);
15         }
16         String tmpcookies = "";
17         for (Header header : headers) {
18             String key = header.getName();
19             String value = header.getValue();
20             if (key.contains("Set-Cookie") && value.contains("SERVERID")) {
21                 tmpcookies += value;
22                 tmpcookies = tmpcookies.replace("Path=/", "");
23             }
24 
25         }
26         if (tmpcookies.length() > 0) {
27             tmpcookies = "AspxAutoDetectCookieSupport=1;" + tmpcookies.substring(0, tmpcookies.length() - 1);
28         }
29         LoginBean bean = new LoginBean();//只有三個屬性的Bean ,String類型的input1,input2,remenber boolean類型
30         bean.setRemember(true);
31         try {
32             bean.setInput1(RSA.encrypt("huanghaibin_dev", RSA.KEY));//公鑰加密
33             bean.setInput2(RSA.encrypt("11111111", RSA.KEY));
34             String json = new Gson().toJson(bean);
35             ByteArrayEntity entity = null;
36             entity = new ByteArrayEntity(json.getBytes("UTF-8"));         38             client.addHeader("Cookie", tmpcookies);
39             client.addHeader("Content-Type", "application/json; charset=utf-8");
40             //client.addHeader("VerificationToken", token);
41             client.addHeader("X-Requested-With", "XMLHttpRequest");
42             client.post(WelcomeActivity.this, "https://passport.cnblogs.com/user/signin", entity, "application/json; charset=UTF-8", new TextHttpResponseHandler() {
43                 @Override
44                 public void onFailure(int i, Header[] headers, String s, Throwable throwable) {
45 
46                 }
47 
48                 @Override
49                 public void onSuccess(int i, Header[] headers, String s) {
50                      //返回{success:true}這種類型的json
51                 }
52             });
53         } catch (Exception e) {
54             e.printStackTrace();
55         }
56     }
57 });

 


RSA加密
 
 1 public static PublicKey getPublicKey(String key) throws Exception {
 2     X509EncodedKeySpec keySpec = new X509EncodedKeySpec(
 3             Base64.decode(key.getBytes(),Base64.DEFAULT));
 4     KeyFactory keyFactory = KeyFactory.getInstance("RSA");
 5     PublicKey publicKey = keyFactory.generatePublic(keySpec);
 6     return publicKey;
 7 }
 8 
 9 public static String encrypt(String source, String publicKey)
10         throws Exception {
11     Key key = getPublicKey(publicKey);
12     /** 獲得Cipher對象來實現對源數據的RSA加密 */
13     Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
14     cipher.init(Cipher.ENCRYPT_MODE, key);
15     byte[] b = source.getBytes();
16     /** 執行加密操做 */
17     byte[] b1 = cipher.doFinal(b);
18     return new String(Base64.encode(b1,Base64.DEFAULT),
19             ConfigureEncryptAndDecrypt.CHAR_ENCODING);
20  }
相關文章
相關標籤/搜索