新增学生信息获取和解析;更改部分代码结构

old-package
lensfrex 2 years ago
parent b9e75fcd51
commit f352b34b6f
Signed by: lensfrex
GPG Key ID: 0F69A0A2FBEE98A0
  1. 2
      README.md
  2. 14
      mywust-core/pom.xml
  3. 2
      mywust-core/src/main/java/cn/linghang/mywust/core/api/Bkjx.java
  4. 7
      mywust-core/src/main/java/cn/linghang/mywust/core/api/UnionAuth.java
  5. 4
      mywust-core/src/main/java/cn/linghang/mywust/core/exception/CookieInvalidException.java
  6. 4
      mywust-core/src/main/java/cn/linghang/mywust/core/exception/HtmlPageParseException.java
  7. 7
      mywust-core/src/main/java/cn/linghang/mywust/core/parser/Parser.java
  8. 64
      mywust-core/src/main/java/cn/linghang/mywust/core/parser/undergraduate/StudentInfoPageParser.java
  9. 31
      mywust-core/src/main/java/cn/linghang/mywust/core/parser/undergraduate/xpath/StudentInfoXpath.java
  10. 3
      mywust-core/src/main/java/cn/linghang/mywust/core/request/AuthRequestFactory.java
  11. 9
      mywust-core/src/main/java/cn/linghang/mywust/core/request/BkjxRequestFactory.java
  12. 5
      mywust-core/src/main/java/cn/linghang/mywust/core/request/LibraryRequestFactory.java
  13. 4
      mywust-core/src/main/java/cn/linghang/mywust/core/request/RequestFactory.java
  14. 17
      mywust-core/src/main/java/cn/linghang/mywust/core/service/auth/JwcLogin.java
  15. 17
      mywust-core/src/main/java/cn/linghang/mywust/core/service/auth/LibraryLogin.java
  16. 1
      mywust-core/src/main/java/cn/linghang/mywust/core/service/auth/UnionLogin.java
  17. 4
      mywust-core/src/main/java/cn/linghang/mywust/core/service/library/LibraryService.java
  18. 50
      mywust-core/src/main/java/cn/linghang/mywust/core/service/undergraduate/JwcService.java
  19. 32
      mywust-core/src/main/java/cn/linghang/mywust/core/util/BkjxUtil.java
  20. 8
      mywust-model/pom.xml
  21. 26
      mywust-model/src/main/java/cn/linghang/mywust/model/undergrade/StudentInfo.java
  22. 4
      mywust-network-okhttp/src/main/java/cn/linghang/mywust/network/okhttp/SimpleOkhttpRequester.java
  23. 8
      mywust-network/src/main/java/cn/linghang/mywust/network/HttpResponse.java
  24. 2
      mywust-test/src/test/java/JwcLegacyLoginTest.java
  25. 2
      mywust-test/src/test/java/JwcLoginTest.java
  26. 3
      mywust-test/src/test/java/LibraryLoginTest.java

@ -6,6 +6,8 @@
核心代码来自武科大助手后端爬虫模块,在此基础上进行部分修改以适用于各种平台,因此不会使用重量级的框架,尽量保证仅使用原生java或jvm兼容的语言即可使用,是一个比较~~轻量~~的库 核心代码来自武科大助手后端爬虫模块,在此基础上进行部分修改以适用于各种平台,因此不会使用重量级的框架,尽量保证仅使用原生java或jvm兼容的语言即可使用,是一个比较~~轻量~~的库
由于处在早期阶段,因此项目结构随时可能发生巨大变化,在正式版出来前请勿重度依赖
~~(说白了就是一个爬虫库,只不过泛用性高,在任何jvm平台上都能使用,而不仅限于spring体系)~~ ~~(说白了就是一个爬虫库,只不过泛用性高,在任何jvm平台上都能使用,而不仅限于spring体系)~~
后续可能会根据需要新增其他语言的实现以提供给其他语言和平台的使用 后续可能会根据需要新增其他语言的实现以提供给其他语言和平台的使用

