【Java】模擬登錄教務網並獲取數據


本文章僅做技術交流演示學習,請勿用於違法操作!

前期准備

首先我們需要到要模擬登錄的網頁,進行抓包操作。

使用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成功

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM