小白Android实现从学校教务系统获取信息(一)抓包与模拟登录

上课的时候经常忘记教室,打开教务系统看又太麻烦,所以最近想做一个app能够自动推送上课信息、上课教室,查成绩,如果可以的话再做一个一键评教。其他功能还在想。以前没接触过不用api做的,模拟登录整整折腾了一天半,慢慢做吧,毕竟我菜。

先上两张效果图,我登录了之后点击了课表信息源数据显示在UI上。


首先用Charles抓包,抓包之前要记得清除历史数据,否则cookie保存到本地了获取不到。以下是学校教务系统登录界面:


对从打开教务系统开始到登录成功抓包主要数据:


第一次GET请求拿到第一个Cookie的JSESSIONID(位于Set-Cookie字段):


第二次POST请求提交用户名、密码等数据,页面重定向。以下是POST请求需要提交的数据:


其它字段都是固定的,但这里有一个It字段需要获取,于是用Find功能查找这个字段的来源


发现在ResponseBody里。

第三次GET请求拿到重定向页面的Cookie,也就是JSESSIONID字段:


代码实现(草稿)

我先写好了登录界面的布局:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.asa.easyxiian.MainActivity">


    <ImageView
        android:id="@+id/first_page_background"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="centerCrop"
        android:src="@drawable/first_page" />


    <LinearLayout
        android:id="@+id/log_in"
        android:layout_width="150dp"
        android:layout_height="match_parent"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:gravity="center"
        android:orientation="vertical">

        <EditText
            android:id="@+id/user_name"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"

            android:hint="学号" />

        <EditText
            android:id="@+id/password"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="密码" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="LogIn"
            android:text="log in" />

        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <TextView
                android:id="@+id/user_name_display"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
        </ScrollView>

        <TextView
            android:id="@+id/password_display"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />


    </LinearLayout>


</RelativeLayout>

最下面的两个TextView是用来测试用户名密码的输入还有用来测试后来得到的Response的,之后会删掉换到其他Activity

NetworkUtil工具类,用来封装GET和POST请求,用的工具包是HttpClient和Jsoup。

package com.example.asa.easyxiian;

import android.net.Uri;
import android.util.Log;

import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.URI;
import org.apache.commons.httpclient.URIException;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.jsoup.Jsoup;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;

/**
 * Created by asa on 2018/4/21.
 */

public class NetworkUtils {


    static HttpClient mClient;

    final static String QUERY_PARAM = "ticket";

    private static final String BASE_URL = "http://jwxt.xidian.edu.cn/caslogin.jsp";

    public static GetMethod getAction(HttpClient client, String url) throws IOException {

        mClient = client;
        GetMethod getMethod = new GetMethod(url);

        mClient.executeMethod(getMethod);

        return getMethod;
    }

    public static GetMethod getMethodUseCookie(HttpClient client, String JSESSION, String url) throws IOException {

        GetMethod getMethod = new GetMethod(url);
        getMethod.setFollowRedirects(false);
        getMethod.addRequestHeader(new Header("Cookie", "JSESSION=" + JSESSION));
        client.executeMethod(getMethod);
        return getMethod;
    }

    public static String[] getData(GetMethod getMethod) throws IOException {

        String[] results = new String[2];
        String JSESSION;
        org.jsoup.nodes.Document document;
        String lt = "";

        InputStream inputStream = getMethod.getResponseBodyAsStream();
        StringBuilder output = new StringBuilder();
        InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "gbk");
        BufferedReader reader = new BufferedReader(inputStreamReader);
        String line = reader.readLine();
        while (line != null) {
            output.append(line);
            line = reader.readLine();
        }

        document = Jsoup.parseBodyFragment(output.toString());
        org.jsoup.nodes.Element body = document.body();
        lt = body.select("[name=lt]").attr("value");

        getMethod.releaseConnection();


        JSESSION = getMethod.getResponseHeader("Set-Cookie").getValue().trim().split(";")[0].split(",")[1].trim().split("=")[1];
        results[0] = JSESSION;
        results[1] = lt;
        return results;
    }

    public static PostMethod postAction(HttpClient client, String url, String userName, String password, String JSESSION, String lt) throws IOException, URIException {
        mClient = client;
        PostMethod postMethod = new PostMethod();
        URI uri = new URI(url);
        postMethod.setURI(uri);
        postMethod.addRequestHeader(new Header("Cookie", "JSESSION=" + JSESSION));
        postMethod.addParameter(new NameValuePair("_eventId", "submit"));
        postMethod.addParameter(new NameValuePair("username", userName));
        postMethod.addParameter(new NameValuePair("password", password));
        postMethod.addParameter(new NameValuePair("lt", lt));
        postMethod.addParameter(new NameValuePair("execution", "e1s1"));
        postMethod.addParameter(new NameValuePair("rmShown", "1"));
        postMethod.addParameter(new NameValuePair("submit", ""));
        mClient.executeMethod(postMethod);
        return postMethod;
    }

    public static String buildUrl(String locationQuery) {

        Uri builtUri = Uri.parse(BASE_URL).buildUpon()
                .appendQueryParameter(QUERY_PARAM, locationQuery)
                .build();

        URL url = null;
        try {
            url = new URL(builtUri.toString());
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }

        Log.v("NetworkUtils : ", "Built URI " + url);

        return url.toString();
    }


}