@ -34,6 +34,20 @@
<artifactId>jregex</artifactId> <artifactId>jregex</artifactId>
<version>1.2_01</version> <version>1.2_01</version>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.15.3</version>
</dependency>
<dependency>
<groupId>cn.linghang</groupId>
<artifactId>mywust-model</artifactId>
<version>0.0.1-dev</version>
<scope>compile</scope>
</dependency>
</dependencies> </dependencies>
<properties> <properties>

@ -12,6 +12,8 @@ public class Bkjx {
public static final String BKJX_TEST_API = "http://bkjx.wust.edu.cn/jsxsd/xxwcqk/xxwcqk_idxOnzh.do"; public static final String BKJX_TEST_API = "http://bkjx.wust.edu.cn/jsxsd/xxwcqk/xxwcqk_idxOnzh.do";
public static final String BKJX_STUDENT_INFO_API = "http://bkjx.wust.edu.cn/jsxsd/grxx/xsxx";
public static class Legacy { public static class Legacy {
public static final String BKJX_INDEX = "http://bkjx.wust.edu.cn"; public static final String BKJX_INDEX = "http://bkjx.wust.edu.cn";
public static final String BKJX_DATA_STRING_API = "http://bkjx.wust.edu.cn/Logon.do?method=logon&flag=sess"; public static final String BKJX_DATA_STRING_API = "http://bkjx.wust.edu.cn/Logon.do?method=logon&flag=sess";

@ -6,7 +6,10 @@ public class UnionAuth {
*/ */
public static final String UNION_AUTH_API = "https://auth.wust.edu.cn/lyuapServer/v1/tickets"; public static final String UNION_AUTH_API = "https://auth.wust.edu.cn/lyuapServer/v1/tickets";
public static final String LIBRARY_SSO_SERVICE = "https://libsys.wust.edu.cn:443/meta-local/opac/cas/rosetta"; public static class Service {
public static final String BKJX_SSO_SERVICE = "http://bkjx.wust.edu.cn/jsxsd/sso.jsp"; public static final String LIBRARY_SSO_SERVICE = "https://libsys.wust.edu.cn:443/meta-local/opac/cas/rosetta";
public static final String BKJX_SSO_SERVICE = "http://bkjx.wust.edu.cn/jsxsd/sso.jsp";
}
} }

@ -0,0 +1,4 @@
package cn.linghang.mywust.core.exception;
public class CookieInvalidException extends BasicException {
}

@ -0,0 +1,4 @@
package cn.linghang.mywust.core.exception;
public class HtmlPageParseException extends BasicException {
}

@ -0,0 +1,7 @@
package cn.linghang.mywust.core.parser;
import cn.linghang.mywust.core.exception.HtmlPageParseException;
public interface Parser<T> {
public T parse(String html) throws HtmlPageParseException;
}

@ -0,0 +1,64 @@
package cn.linghang.mywust.core.parser.undergraduate;
import cn.linghang.mywust.core.exception.HtmlPageParseException;
import cn.linghang.mywust.core.parser.Parser;
import cn.linghang.mywust.core.parser.undergraduate.xpath.StudentInfoXpath;
import cn.linghang.mywust.model.undergrade.StudentInfo;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
public class StudentInfoPageParser implements Parser<StudentInfo> {
public StudentInfo parse(String html) throws HtmlPageParseException {
Document page = Jsoup.parse(html);
Element table = page.getElementById("xjkpTable");
if (table == null) {
throw new HtmlPageParseException();
}
Elements studentElements = table.selectXpath(StudentInfoXpath.STUDENT_NUMBER);
String studentNumber = studentElements.isEmpty() ? null : studentElements.get(0).text().replace("学号:", "");
Elements collegeElements = table.selectXpath(StudentInfoXpath.COLLEGE);
String college = collegeElements.isEmpty() ? null : collegeElements.get(0).text().replace("院系:", "");
Elements majorElements = table.selectXpath(StudentInfoXpath.MAJOR);
String major = majorElements.isEmpty() ? null : majorElements.get(0).text().replace("专业:", "");
Elements classElements = table.selectXpath(StudentInfoXpath.CLASS);
String clazz = classElements.isEmpty() ? null : classElements.get(0).text().replace("班级:", "");
Elements nameElements = table.selectXpath(StudentInfoXpath.NAME);
String name = nameElements.isEmpty() ? null : nameElements.get(0).text();
Elements sexElements = table.selectXpath(StudentInfoXpath.SEX);
String sex = sexElements.isEmpty() ? null : sexElements.get(0).text();
Elements birthdayElements = table.selectXpath(StudentInfoXpath.BIRTHDAY);
String birthday = birthdayElements.isEmpty() ? null : birthdayElements.get(0).text();
Elements hometownElements = table.selectXpath(StudentInfoXpath.HOMETOWN);
String hometown = hometownElements.isEmpty() ? null : hometownElements.get(0).text();
Elements nationalityElements = table.selectXpath(StudentInfoXpath.NATIONALITY);
String nationality = nationalityElements.isEmpty() ? null : nationalityElements.get(0).text();
Elements idNumberElements = table.selectXpath(StudentInfoXpath.ID_NUMBER);
String idNumber = idNumberElements.isEmpty() ? null : idNumberElements.get(0).text();
return StudentInfo.builder()
.studentNumber(studentNumber)
.college(college)
.major(major)
.clazz(clazz)
.name(name)
.sex(sex)
.birthday(birthday)
.hometown(hometown)
.nationality(nationality)
.idNumber(idNumber)
.build();
}
}

@ -0,0 +1,31 @@
package cn.linghang.mywust.core.parser.undergraduate.xpath;
/**
* <p>本科生学生信息接口用到的xpath</p>
* <p>看着挺唬人的其实直接浏览器选择元素复制xpath就行了</p>
* <p>这里的xpath只要网站UI不整什么花活就不会出问题</p>
*
* @author lensfrex
* @create 2022-10-22 22:16
*/
public class StudentInfoXpath {
public static final String STUDENT_NUMBER = "//*[@id=\"xjkpTable\"]/tbody/tr[3]/td[5]";
public static final String COLLEGE = "//*[@id=\"xjkpTable\"]/tbody/tr[3]/td[1]";
public static final String MAJOR = "//*[@id=\"xjkpTable\"]/tbody/tr[3]/td[2]";
public static final String CLASS = "//*[@id=\"xjkpTable\"]/tbody/tr[3]/td[4]";
public static final String NAME = "//*[@id=\"xjkpTable\"]/tbody/tr[4]/td[2]";
public static final String SEX = "//*[@id=\"xjkpTable\"]/tbody/tr[4]/td[4]";
public static final String BIRTHDAY = "//*[@id=\"xjkpTable\"]/tbody/tr[5]/td[2]";
public static final String HOMETOWN = "//*[@id=\"xjkpTable\"]/tbody/tr[7]/td[2]";
public static final String NATIONALITY = "//*[@id=\"xjkpTable\"]/tbody/tr[8]/td[4]";
public static final String ID_NUMBER = "//*[@id=\"xjkpTable\"]/tbody/tr[50]/td[4]";
}

@ -1,8 +1,7 @@
package cn.linghang.mywust.core.service.auth; package cn.linghang.mywust.core.request;
import cn.linghang.mywust.core.api.UnionAuth; import cn.linghang.mywust.core.api.UnionAuth;
import cn.linghang.mywust.network.HttpRequest; import cn.linghang.mywust.network.HttpRequest;
import cn.linghang.mywust.network.RequestFactory;
import cn.linghang.mywust.util.StringUtil; import cn.linghang.mywust.util.StringUtil;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;

@ -1,19 +1,22 @@
package cn.linghang.mywust.core.service.undergraduate; package cn.linghang.mywust.core.request;
import cn.linghang.mywust.core.api.Bkjx; import cn.linghang.mywust.core.api.Bkjx;
import cn.linghang.mywust.network.HttpRequest; import cn.linghang.mywust.network.HttpRequest;
import cn.linghang.mywust.network.RequestFactory;
import cn.linghang.mywust.util.StringUtil; import cn.linghang.mywust.util.StringUtil;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
public class BkjxAuthRequestFactory extends RequestFactory { public class BkjxRequestFactory extends RequestFactory {
public static HttpRequest sessionCookieRequest(String serviceTicket) { public static HttpRequest sessionCookieRequest(String serviceTicket) {
return makeHttpRequest(String.format(Bkjx.BKJX_SESSION_COOKIE_API, serviceTicket)); return makeHttpRequest(String.format(Bkjx.BKJX_SESSION_COOKIE_API, serviceTicket));
} }
public static HttpRequest studentInfoRequest(String cookies) {
return makeHttpRequest(Bkjx.BKJX_STUDENT_INFO_API, null, cookies);
}
public static class Legacy { public static class Legacy {
public static HttpRequest dataStringRequest() { public static HttpRequest dataStringRequest() {
return makeHttpRequest(Bkjx.Legacy.BKJX_DATA_STRING_API); return makeHttpRequest(Bkjx.Legacy.BKJX_DATA_STRING_API);

@ -1,10 +1,9 @@
package cn.linghang.mywust.core.service.library; package cn.linghang.mywust.core.request;
import cn.linghang.mywust.core.api.Library; import cn.linghang.mywust.core.api.Library;
import cn.linghang.mywust.network.HttpRequest; import cn.linghang.mywust.network.HttpRequest;
import cn.linghang.mywust.network.RequestFactory;
public class LibraryAuthRequestFactory extends RequestFactory { public class LibraryRequestFactory extends RequestFactory {
public static HttpRequest sessionCookieRequest(String serviceTicket) { public static HttpRequest sessionCookieRequest(String serviceTicket) {
return makeHttpRequest(String.format(Library.LIBRARY_SESSION_COOKIE_API, serviceTicket)); return makeHttpRequest(String.format(Library.LIBRARY_SESSION_COOKIE_API, serviceTicket));
} }

@ -1,4 +1,6 @@
package cn.linghang.mywust.network; package cn.linghang.mywust.core.request;
import cn.linghang.mywust.network.HttpRequest;
import java.net.URL; import java.net.URL;

@ -1,10 +1,10 @@
package cn.linghang.mywust.core.service.undergraduate; package cn.linghang.mywust.core.service.auth;
import cn.linghang.mywust.core.api.Bkjx; import cn.linghang.mywust.core.api.Bkjx;
import cn.linghang.mywust.core.api.UnionAuth; import cn.linghang.mywust.core.api.UnionAuth;
import cn.linghang.mywust.core.exception.BasicException; import cn.linghang.mywust.core.exception.BasicException;
import cn.linghang.mywust.core.exception.PasswordWornException; import cn.linghang.mywust.core.exception.PasswordWornException;
import cn.linghang.mywust.core.service.auth.UnionLogin; import cn.linghang.mywust.core.request.BkjxRequestFactory;
import cn.linghang.mywust.network.HttpRequest; import cn.linghang.mywust.network.HttpRequest;
import cn.linghang.mywust.network.HttpResponse; import cn.linghang.mywust.network.HttpResponse;
import cn.linghang.mywust.network.RequestClientOption; import cn.linghang.mywust.network.RequestClientOption;
@ -37,7 +37,7 @@ public class JwcLogin {
@Deprecated @Deprecated
public String getLoginCookieLegacy(String username, String password, RequestClientOption requestOption) throws IOException, BasicException { public String getLoginCookieLegacy(String username, String password, RequestClientOption requestOption) throws IOException, BasicException {
// 获取某段神秘的dataStr(反正官网代码是这么叫的) // 获取某段神秘的dataStr(反正官网代码是这么叫的)
HttpRequest dataStringRequest = BkjxAuthRequestFactory.Legacy.dataStringRequest(); HttpRequest dataStringRequest = BkjxRequestFactory.Legacy.dataStringRequest();
HttpResponse dataStringResponse = requester.post(dataStringRequest, requestOption); HttpResponse dataStringResponse = requester.post(dataStringRequest, requestOption);
if (dataStringResponse.getBody() == null) { if (dataStringResponse.getBody() == null) {
throw new BasicException(); throw new BasicException();
@ -47,7 +47,7 @@ public class JwcLogin {
// 获取登录ticket // 获取登录ticket
String encoded = PasswordEncoder.legacyPassword(username, password, dataString); String encoded = PasswordEncoder.legacyPassword(username, password, dataString);
HttpRequest ticketRequest = BkjxAuthRequestFactory.Legacy.ticketRedirectRequest(encoded); HttpRequest ticketRequest = BkjxRequestFactory.Legacy.ticketRedirectRequest(encoded);
ticketRequest.setCookies(dataStringResponse.getCookies()); ticketRequest.setCookies(dataStringResponse.getCookies());
HttpResponse ticketResponse = requester.post(ticketRequest, requestOption); HttpResponse ticketResponse = requester.post(ticketRequest, requestOption);
@ -61,7 +61,7 @@ public class JwcLogin {
throw new PasswordWornException(); throw new PasswordWornException();
} }
HttpRequest sessionRequest = BkjxAuthRequestFactory.makeHttpRequest(sessionRedirect); HttpRequest sessionRequest = BkjxRequestFactory.makeHttpRequest(sessionRedirect);
HttpResponse sessionResponse = requester.get(sessionRequest, requestOption); HttpResponse sessionResponse = requester.get(sessionRequest, requestOption);
String cookies = sessionResponse.getCookies(); String cookies = sessionResponse.getCookies();
@ -73,10 +73,11 @@ public class JwcLogin {
} }
public String getLoginCookie(String username, String password, RequestClientOption requestOption) throws IOException, BasicException { public String getLoginCookie(String username, String password, RequestClientOption requestOption) throws IOException, BasicException {
String serviceTicket = unionLogin.getServiceTicket(username, password, UnionAuth.BKJX_SSO_SERVICE, requestOption); // 获取service ticket以进行进一步的登录
String serviceTicket = unionLogin.getServiceTicket(username, password, UnionAuth.Service.BKJX_SSO_SERVICE, requestOption);
// 获取登录cookie(session) // 获取登录cookie(session)
HttpRequest sessionRequest = BkjxAuthRequestFactory.sessionCookieRequest(serviceTicket); HttpRequest sessionRequest = BkjxRequestFactory.sessionCookieRequest(serviceTicket);
HttpResponse sessionResponse = requester.get(sessionRequest, requestOption); HttpResponse sessionResponse = requester.get(sessionRequest, requestOption);
String cookies = sessionResponse.getCookies(); String cookies = sessionResponse.getCookies();
@ -97,7 +98,7 @@ public class JwcLogin {
"</script>").length(); "</script>").length();
public boolean checkCookies(String cookies, RequestClientOption option) throws IOException { public boolean checkCookies(String cookies, RequestClientOption option) throws IOException {
HttpRequest testRequest = BkjxAuthRequestFactory.makeHttpRequest(Bkjx.BKJX_TEST_API, null, cookies); HttpRequest testRequest = BkjxRequestFactory.makeHttpRequest(Bkjx.BKJX_TEST_API, null, cookies);
HttpResponse testResponse = requester.get(testRequest, option); HttpResponse testResponse = requester.get(testRequest, option);
// 判断响应长度是否为178个字,登录跳转响应长度 // 判断响应长度是否为178个字,登录跳转响应长度

@ -1,9 +1,9 @@
package cn.linghang.mywust.core.service.library; package cn.linghang.mywust.core.service.auth;
import cn.linghang.mywust.core.api.Library; import cn.linghang.mywust.core.api.Library;
import cn.linghang.mywust.core.api.UnionAuth; import cn.linghang.mywust.core.api.UnionAuth;
import cn.linghang.mywust.core.exception.BasicException; import cn.linghang.mywust.core.exception.BasicException;
import cn.linghang.mywust.core.service.auth.UnionLogin; import cn.linghang.mywust.core.request.LibraryRequestFactory;
import cn.linghang.mywust.network.HttpRequest; import cn.linghang.mywust.network.HttpRequest;
import cn.linghang.mywust.network.HttpResponse; import cn.linghang.mywust.network.HttpResponse;
import cn.linghang.mywust.network.RequestClientOption; import cn.linghang.mywust.network.RequestClientOption;
@ -22,15 +22,16 @@ public class LibraryLogin {
} }
public String getLibraryLoginCookie(String username, String password, RequestClientOption requestOption) throws BasicException, IOException { public String getLibraryLoginCookie(String username, String password, RequestClientOption requestOption) throws BasicException, IOException {
String serviceTicket = unionLogin.getServiceTicket(username, password, UnionAuth.LIBRARY_SSO_SERVICE, requestOption); // 获取service ticket以进行进一步的登录
String serviceTicket = unionLogin.getServiceTicket(username, password, UnionAuth.Service.LIBRARY_SSO_SERVICE, requestOption);
// 获取登录cookie(session) // 获取登录cookie(session)
HttpRequest sessionRequest = LibraryAuthRequestFactory.sessionCookieRequest(serviceTicket); HttpRequest sessionRequest = LibraryRequestFactory.sessionCookieRequest(serviceTicket);
HttpResponse sessionResponse = requester.get(sessionRequest, requestOption); HttpResponse sessionResponse = requester.get(sessionRequest, requestOption);
String cookies = sessionResponse.getCookies(); String cookies = sessionResponse.getCookies();
// 请求一次首页 // 请求一次首页
HttpRequest indexRequest = LibraryAuthRequestFactory.indexRequest(); HttpRequest indexRequest = LibraryRequestFactory.indexRequest();
indexRequest.setCookies(cookies); indexRequest.setCookies(cookies);
requester.get(indexRequest, requestOption); requester.get(indexRequest, requestOption);
@ -39,13 +40,11 @@ public class LibraryLogin {
public boolean checkCookie(String cookies) throws IOException { public boolean checkCookie(String cookies) throws IOException {
RequestClientOption option = RequestClientOption.DEFAULT_OPTION; RequestClientOption option = RequestClientOption.DEFAULT_OPTION;
HttpRequest testRequest = LibraryRequestFactory.makeHttpRequest(Library.LIBRARY_COOKIE_TEST_URL, null, cookies);
HttpRequest testRequest = LibraryAuthRequestFactory.makeHttpRequest(Library.LIBRARY_COOKIE_TEST_URL);
testRequest.setCookies(cookies);
HttpResponse testResponse = requester.get(testRequest, option); HttpResponse testResponse = requester.get(testRequest, option);
// 响应居然是JSON,好评! // 响应居然是JSON,好评!
// 但是我们只要看有没有Unauthorized关键字就行了 // 但是我们只要看是不是Unauthorized就行了
// 未认证的话是Unauthorized(如果人家没有抽风改掉的话) // 未认证的话是Unauthorized(如果人家没有抽风改掉的话)
byte[] responseData = testResponse.getBody(); byte[] responseData = testResponse.getBody();
return responseData != null && !new String(responseData).equalsIgnoreCase("Unauthorized"); return responseData != null && !new String(responseData).equalsIgnoreCase("Unauthorized");

@ -2,6 +2,7 @@ package cn.linghang.mywust.core.service.auth;
import cn.linghang.mywust.core.exception.BasicException; import cn.linghang.mywust.core.exception.BasicException;
import cn.linghang.mywust.core.exception.PasswordWornException; import cn.linghang.mywust.core.exception.PasswordWornException;
import cn.linghang.mywust.core.request.AuthRequestFactory;
import cn.linghang.mywust.network.HttpRequest; import cn.linghang.mywust.network.HttpRequest;
import cn.linghang.mywust.network.HttpResponse; import cn.linghang.mywust.network.HttpResponse;
import cn.linghang.mywust.network.RequestClientOption; import cn.linghang.mywust.network.RequestClientOption;

@ -0,0 +1,4 @@
package cn.linghang.mywust.core.service.library;
public class LibraryService {
}

@ -0,0 +1,50 @@
package cn.linghang.mywust.core.service.undergraduate;
import cn.linghang.mywust.core.exception.CookieInvalidException;
import cn.linghang.mywust.core.exception.HtmlPageParseException;
import cn.linghang.mywust.core.parser.undergraduate.StudentInfoPageParser;
import cn.linghang.mywust.core.request.BkjxRequestFactory;
import cn.linghang.mywust.core.util.BkjxUtil;
import cn.linghang.mywust.model.undergrade.StudentInfo;
import cn.linghang.mywust.network.HttpRequest;
import cn.linghang.mywust.network.HttpResponse;
import cn.linghang.mywust.network.RequestClientOption;
import cn.linghang.mywust.network.Requester;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
public class JwcService {
private static final Logger log = LoggerFactory.getLogger(JwcService.class);
private final Requester requester;
private final StudentInfoPageParser studentInfoPageParser;
public JwcService(Requester requester, StudentInfoPageParser studentInfoPageParser) {
this.requester = requester;
this.studentInfoPageParser = studentInfoPageParser;
}
public String getStudentInfoPage(String cookies, RequestClientOption requestOption) throws IOException, CookieInvalidException {
HttpRequest request = BkjxRequestFactory.studentInfoRequest(cookies);
HttpResponse response = requester.get(request, requestOption);
// 检查响应是否正确
if (response.getBody() == null ||
response.getStatusCode() != HttpResponse.HTTP_OK ||
BkjxUtil.checkLoginFinger(response.getBody())) {
throw new CookieInvalidException();
}
return new String(response.getBody());
}
public StudentInfo getStudentInfo(String cookies, RequestClientOption requestOption) throws IOException, CookieInvalidException, HtmlPageParseException {
String studentInfoPage = this.getStudentInfoPage(cookies, requestOption);
return studentInfoPageParser.parse(studentInfoPage);
}
}

@ -0,0 +1,32 @@
package cn.linghang.mywust.core.util;
import java.nio.charset.StandardCharsets;
/**
* 专属于bkjx本科教学系统服务的工具类
*/
public class BkjxUtil {
// 在“Bkjx”系统里边如果收到的响应开头是这个的话多半是cookie无效了,需要重新登录获取cookie
private static final byte[] LOGIN_MESSAGE_RESPONSE_FINGER = "<script languge='javascript'>".getBytes(StandardCharsets.UTF_8);
/**
* <p>通过粗暴地比较响应字节前几个字符是否为登录跳转特征字符判断是否需要重新登录</p>
* <p>对于null对象一律认为不需要</p>
*
* @param response 响应的字节
* @return 是否需要重新登录
*/
public static boolean checkLoginFinger(byte[] response) {
if (response == null) {
return false;
}
for (int i = 0; i < LOGIN_MESSAGE_RESPONSE_FINGER.length; i++) {
if (LOGIN_MESSAGE_RESPONSE_FINGER[i] != response[i]) {
return false;
}
}
return true;
}
}

@ -10,6 +10,14 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>mywust-model</artifactId> <artifactId>mywust-model</artifactId>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>compile</scope>
</dependency>
</dependencies>
<properties> <properties>
<maven.compiler.source>11</maven.compiler.source> <maven.compiler.source>11</maven.compiler.source>

@ -0,0 +1,26 @@
package cn.linghang.mywust.model.undergrade;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* <p>学生数据实体类</p>
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class StudentInfo {
private String studentNumber;
private String name;
private String college;
private String major;
private String clazz;
private String birthday;
private String sex;
private String nationality;
private String hometown;
private String idNumber;
}

@ -237,6 +237,8 @@ public class SimpleOkhttpRequester implements Requester {
try (Response response = client.newCall(request).execute()) { try (Response response = client.newCall(request).execute()) {
HttpResponse httpResponse = new HttpResponse(); HttpResponse httpResponse = new HttpResponse();
httpResponse.setStatusCode(response.code());
// 取所有的响应头,如果同一个响应头字段有多个值只拿第一个 // 取所有的响应头,如果同一个响应头字段有多个值只拿第一个
Map<String, List<String>> responseHeaders = response.headers().toMultimap(); Map<String, List<String>> responseHeaders = response.headers().toMultimap();
Map<String, String> headerMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); Map<String, String> headerMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
@ -256,7 +258,7 @@ public class SimpleOkhttpRequester implements Requester {
log.debug("Receive Response: {}", response); log.debug("Receive Response: {}", response);
log.debug("Response Headers: {}", responseHeaders); log.debug("Response Headers: {}", responseHeaders);
// log.debug("Response body: {}", new String(Arrays.copyOf(httpResponse.getBody(), 64)));
return httpResponse; return httpResponse;
} }
} }

@ -6,6 +6,14 @@ import java.util.Map;
@Data @Data
public class HttpResponse { public class HttpResponse {
public static final int HTTP_OK = 200;
public static final int HTTP_FORBIDDEN = 403;
public static final int HTTP_NOT_FOUND = 404;
public static final int HTTP_SERVER_ERROR = 500;
public static final int HTTP_SERVICE_UNAVAILABLE = 503;
private int statusCode;
private Map<String, String> headers; private Map<String, String> headers;
private String cookies; private String cookies;

@ -1,6 +1,6 @@
import cn.linghang.mywust.core.exception.BasicException; import cn.linghang.mywust.core.exception.BasicException;
import cn.linghang.mywust.core.service.auth.UnionLogin; import cn.linghang.mywust.core.service.auth.UnionLogin;
import cn.linghang.mywust.core.service.undergraduate.JwcLogin; import cn.linghang.mywust.core.service.auth.JwcLogin;
import cn.linghang.mywust.network.RequestClientOption; import cn.linghang.mywust.network.RequestClientOption;
import cn.linghang.mywust.network.Requester; import cn.linghang.mywust.network.Requester;
import cn.linghang.mywust.network.okhttp.SimpleOkhttpRequester; import cn.linghang.mywust.network.okhttp.SimpleOkhttpRequester;

@ -1,6 +1,6 @@
import cn.linghang.mywust.core.exception.BasicException; import cn.linghang.mywust.core.exception.BasicException;
import cn.linghang.mywust.core.service.auth.UnionLogin; import cn.linghang.mywust.core.service.auth.UnionLogin;
import cn.linghang.mywust.core.service.undergraduate.JwcLogin; import cn.linghang.mywust.core.service.auth.JwcLogin;
import cn.linghang.mywust.network.RequestClientOption; import cn.linghang.mywust.network.RequestClientOption;
import cn.linghang.mywust.network.Requester; import cn.linghang.mywust.network.Requester;
import cn.linghang.mywust.network.okhttp.SimpleOkhttpRequester; import cn.linghang.mywust.network.okhttp.SimpleOkhttpRequester;

@ -1,7 +1,6 @@
import cn.linghang.mywust.core.exception.BasicException; import cn.linghang.mywust.core.exception.BasicException;
import cn.linghang.mywust.core.service.auth.UnionLogin; import cn.linghang.mywust.core.service.auth.UnionLogin;
import cn.linghang.mywust.core.service.library.LibraryLogin; import cn.linghang.mywust.core.service.auth.LibraryLogin;
import cn.linghang.mywust.core.service.undergraduate.JwcLogin;
import cn.linghang.mywust.network.RequestClientOption; import cn.linghang.mywust.network.RequestClientOption;
import cn.linghang.mywust.network.Requester; import cn.linghang.mywust.network.Requester;
import cn.linghang.mywust.network.okhttp.SimpleOkhttpRequester; import cn.linghang.mywust.network.okhttp.SimpleOkhttpRequester;

Loading…
Cancel
Save