程序员@IT·互联网

Android模拟登录教务系统

2018-02-20  本文已影响0人  JACoder

一、原理

  • 在超级课程表等APP中,大都有使用学号密码登录教务系统读取课程的功能。在这里我将模仿此功能使用J2V8、Jsoup等第三方库实现模拟登录教务系统读取信息。
  • 这里的教务系统以南昌大学教务系统(湖南强智科技教务系统)为例。

现在的大多数网站架构都是后台使用Session来进行用户验证,并向前端返回Cookie,前端的每次请求都带上这个Cookie以作为用户登陆态的保持与身份的认证。
另有部分网站(尤其是APP)使用JWT(JSON Web Tokens)进行登陆态验证,确认登录后前端请求带上返回的Token,也是大同小异。这里主要以前者为例。


我们先来梳理一下登录的流程

1. 前端向后台Servlet发送请求,返回验证码图片。请求头中携带Cookie。
向后台请求验证码图片
2.登录时,请求头中携带相应Cookie,并将账号密码编码后连同验证码一同post到后台
登录
3.若后台验证账号密码与验证码正确,则该Cookie已经作为本用户登录态的凭证,每次请求携带该Cookie即可访问相应权限的Url。

二、具体实现

  • 在Android代码中,我将所有Activity均继承于BaseActivity,BaseActivity中实现沉浸式标题栏的相应代码,将配置文件中的主题修改为@style/Theme.AppCompat.Light.NoActionBar以完成沉浸式标题栏。
/**
 * 封装可复用的代码,作为所有Activity的父类
 */
public abstract class BaseActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //region 沉浸式状态栏代码
        // 5.0以上系统状态栏透明
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Window window = getWindow();
            window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            window.setStatusBarColor(Color.TRANSPARENT);
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        }
        //endregion
        setContentView(getViewId());
    }

    /**
     *添加抽象方法,供子类绑定视图
     * @return
     */
    abstract int getViewId();
}
  • 继续进行功能分析,首先,我们要向相应的Servlet发送请求,将验证码显示在App上。
  • 在这里我们使用的是jsoup: Java HTML Parser进行网络链接以及Cookie获取。

我们可以直接在jsoup官网下载对应jar包。为方便起见,我们在Maven Repository中找到jsoup的gradle依赖,并将其添加到build.gradledependencies中。

compile group: 'org.jsoup', name: 'jsoup', version: '1.11.2'

然后我们使用jsoup进行验证码的请求,并将Cookie信息保存下来。
(注:jsoup对网络的请求写在子线程中,通过handle对前端进行操作)

private String url_safecode = "http://jwc101.ncu.edu.cn/jsxsd/verifycode.servlet"; // 验证码
private Map<String, String> cookie;
class PicThread extends Thread{
        @Override
        public void run() {
            Connection.Response response = null;
            try {
                response = Jsoup.connect(url_safecode)
                        .ignoreContentType(true) // 获取图片需设置忽略内容类型
                        .userAgent("Mozilla")
                        .method(Connection.Method.GET).timeout(3000).execute();
                cookie = response.cookies();  //将cookie保存下来
                byte[] bytes = response.bodyAsBytes();
                //验证码保存为Bitmap
                Bitmap bitmap= BitmapFactory.decodeByteArray(bytes,0,bytes.length); 
                Message message=new Message();
                message.what=0x123;
                message.obj=bitmap;
                handler.sendMessage(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

Handle接受到Message后将验证码显示在界面的ImageView上。

handler=new Handler(){
            @Override
            public void handleMessage(Message msg) {
                if(msg.what==0x123){
                    imageView.setImageBitmap((Bitmap)msg.obj);
                }
            }
        };

OK,我们现在将验证码显示了出来并保存了Cookie,接下来就是将账号与密码Post到后台。这里要注意一个问题,我们通过之前的截图可以看到,登录时Post的账号与密码都是重新编码过的。
我们通过右键查看网页源代码可以看到,该编码过程账号和密码分别是调用conwork.js中的encodeInp方法,然后将编码后的密文用%%%进行连接。

编码
  • 这里有两种解决方案,一种解决方案是将加密算法使用Java重写一遍,这种方法比较麻烦,我在这里采用的是第二种方法,直接在Android中使用J2V8框架运行JS代码进行加密。

J2V8框架的添加方式与jsoup大同小异,在maven repository中找到J2V8的gradle依赖,然后添加到build.gradle中。

compile 'com.eclipsesource.j2v8:j2v8:4.5.0@aar'

将conwork.js放到项目目录的assets中,然后使用J2V8调用。

try {
        InputStream is=getAssets().open("conwork.js");   //获取用户名与密码加密的js代码
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        StringBuilder sb = new StringBuilder();
        String line = null;
        try {
                while ((line = reader.readLine()) != null) {
                        sb.append(line);
                }
                V8 runtime = V8.createV8Runtime();      //使用J2V8运行js代码并将编码结果返回
                final String encodename = runtime.executeStringScript(sb.toString()
                                + "encodeInp('"+userin.getText().toString()+"');\n");
                final String encodepwd=runtime.executeStringScript(sb.toString()+"encodeInp('"+pwdin.getText().toString()+"');\n");
                runtime.release();
        } catch (IOException e) {
                e.printStackTrace();
        } finally {
                try {
                        is.close();
                } catch (IOException e) {
                        e.printStackTrace();
                }
        }
} catch (IOException e) {
        e.printStackTrace();
}

然后我们将encodenameencodepwd使用jsoup来post到教务系统登录的url,post时带上之前保存的cookie。

(new Thread(){
        @Override
        public void run() {
                Map<String, String> data = new HashMap<String, String>();   //进行Post的参数
                data.put("encoded", encodename+"%%%"+encodepwd);
                data.put("RANDOMCODE", verin.getText().toString());
                Connection connect = Jsoup.connect(url_Login)
                                        .header("Accept",
                                                "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8")
                                        .userAgent("Mozilla").method(Connection.Method.POST).data(data).timeout(3000);
                for (Map.Entry<String, String> entry : cookie.entrySet()) { //使用获取验证码时生成的cookie
                        connect.cookie(entry.getKey(), entry.getValue());
                }
                Connection.Response response = null;
                try {
                        response = connect.execute();
                        Pattern p=Pattern.compile("<title>(.*)</title>");
                        Matcher m =p .matcher(response.body()) ;
                        if( m. find()) {
                                //通过response的title是否为"学生个人中心"来进行判断是否登录成功
                               if((m.group(1).toString()).equals("学生个人中心")){
                                       //登录成功的相应代码
                                }else{

                               }
                        }

                } catch (IOException e) {
                        e.printStackTrace();
                }
        }
}).start();

这时我们就已经完成了模拟登录,之后可以使用这个cookie访问管理系统的各个url了。

三、最终效果

效果
上一篇下一篇

猜你喜欢

热点阅读