feat: 本科生:新增在“专属选课时间段”被“封号”的判断处理

main
lensfrex 1 year ago
parent 5fd5099181
commit c13aa2f56f
Signed by: lensfrex
GPG Key ID: 0F69A0A2FBEE98A0
  1. 5
      mywust-common/src/main/java/cn/wustlinghang/mywust/exception/ApiException.java
  2. 35
      mywust-core/src/main/java/cn/wustlinghang/mywust/core/request/service/auth/UndergraduateLogin.java
  3. 6
      mywust-core/src/main/java/cn/wustlinghang/mywust/core/request/service/undergraduate/UndergradApiServiceBase.java
  4. 40
      mywust-core/src/main/java/cn/wustlinghang/mywust/core/util/BkjxUtil.java

@ -99,6 +99,11 @@ public class ApiException extends BasicException {
*/ */
UNI_LOGIN_NEED_TFA(100107, "统一认证登录: 用户账号需要TFA二步验证"), UNI_LOGIN_NEED_TFA(100107, "统一认证登录: 用户账号需要TFA二步验证"),
/**
* 专属选课时间段账号被禁用
*/
UNDERGRAD_BANNED_IN_EXCLUSIVE_TIME(100108, "本科生登录:专属选课时间段账号被禁用"),
// -------------------------------- // --------------------------------
// 共有异常码:cookie无效 // 共有异常码:cookie无效

@ -84,36 +84,43 @@ public class UndergraduateLogin {
} }
private void checkCookie(String cookies, RequestClientOption requestOption) throws ApiException, IOException { private void checkCookie(String cookies, RequestClientOption requestOption) throws ApiException, IOException {
if (roughCheckCookieFail(cookies)) { if (!roughCheckCookie(cookies)) {
log.error("[mywust]: Cookie粗查不通过:{}", cookies); log.error("[mywust]: Cookie粗查不通过:{}", cookies);
throw new ApiException(ApiException.Code.UNKNOWN_EXCEPTION, "登录获取的Cookie无效"); throw new ApiException(ApiException.Code.UNKNOWN_EXCEPTION, "登录获取的Cookie无效");
} }
// 检查Cookie是否真正可用,同时请求一次任意接口使后续接口能够正确响应 // 检查Cookie是否真正可用,同时请求一次任意接口使后续接口能够正确响应
// 拿到Cookie后调用的第一个接口有时候会产生302/301跳转到主页,需要再次调用才能正确响应 // 拿到Cookie后调用的第一个接口有时候会产生302/301跳转到主页,需要再次调用才能正确响应
if (checkCookiesFail(cookies, requestOption)) { if (!testCookie(cookies, requestOption)) {
log.warn("[mywust]: Cookie检查不通过:{}", cookies); log.warn("[mywust]: Cookie检查不通过:{}", cookies);
} }
} }
private boolean roughCheckCookieFail(String cookies) { private boolean roughCheckCookie(String cookies) {
return cookies == null || !cookies.contains("JSESSIONID") || !cookies.contains("SERVERID"); return cookies != null && cookies.contains("JSESSIONID") && cookies.contains("SERVERID");
} }
private static final int COOKIES_ERROR_RESPONSE_LENGTH = public boolean testCookie(String cookies, RequestClientOption option) throws IOException, ApiException {
("<script languge='javascript'>window.location.href='https://auth.wust.edu.cn/lyuapServer/login?service=http%3A%2F%2Fbkjx.wust.edu.cn%2Fjsxsd%2Fframework%2FblankPage.jsp'</script>")
.length();
public boolean checkCookiesFail(String cookies, RequestClientOption option) throws IOException {
HttpRequest testRequest = BkjxRequestFactory.makeHttpRequest(UndergradUrls.BKJX_TEST_API, null, cookies); HttpRequest testRequest = BkjxRequestFactory.makeHttpRequest(UndergradUrls.BKJX_TEST_API, null, cookies);
HttpResponse testResponse = requester.get(testRequest, option); HttpResponse testResponse = requester.get(testRequest, option);
// 判断响应长度是否为这么多个字,登录跳转响应长度 // 空白页返回的是很短的几个换行符,优先判断响应长度,暂时定为8
// 这种办法虽然在极端情况下可能会出错(而且还挺蠢的),但是是最快的办法中比较准确的了 if (testResponse.getBody().length < 8) {
return Math.abs(COOKIES_ERROR_RESPONSE_LENGTH - testResponse.getBody().length) <= 8; return true;
}
String test = testResponse.getStringBody();
if (test.contains("script")) {
return false;
} else if (test.contains("禁用")) {
// 选课时间段
throw new ApiException(ApiException.Code.UNDERGRAD_BANNED_IN_EXCLUSIVE_TIME);
}
return true;
} }
public boolean checkCookiesFail(String cookies) throws IOException { public boolean testCookie(String cookies) throws IOException, ApiException {
return this.checkCookiesFail(cookies, null); return this.testCookie(cookies, null);
} }
} }