下面是主程序代码,用了AsynTask,我知道用它实现不太好,以后再改,模拟登录的时候一定要关闭重定向,否则拿不到第二个JSESSIONID。登录过程中的状态码一次200,两次302,否则登录失败。登录成功后拿到源数据后显示在主页面上。

package com.example.asa.easyxiian;

import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;

import java.io.IOException;


import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;

import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;


public class MainActivity extends AppCompatActivity {


    final static String loginUrl1 = "http://ids.xidian.edu.cn/authserver/login?service=http%3A%2F%2Fjwxt.xidian.edu.cn%2Fcaslogin.jsp";

    final static String classInforURL = "http://jwxt.xidian.edu.cn/xkAction.do?actionType=6";

    private EditText mEditTextUserName;
    private EditText mEditTextPassWord;
    private String mUserName;
    private String mPassWord;
    HttpClient mClient;
    TextView mTextViewUserName;
    TextView mTextViewPassWord;
    private GetMethod mGetMethod;
    String mJSESSION;

    String mClassInfo;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mTextViewUserName = (TextView) findViewById(R.id.user_name_display);
        mTextViewPassWord = (TextView) findViewById(R.id.password_display);

        ImageView background = (ImageView) findViewById(R.id.first_page_background);
        background.setAlpha(80);


    }

    public void LogIn(View view) {

        mEditTextUserName = (EditText) findViewById(R.id.user_name);
        mUserName = mEditTextUserName.getText().toString();

        mEditTextPassWord = (EditText) findViewById(R.id.password);
        mPassWord = mEditTextPassWord.getText().toString();

        new LogInTask().execute();


        new getClassesInformationTask().execute(classInforURL);


    }

    private class getClassesInformationTask extends AsyncTask<String, Void, String> {

        @Override
        protected String doInBackground(String... strings) {
            String result = null;
            GetMethod getMethod = null;

            try {
                getMethod = NetworkUtils.getMethodUseCookie(mClient, mJSESSION, strings[0]);
                result = getMethod.getResponseBodyAsString();
                mClassInfo = result;
            } catch (IOException e) {
                Log.e("MainActivity", "使用Cookie的GET失败。");
            }

            return result;
        }

        @Override
        protected void onPostExecute(String s) {
            mTextViewUserName.setText(s);
        }
    }

    private class LogInTask extends AsyncTask<String, Void, String> {

        @Override
        protected String doInBackground(String... strings) {
            mClient = new HttpClient();
            String data[] = new String[2];
            String result = null;

            //GET请求拿到JSESSIONID
            try {
                GetMethod getMethod = NetworkUtils.getAction(mClient, loginUrl1);
                data = NetworkUtils.getData(getMethod);
                getMethod.releaseConnection();
            } catch (IOException e) {
                Log.e("MainActivity : ", "第一步GET请求失败");
            }

            //用拿到的JSESSIONID填充POST网址
            String postUrl = "http://ids.xidian.edu.cn/authserver/login;jsessionid="
                    + data[0] + "?service=http%3A%2F%2Fjwxt.xidian.edu.cn%2Fcaslogin.jsp";

            String urlResponse = null;
            String JSESSIONID2 = null;
            //POST请求发送登录数据,拿到第二步网址urlResponse
            try {
                PostMethod postMethod = NetworkUtils.postAction(mClient, postUrl, mUserName, mPassWord, data[0], data[1]);
                urlResponse = postMethod.getResponseHeader("Location").toString().split(" ")[1];

            } catch (IOException e) {
                Log.e("MainActivity : ", "第二步POST请求失败");
            }


            String logInPage = null;
            //第二次GET请求进入登录界面,拿到第二个JSESSIONID
            try {
                GetMethod getMethod = new GetMethod(urlResponse);
                getMethod.setFollowRedirects(false);
                int statusCode = mClient.executeMethod(getMethod);
                JSESSIONID2 = getMethod.getResponseHeader("Set-Cookie").getValue().split(";")[0].trim().split("=")[1];
                Log.e("Set-Cookie", JSESSIONID2 + "");

                //检查是否登录成功
                Log.e("Status code", "" + statusCode);

                logInPage = getMethod.getResponseHeader("Location").toString().split(" ")[1];
                Log.e("Response page", "" + logInPage);

            } catch (IOException e) {
                Log.e("MainActivity : ", "第二步GET请求失败");
            }

            //进入登录后页面
            GetMethod getMethod = new GetMethod(logInPage);
            getMethod.setFollowRedirects(false);
            getMethod.addRequestHeader(new Header("Cookie", "JSESSION=" + JSESSIONID2));
            try {
                mClient.executeMethod(getMethod);
                result = getMethod.getResponseBodyAsString();
                mGetMethod = getMethod;
                mJSESSION = JSESSIONID2;
            } catch (IOException e) {
                Log.e("MainActivity : ", "进入登录后页面失败");
            }


            return result;
        }

        @Override
        protected void onPostExecute(String s) {
            mTextViewUserName.setText(s);
        }
    }


}

猜你喜欢

转载自blog.csdn.net/fuyuqing155/article/details/80038420