学生党-花式登录正方教务系统

991 阅读5分钟

本章主要讲述如何通过花式手段(代理请求)登录自己学校的正方教务系统,如果看到这篇文章的你学校也是正方教务系统,也是可以尝试的。

技术栈:

前端:小程序 后端:node

前提:

1.学校使用的是正方教务系统。

2.本例子只是提供基础结构和大致流程,具体的学校系统间的鉴权逻辑需要自己处理一下,一般是cookie,refer等条件需要着重注意一下。

准备工作

  1. 通过终端ping通学校教务系统,获得IP,这个步骤是为了后面在请求中保证验证码正确做准备。 ping xx.xxxx.cn 注:需要去掉http://或者https://
  2. 适当的对node有了解
  3. 会自行建立项目,安装对应的模块(cheerio,superagent,charset)
  4. 如果2和3都不会,扫下面二维码公众号私我,我教你。

// 大致步骤如下
npm init // 创建项目
npm i cheerio
npm i superagent
npm i charset

分析

我先来总结一下流程:进入教务系统=>找到需要的参数=>找到请求接口=>注意观察信息=>获得结果

登录界面
4个参数[用户名密码验证码身份],身份是默认值,前期可以选择先不传,所以最后要传的就是[用户名密码验证码]

验证码的问题

用户名和密码我们可以通过input标签输入,但是验证码我们应该如何拿到呢,F12打开控制台,从标签中找到验证码,验证码的url是.aspx结尾。 此时就不能按常规图片链接方式处理了,我们可以这样理解,首先这个验证码肯定是一个动态地址。那么我们如何获得这个动态地址,我们有教务系统的链接,xxx.xxx.xxx/(xxxxxxx)/default.aspx,做一种假设,我把验证码上的xx.aspx放在链接的后面,是不是就可以了? 尝试一下,发现ok。这样就可以把链接和xx.aspx做一个替换放在img的url上。这样就可以获取到验证码。现在我们先不输入点一下登录,看看会不会有隐藏信息。

入参列表

果然不少,那么接下来通过常规方式进入教务系统,看看都有哪些被填写了。 经过测试发现[__VIEWSTATE,txtUserName,TextBox2,txtSecretCode,RadioButtonList1]这几项是必填的,后四项其实就是表单里的数据,那么这个__VIEWSTATE是哪里来的,通过标签搜索我发现它是一个标签里的value值。

__VIEWSTATE
那么由此我也就确定了所有的值,由此可以开始进行代码编写工作进行登录了。

登录

这一块其实主要就是界面和接口的实现了,由于之前的分析我们已经有了一个大体的思路。 实现思路: onLoad(根据教务系统ip获得url地址) => 输入用户信息和验证码 => 请求学校的登录接口

  1. 获取验证码API
const hostUrl = '学校教务系统IP';
const getCodeUrl = ctx => {
  return new Promise((resolve, reject) => {
    superagent
      .get(hostUrl)
      .charset('gb2312')
      .set(headers)
      .end(function(err, res) {
        console.log('res', res);
        const body = res.text;
        // 获得重定向地址
        const systemUrl = res.redirects[0];
        // 获得codeUrl后缀
        $ = cheerio.load(body);
        const viewState = $('#form1 > input')[0].attribs.value;
        // 替换返回验证码地址
        const codeUrl = systemUrl.replace(/default2.aspx/, 'CheckCode.aspx');
        resolve({ codeUrl, viewState, systemUrl });
      });
  });
};

这块的逻辑就是根据superagent(请求代理模块)去请求【教务系统】的页面数据,通过返回的ip进行验证码的url地址拼接(保证url的正确性),最后把一些必要的信息返回给前端【小程序】。

  1. 小程序请求API
getCodeUrl: function() {
    let that = this;
    wx.request({
      url: 'http://127.0.0.1:3000/login/getCodeUrl',
      header: {
        'content-type': 'application/json'
      },
      success(res) {
        const data = res.data;
        that.setData({
          codeUrl: data.codeUrl,
          systemUrl: data.systemUrl,
        })
      }
    })
  },

小程序去请求getCodeUrl获得【验证码url】和【教务系统url】。

  1. 小程序页面