@ -1,9 +1,9 @@
package cn.wustlinghang.mywust.core.request.service.undergraduate; package cn.wustlinghang.mywust.core.request.service.undergraduate;
import cn.wustlinghang.mywust.core.api.UndergradUrls; import cn.wustlinghang.mywust.core.api.UndergradUrls;
import cn.wustlinghang.mywust.exception.ApiException;
import cn.wustlinghang.mywust.core.request.factory.RequestFactory; import cn.wustlinghang.mywust.core.request.factory.RequestFactory;
import cn.wustlinghang.mywust.core.util.BkjxUtil; import cn.wustlinghang.mywust.core.util.BkjxUtil;
import cn.wustlinghang.mywust.exception.ApiException;
import cn.wustlinghang.mywust.network.RequestClientOption; import cn.wustlinghang.mywust.network.RequestClientOption;
import cn.wustlinghang.mywust.network.Requester; import cn.wustlinghang.mywust.network.Requester;
import cn.wustlinghang.mywust.network.entitys.HttpRequest; import cn.wustlinghang.mywust.network.entitys.HttpRequest;
@ -23,7 +23,8 @@ public abstract class UndergradApiServiceBase {
// 检查响应是否正确 // 检查响应是否正确
if (response.getBody() == null || if (response.getBody() == null ||
response.getStatusCode() != HttpResponse.HTTP_OK || response.getStatusCode() != HttpResponse.HTTP_OK ||
BkjxUtil.hasLoginFinger(response.getBody())) { BkjxUtil.needLogin(response.getBody()) ||
BkjxUtil.isBannedResponse(response)) {
throw new ApiException(ApiException.Code.COOKIE_INVALID); throw new ApiException(ApiException.Code.COOKIE_INVALID);
} }
@ -41,5 +42,6 @@ public abstract class UndergradApiServiceBase {
} }
public abstract String getPage(String cookie, Map<String, String> params, RequestClientOption option) throws ApiException, IOException; public abstract String getPage(String cookie, Map<String, String> params, RequestClientOption option) throws ApiException, IOException;
public abstract String getPage(String cookie, Map<String, String> params) throws ApiException, IOException; public abstract String getPage(String cookie, Map<String, String> params) throws ApiException, IOException;
} }

@ -1,5 +1,7 @@
package cn.wustlinghang.mywust.core.util; package cn.wustlinghang.mywust.core.util;
import cn.wustlinghang.mywust.network.entitys.HttpResponse;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
/** /**
@ -11,12 +13,13 @@ public class BkjxUtil {
/** /**
* <p>通过粗暴地比较响应字节前几个字符是否为登录跳转特征字符判断是否需要重新登录</p> * <p>通过粗暴地比较响应字节前几个字符是否为登录跳转特征字符判断是否需要重新登录</p>
* <p>只比较前若干个字节在数据较大时比较有用</p>
* <p>对于null对象一律认为不需要</p> * <p>对于null对象一律认为不需要</p>
* *
* @param response 响应的字节 * @param response 响应的字节
* @return 是否需要重新登录 * @return 是否需要重新登录
*/ */
public static boolean hasLoginFinger(byte[] response) { public static boolean needLogin(byte[] response) {
if (response == null) { if (response == null) {
return false; return false;
} }
@ -29,4 +32,39 @@ public class BkjxUtil {
return true; return true;
} }
/**
* <p>检查是否为专属选课时间封号</p>
* <p>对于null对象一律认为不是</p>
*
* @param response HttpResponse响应
* @return 是否为封号响应
*/
public static boolean isBannedResponse(HttpResponse response) {
if (response == null || response.getBody() == null) {
return false;
}
// 这里用了个取巧的办法,如果响应体大于400bytes,则直接认为不是封号的响应,不继续比较关键字
if (response.getBody().length > 400) {
return false;
}
return isBannedResponse(response.getStringBody());
}
/**
* <p>检查是否为专属选课时间封号</p>
* <p>对于null对象一律认为不是</p>
*
* @param responseText 响应字符串
* @return 是否为封号响应
*/
public static boolean isBannedResponse(String responseText) {
if (responseText == null) {
return false;
}
return responseText.contains("当前登录帐号已被禁用");
}
} }

Loading…
Cancel
Save