本文章僅做技術交流演示學習,請勿用於違法操作!
前期准備
首先我們需要到要模擬登錄的網頁,進行抓包操作。
使用Chrome瀏覽器打開系統的登錄頁面,按F12打開開發者工具
切換到Network選項卡
然后正常進行登錄操作。
登錄成功后,可以在右面看到加載出很多的數據,我們需要逐一查看Headers找到對應的登錄接口
如圖所示,大部分都是login相關的頁面名稱。
很顯然,這個接口URL即我們想要的請求登錄接口。
RequestURL:http://打碼/jsxsd/xk/LoginToXk
根據登錄界面和FormData 我們知道,登錄提供的是賬戶和密碼,但是瀏覽器在請求的時候是三個參數,分別為:userAccount,userPassword,encoded
顯然userAccount即我們提供的賬號,userPassword是密碼但為空,encoded是一堆看不懂的密文
初步推測是encoded應該是加密過后的密碼
那么加密過程一定是在前端完成的,我們根據經驗加密函數應該也是使用js寫的。
回到登錄界面,開啟開發者模式,刷新一下,果然在列表中有一個名為conwork.js的文件
該文件就是用來對密碼進行加密的文件,我們將其保存下來。
eval(function(p, a, c, k, e, d) { e = function(c) { return (c < a ? "" : e(parseInt(c / a))) + ((c = c % a) > 35 ? String.fromCharCode(c + 29) : c.toString(36)) } ; if (!''.replace(/^/, String)) { while (c--) d[e(c)] = k[c] || e(c); k = [function(e) { return d[e] } ]; e = function() { return '\\w+' } ; c = 1; } ;while (c--) if (k[c]) p = p.replace(new RegExp('\\b' + e(c) + '\\b','g'), k[c]); return p; }('b 9="o+/=";p q(a){b e="";b 8,5,7="";b f,g,c,1="";b i=0;m{8=a.h(i++);5=a.h(i++);7=a.h(i++);f=8>>2;g=((8&3)<<4)|(5>>4);c=((5&s)<<2)|(7>>6);1=7&t;k(j(5)){c=1=l}v k(j(7)){1=l}e=e+9.d(f)+9.d(g)+9.d(c)+9.d(1);8=5=7="";f=g=c=1=""}u(i<a.n);r e}', 32, 32, '|enc4||||chr2||chr3|chr1|keyStr|input|var|enc3|charAt|output|enc1|enc2|charCodeAt||isNaN|if|64|do|length|ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789|function|encodeInp|return|15|63|while|else'.split('|'), 0, {}))
到此,前期准備就完成了。
模擬登錄
本文使用Java來進行編寫,其他語言原理類似。
Java運行JS代碼
首先要解決的就是,如何能在java代碼中運行這個使用js編寫的加密函數。
當然,如果有能力看懂的話,你也可以重寫一遍這個方法,可以跳過本段內容。
我們使用的是Java自帶的解決方案:ScriptEngineManager
首先編寫一個通用方法:
/** * 從給定的js文件中獲取指定接口中的方法的實例 * @param js js字符串 * @param clazz 接口的class * @return 返回一個指定接口方法的實例 */ public <T> T getMethod (Class<T> clazz,String js) { ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("js"); try { //1.可以直接讀取指定目錄下的文件 //String path = ExecuteScript.class.getResource("").getPath(); //System.out.println(path); // FileReader的參數為所要執行的js文件的路徑 //engine.eval(new FileReader(fileLoacation)); //2.直接加載字符串 engine.eval(js); if (engine instanceof Invocable) { Invocable invocable = (Invocable) engine; T executeMethod = invocable.getInterface(clazz); return executeMethod; } } catch (Exception e) { e.printStackTrace(); } return null; }
然后根據Js的方法創建一個接口,接口中的方法名稱要和js中的方法名稱對應。
public interface JSMethods { public String encodeInp(String input); }
使用方法:
/** * 加密 * @param onecard 學號 * @param password 密碼 * @return 加密的學號+%%%+加密的密碼 * */ private String getEncodeString(String onecard,String password){ ExecuteScript executeScript = new ExecuteScript(); //String fileLocation="src/main/resources/templates/conwork.js"; //通過下面一行代碼就可以獲取指定接口中方法的實例 JSMethods method = executeScript.getMethod(JSMethods.class,JscriptMethod.data); String result=method.encodeInp(onecard); String pass=method.encodeInp(password); return result+"%%%"+pass; }
這里的result是根據實際encoded結果分析出來的。
使用Okhttp進行模擬Post請求
首先創建請求體:requestbody
RequestBody requestBody=new FormBody.Builder() .add("userAccount",account) .add("userPassword","") .add("encoded",getEncodeString(account,password)) .build();
必須與我們前面分析出來的請求參數一一對應。
接着我們創建一個request請求:
Request request=new Request.Builder() .url(REQUEST_JW_URL) .header("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8") .header("User-Agent","Mozilla") .post(requestBody) .build();
這里的請求頭,可以參考瀏覽器獲取的結果
然后使用okhttpclient發起post請求即可,根據response判斷是否登錄成功
/** * 模擬登陸 * @param account 學號 * @param password 密碼 * */ private String login(String account,String password){ String result=""; RequestBody requestBody=new FormBody.Builder() .add("userAccount",account) .add("userPassword","") .add("encoded",getEncodeString(account,password)) .build(); Request request=new Request.Builder() .url(REQUEST_JW_URL) .header("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8") .header("User-Agent","Mozilla") .post(requestBody) .build(); try { Response response=client.newCall(request).execute(); result=response.body().string(); } catch (IOException e) { e.printStackTrace(); } return result; }
如果需要登錄后進行訪問其他頁面獲取數據,這里只需要配置一下okhttp的cookies管理工具:
public OkHttpClient client = new OkHttpClient.Builder()//這個cookies就不存在本地了,因為不需要驗證碼直接登錄,每次刷新就好 .cookieJar(new CookieJar() { @Override public void saveFromResponse(HttpUrl httpUrl, List<Cookie> list) { cookieStore.put(httpUrl.host(), list); } @Override public List<Cookie> loadForRequest(HttpUrl httpUrl) { List<Cookie> cookies = cookieStore.get(httpUrl.host()); return cookies != null ? cookies : new ArrayList<Cookie>(); } }) .build();
全局都使用該client進行請求即可,注意無論是同步還是異步操作都要保證login成功