<!-- login.wxml -->
<view class="container" bindsubmit="formSubmit">
  <view class="loginTitle">
    <image src="{{loginIcon}}"></image>
    <text>登录</text>
  </view>
  <form class="loginFrom" bindsubmit="formSubmit">
    <view class="formItem">
      <text class="title">学号</text>
      <input name="txtUserName" placeholder="请输入学号" maxlength='10' />
    </view>
    <view class="formItem">
      <view class="title">密码</view>
      <input name="TextBox2" placeholder="请输入密码" type="password" />
    </view>
    <view class="formItem">
      <view class="title">验证码</view>
        <input name="txtSecretCode" placeholder="请输入验证码" class="codeInput" maxlength='4'/>
        <image src="{{codeUrl}}" class="codeImg"></image>
    </view>
    <button form-type="submit">提交</button>
  </form>
</view>

这块其实就是普通的页面展示(大概样子),主要是为了让你能看到在第二步【setData】时的数据是干什么用的。完成这三步就完成了【登录】需要的百分之50功能。

  1. 登录
  // 表单提交
  formSubmit(e){
    const data = e.detail.value;
    // 登录主页
    const payload = {
      RadioButtonList1:"%D1%A7%C9%FA",
      ...data
    }
   this.loginSystem(payload);
  },
  // 得到系统主页链接
  getSystemUrl(payload) {
    let that = this;
    const data = {
        __VIEWSTATE: "学校与学校的不一样",
        Textbox1: "",
        Button1: "",
        lbLanguage: "",
        hidPdrs: "",
        hidsc: "",
        systemUrl: that.data.systemUrl,
        ...payload
     };
    wx.request({
      url: 'http://127.0.0.1:3000/login/loginSystem',
      method: 'post',
      header: {'content-type': 'application/x-www-form-urlencoded'},
      data,
      success(res) {
        const { data:{ errCode = "", url = "", name = "" } = {} } = res;
        // 缓存系统地址
        wx.setStorageSync('systemUrl',url);
        wx.setStorageSync('stuName', name);
        wx.setStorageSync('stuCode', payload.txtUserName);
      },
      complete() {}
    })
  },

为了保证看起来比较简单,我删去了【加载】【防多点】【异常兼容】等代码。这块主要通过form表单获得输入的数据,经过添加处理后提交给node接收参数。

  1. 登录API
// 获取系统主页面
const getMainSystemUrl = payload => {
  // 传参
  const url = payload.systemUrl;
  const __VIEWSTATE = payload.__VIEWSTATE;
  const data = {
    __VIEWSTATE,
    txtUserName: payload.txtUserName,
    Textbox1: '',
    TextBox2: payload.TextBox2,
    txtSecretCode: payload.txtSecretCode,
    RadioButtonList1: '%D1%A7%C9%FA',
    Button1: '',
    lbLanguage: '',
    hidPdrs: '',
    hidsc: ''
  };
  return new Promise((resolve, reject) => {
    superagent
      .post(url)
      .charset('gb2312')
      .set(headers)
      .send(data)
      .end(function(err, res) {
        const mainSystemUrl = res.redirects[0];
        const body = res.text;
        $ = cheerio.load(body);
        const stuName = $('#xhxm')
          .text()
          .replace(/同学/, '');
        resolve({ url: mainSystemUrl, name: stuName });
      });
  });
};

这一块就是node接收到小程序传递的参数,对数据进行处理后,去请求【教务系统】的登录接口,通过接口返回的内容,取出【姓名】【学号】等信息,辅助我们完成接下来的功能。


如果你看到了这里,并且每一块都详细的看明白了,那你用python,java都可以实现这样一套简单的接口请求。但是如果你看完之后,copy到了自己的项目中发现没法使用,你可以扫描下面的公众号二维码,后台提出问题我替你解答。获得完整源码可以微信搜索【前端小白的成长记录】关注或者扫码二维码回复教务系统获得。 【注】如果你们学校的教务系统是正方的,源码只需修改教务系统IP即可使用,目前已有查询成绩和查询课表功能。

前端小白的成长记录

小白学习群