[积累性更新] 1.统一认证登录:新增部分错误信息; 2.重命名部分类和方法; 3.network模块部分方法新增一些参数,部分实体类新增builder,方便使用; 4.研究生部分新增简单的Ddddocr验证码处理器; 5.SimpleOkhttpRequester新增CookieJar支持;
	
		
	
				
					
				
			
							parent
							
								
									79c6659509
								
							
						
					
					
						commit
						24c23c33e1
					
				| @ -1,5 +0,0 @@ | |||||||
| package cn.linghang.mywust.core; |  | ||||||
| 
 |  | ||||||
| public class Mywust { |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
| @ -1,5 +0,0 @@ | |||||||
| package cn.linghang.mywust.core; |  | ||||||
| 
 |  | ||||||
| public class MywustFactory { |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
| @ -1,6 +1,6 @@ | |||||||
| package cn.linghang.mywust.core.api; | package cn.linghang.mywust.core.api; | ||||||
| 
 | 
 | ||||||
| public class Graduate { | public class GraduateUrls { | ||||||
|     public static final String GRADUATE_CAPTCHA_API = "http://59.68.177.189/pyxx/PageTemplate/NsoftPage/yzm/createyzm.aspx"; |     public static final String GRADUATE_CAPTCHA_API = "http://59.68.177.189/pyxx/PageTemplate/NsoftPage/yzm/createyzm.aspx"; | ||||||
|     public static final String GRADUATE_LOGIN_API = "http://59.68.177.189/pyxx/login.aspx"; |     public static final String GRADUATE_LOGIN_API = "http://59.68.177.189/pyxx/login.aspx"; | ||||||
| 
 | 
 | ||||||
| @ -1,6 +1,6 @@ | |||||||
| package cn.linghang.mywust.core.api; | package cn.linghang.mywust.core.api; | ||||||
| 
 | 
 | ||||||
| public class Library { | public class LibraryUrls { | ||||||
|     public static final String LIBRARY_SESSION_COOKIE_API = "https://libsys.wust.edu.cn/meta-local/opac/cas/rosetta?ticket=%s"; |     public static final String LIBRARY_SESSION_COOKIE_API = "https://libsys.wust.edu.cn/meta-local/opac/cas/rosetta?ticket=%s"; | ||||||
| 
 | 
 | ||||||
|     public static final String LIBRARY_INDEX_URL = "https://libsys.wust.edu.cn/meta-local/opac/cas/rosetta"; |     public static final String LIBRARY_INDEX_URL = "https://libsys.wust.edu.cn/meta-local/opac/cas/rosetta"; | ||||||
| @ -1,6 +1,6 @@ | |||||||
| package cn.linghang.mywust.core.api; | package cn.linghang.mywust.core.api; | ||||||
| 
 | 
 | ||||||
| public class PhysicsSystem { | public class PhysicsSystemUrls { | ||||||
|     public static final String PHYSICS_LOGIN_INDEX = "http://wlsy.wust.edu.cn/Page/BI/BI000.aspx"; |     public static final String PHYSICS_LOGIN_INDEX = "http://wlsy.wust.edu.cn/Page/BI/BI000.aspx"; | ||||||
| 
 | 
 | ||||||
|     public static final String PHYSICS_LOGIN_COOKIES_API = "http://wlsy.wust.edu.cn/Page/BI/BI000.aspx"; |     public static final String PHYSICS_LOGIN_COOKIES_API = "http://wlsy.wust.edu.cn/Page/BI/BI000.aspx"; | ||||||
| @ -1,6 +1,6 @@ | |||||||
| package cn.linghang.mywust.core.api; | package cn.linghang.mywust.core.api; | ||||||
| 
 | 
 | ||||||
| public class UnionAuth { | public class UnionAuthUrls { | ||||||
|     /** |     /** | ||||||
|      * 统一认证登录验证的api地址,请求之后可进一步获取service ticket来对具体的服务进行登录获取Cookies |      * 统一认证登录验证的api地址,请求之后可进一步获取service ticket来对具体的服务进行登录获取Cookies | ||||||
|      */ |      */ | ||||||
| @ -1,72 +0,0 @@ | |||||||
| package cn.linghang.mywust.core.parser.undergraduate; |  | ||||||
| 
 |  | ||||||
| import cn.linghang.mywust.core.exception.ParseException; |  | ||||||
| import cn.linghang.mywust.core.parser.Parser; |  | ||||||
| import cn.linghang.mywust.core.util.JsoupUtil; |  | ||||||
| import cn.linghang.mywust.model.global.ExamInfo; |  | ||||||
| import org.jsoup.Jsoup; |  | ||||||
| import org.jsoup.nodes.Element; |  | ||||||
| import org.jsoup.select.Elements; |  | ||||||
| import org.slf4j.Logger; |  | ||||||
| import org.slf4j.LoggerFactory; |  | ||||||
| 
 |  | ||||||
| import java.util.ArrayList; |  | ||||||
| import java.util.List; |  | ||||||
| 
 |  | ||||||
| public class ExamInfoParser implements Parser<List<ExamInfo>> { |  | ||||||
|     private static final Logger log = LoggerFactory.getLogger(ExamInfoParser.class); |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public List<ExamInfo> parse(String html) throws ParseException { |  | ||||||
|         Elements rows = Jsoup.parse(html).selectXpath(ExamInfoXpath.EXAM_INFO_ROWS_XPATH); |  | ||||||
|         if (rows.isEmpty()) { |  | ||||||
|             throw new ParseException(html); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         List<ExamInfo> examInfos = new ArrayList<>(rows.size()); |  | ||||||
| 
 |  | ||||||
|         try { |  | ||||||
|             for (Element row : rows) { |  | ||||||
|                 // 提取出当前行的所有格子
 |  | ||||||
|                 Elements girds = row.getElementsByTag("td"); |  | ||||||
| 
 |  | ||||||
|                 // 如果这行格子数少于6个,即到了“成绩”的那个格子就没了,那就没啥意义了,直接跳过,不理了
 |  | ||||||
|                 if (girds.size() < 6) { |  | ||||||
|                     continue; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 ExamInfo examInfo = new ExamInfo(); |  | ||||||
| 
 |  | ||||||
|                 // 这段看着震撼,但其实很丑
 |  | ||||||
|                 examInfo.setId(JsoupUtil.getElementContext(girds.get(0))); |  | ||||||
| 
 |  | ||||||
|                 examInfo.setTerm(JsoupUtil.getElementContext(girds.get(1))); |  | ||||||
|                 examInfo.setCourseNumber(JsoupUtil.getElementContext(girds.get(2))); |  | ||||||
| 
 |  | ||||||
|                 examInfo.setCourseName(JsoupUtil.getElementContext(girds.get(3))); |  | ||||||
|                 examInfo.setGroupName(JsoupUtil.getElementContext(girds.get(4))); |  | ||||||
| 
 |  | ||||||
|                 examInfo.setScore(JsoupUtil.getElementContext(girds.get(5))); |  | ||||||
|                 examInfo.setFlag(JsoupUtil.getElementContext(girds.get(6))); |  | ||||||
|                 examInfo.setCredit(JsoupUtil.getElementContext(girds.get(7))); |  | ||||||
|                 examInfo.setCourseHours(JsoupUtil.getElementContext(girds.get(8))); |  | ||||||
|                 examInfo.setGradePoint(JsoupUtil.getElementContext(girds.get(9))); |  | ||||||
| 
 |  | ||||||
|                 examInfo.setEvaluateMethod(JsoupUtil.getElementContext(girds.get(11))); |  | ||||||
|                 examInfo.setKind(JsoupUtil.getElementContext(girds.get(12))); |  | ||||||
|                 examInfo.setCourseKind(JsoupUtil.getElementContext(girds.get(13))); |  | ||||||
| 
 |  | ||||||
|                 examInfos.add(examInfo); |  | ||||||
|             } |  | ||||||
|         } catch (Exception e) { |  | ||||||
|             log.warn("解析成绩页面时发生错误:{}", e.getMessage()); |  | ||||||
|             log.warn("终止解析,返回已解析数据"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return examInfos; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| final class ExamInfoXpath { |  | ||||||
|     public static final String EXAM_INFO_ROWS_XPATH = "//*[@id=\"dataList\"]/tbody/tr"; |  | ||||||
| } |  | ||||||
| @ -0,0 +1,72 @@ | |||||||
|  | package cn.linghang.mywust.core.parser.undergraduate; | ||||||
|  | 
 | ||||||
|  | import cn.linghang.mywust.core.exception.ParseException; | ||||||
|  | import cn.linghang.mywust.core.parser.Parser; | ||||||
|  | import cn.linghang.mywust.core.util.JsoupUtil; | ||||||
|  | import cn.linghang.mywust.model.global.Score; | ||||||
|  | import org.jsoup.Jsoup; | ||||||
|  | import org.jsoup.nodes.Element; | ||||||
|  | import org.jsoup.select.Elements; | ||||||
|  | import org.slf4j.Logger; | ||||||
|  | import org.slf4j.LoggerFactory; | ||||||
|  | 
 | ||||||
|  | import java.util.ArrayList; | ||||||
|  | import java.util.List; | ||||||
|  | 
 | ||||||
|  | public class UndergradScoreParser implements Parser<List<Score>> { | ||||||
|  |     private static final Logger log = LoggerFactory.getLogger(UndergradScoreParser.class); | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public List<Score> parse(String html) throws ParseException { | ||||||
|  |         Elements rows = Jsoup.parse(html).selectXpath(UndergradScoreXpath.EXAM_INFO_ROWS_XPATH); | ||||||
|  |         if (rows.isEmpty()) { | ||||||
|  |             throw new ParseException(html); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         List<Score> scores = new ArrayList<>(rows.size()); | ||||||
|  | 
 | ||||||
|  |         try { | ||||||
|  |             for (Element row : rows) { | ||||||
|  |                 // 提取出当前行的所有格子
 | ||||||
|  |                 Elements girds = row.getElementsByTag("td"); | ||||||
|  | 
 | ||||||
|  |                 // 如果这行格子数少于6个,即到了“成绩”的那个格子就没了,那就没啥意义了,直接跳过,不理了
 | ||||||
|  |                 if (girds.size() < 6) { | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 Score score = new Score(); | ||||||
|  | 
 | ||||||
|  |                 // 这段看着震撼,但其实很丑
 | ||||||
|  |                 score.setId(JsoupUtil.getElementContext(girds.get(0))); | ||||||
|  | 
 | ||||||
|  |                 score.setTerm(JsoupUtil.getElementContext(girds.get(1))); | ||||||
|  |                 score.setCourseNumber(JsoupUtil.getElementContext(girds.get(2))); | ||||||
|  | 
 | ||||||
|  |                 score.setCourseName(JsoupUtil.getElementContext(girds.get(3))); | ||||||
|  |                 score.setGroupName(JsoupUtil.getElementContext(girds.get(4))); | ||||||
|  | 
 | ||||||
|  |                 score.setScore(JsoupUtil.getElementContext(girds.get(5))); | ||||||
|  |                 score.setFlag(JsoupUtil.getElementContext(girds.get(6))); | ||||||
|  |                 score.setCredit(JsoupUtil.getElementContext(girds.get(7))); | ||||||
|  |                 score.setCourseHours(JsoupUtil.getElementContext(girds.get(8))); | ||||||
|  |                 score.setGradePoint(JsoupUtil.getElementContext(girds.get(9))); | ||||||
|  | 
 | ||||||
|  |                 score.setEvaluateMethod(JsoupUtil.getElementContext(girds.get(11))); | ||||||
|  |                 score.setKind(JsoupUtil.getElementContext(girds.get(12))); | ||||||
|  |                 score.setCourseKind(JsoupUtil.getElementContext(girds.get(13))); | ||||||
|  | 
 | ||||||
|  |                 scores.add(score); | ||||||
|  |             } | ||||||
|  |         } catch (Exception e) { | ||||||
|  |             log.warn("解析成绩页面时发生错误:{}", e.getMessage()); | ||||||
|  |             log.warn("终止解析,返回已解析数据"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return scores; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | final class UndergradScoreXpath { | ||||||
|  |     public static final String EXAM_INFO_ROWS_XPATH = "//*[@id=\"dataList\"]/tbody/tr"; | ||||||
|  | } | ||||||
| @ -1,27 +1,31 @@ | |||||||
| package cn.linghang.mywust.core.request.library; | package cn.linghang.mywust.core.request.library; | ||||||
| 
 | 
 | ||||||
| import cn.linghang.mywust.core.api.Library; | import cn.linghang.mywust.core.api.LibraryUrls; | ||||||
| import cn.linghang.mywust.core.request.RequestFactory; | import cn.linghang.mywust.core.request.RequestFactory; | ||||||
| import cn.linghang.mywust.network.entitys.HttpRequest; | import cn.linghang.mywust.network.entitys.HttpRequest; | ||||||
| 
 | 
 | ||||||
| public class LibraryRequestFactory 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(LibraryUrls.LIBRARY_SESSION_COOKIE_API, serviceTicket)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public static HttpRequest indexRequest() { |     public static HttpRequest indexRequest() { | ||||||
|         return makeHttpRequest(Library.LIBRARY_INDEX_URL); |         return makeHttpRequest(LibraryUrls.LIBRARY_INDEX_URL); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public static HttpRequest currentLoanRequest(String cookie) { |     public static HttpRequest currentLoanRequest(String cookie) { | ||||||
|         return makeHttpRequest(Library.LIBRARY_CURRENT_LOAN_API, null, cookie); |         return makeHttpRequest(LibraryUrls.LIBRARY_CURRENT_LOAN_API, null, cookie); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public static HttpRequest loanHistoryRequest(String cookie) { |     public static HttpRequest loanHistoryRequest(String cookie) { | ||||||
|         return makeHttpRequest(Library.LIBRARY_LOAN_HISTORY_API, null, cookie); |         return makeHttpRequest(LibraryUrls.LIBRARY_LOAN_HISTORY_API, null, cookie); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public static HttpRequest overdueSoonRequest(String cookie) { |     public static HttpRequest overdueSoonRequest(String cookie) { | ||||||
|         return makeHttpRequest(Library.LIBRARY_OVERDUE_SOON_API, null, cookie); |         return makeHttpRequest(LibraryUrls.LIBRARY_OVERDUE_SOON_API, null, cookie); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public static HttpRequest bookInfoRequest(String cookie) { | ||||||
|  |         return makeHttpRequest(LibraryUrls.LIBRARY_OVERDUE_SOON_API, null, cookie); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,62 +0,0 @@ | |||||||
| package cn.linghang.mywust.core.service.auth; |  | ||||||
| 
 |  | ||||||
| import cn.linghang.mywust.core.api.Bkjx; |  | ||||||
| import cn.linghang.mywust.core.api.UnionAuth; |  | ||||||
| import cn.linghang.mywust.core.exception.ApiException; |  | ||||||
| import cn.linghang.mywust.core.exception.BasicException; |  | ||||||
| import cn.linghang.mywust.core.request.undergrade.BkjxRequestFactory; |  | ||||||
| import cn.linghang.mywust.network.RequestClientOption; |  | ||||||
| import cn.linghang.mywust.network.Requester; |  | ||||||
| import cn.linghang.mywust.network.entitys.HttpRequest; |  | ||||||
| import cn.linghang.mywust.network.entitys.HttpResponse; |  | ||||||
| import org.slf4j.Logger; |  | ||||||
| import org.slf4j.LoggerFactory; |  | ||||||
| 
 |  | ||||||
| import java.io.IOException; |  | ||||||
| 
 |  | ||||||
| public class JwcLogin { |  | ||||||
|     private static final Logger log = LoggerFactory.getLogger(JwcLogin.class); |  | ||||||
| 
 |  | ||||||
|     private final Requester requester; |  | ||||||
| 
 |  | ||||||
|     private final UnionLogin unionLogin; |  | ||||||
| 
 |  | ||||||
|     public JwcLogin(Requester requester) { |  | ||||||
|         this.requester = requester; |  | ||||||
|         this.unionLogin = new UnionLogin(requester); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public String getLoginCookie(String username, String password, RequestClientOption requestOption) throws IOException, ApiException { |  | ||||||
|         // 获取service ticket以进行进一步的登录
 |  | ||||||
|         String serviceTicket = unionLogin.getServiceTicket(username, password, UnionAuth.Service.BKJX_SSO_SERVICE, requestOption); |  | ||||||
| 
 |  | ||||||
|         // 获取登录cookie(session)
 |  | ||||||
|         HttpRequest sessionRequest = BkjxRequestFactory.sessionCookieRequest(serviceTicket); |  | ||||||
|         HttpResponse sessionResponse = requester.get(sessionRequest, requestOption); |  | ||||||
| 
 |  | ||||||
|         String cookies = sessionResponse.getCookies(); |  | ||||||
|         if (roughCheckCookie(cookies)) { |  | ||||||
|             throw new ApiException(ApiException.Code.UNKNOWN_EXCEPTION); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return cookies; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private boolean roughCheckCookie(String cookies) { |  | ||||||
|         return cookies == null || !cookies.contains("JSESSIONID") || !cookies.contains("SERVERID"); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private static final int COOKIES_ERROR_RESPONSE_LENGTH = |  | ||||||
|             ("<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 checkCookies(String cookies, RequestClientOption option) throws IOException { |  | ||||||
|         HttpRequest testRequest = BkjxRequestFactory.makeHttpRequest(Bkjx.BKJX_TEST_API, null, cookies); |  | ||||||
|         HttpResponse testResponse = requester.get(testRequest, option); |  | ||||||
| 
 |  | ||||||
|         // 判断响应长度是否为这么多个字,登录跳转响应长度
 |  | ||||||
|         // 这种办法虽然在极端情况下可能会出错(而且还挺蠢的),但是是最快的办法中比较准确的了
 |  | ||||||
|         return testResponse.getBody().length != COOKIES_ERROR_RESPONSE_LENGTH; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
| @ -0,0 +1,110 @@ | |||||||
|  | package cn.linghang.mywust.core.service.auth; | ||||||
|  | 
 | ||||||
|  | import cn.linghang.mywust.core.api.UndergradUrls; | ||||||
|  | import cn.linghang.mywust.core.api.UnionAuthUrls; | ||||||
|  | import cn.linghang.mywust.core.exception.ApiException; | ||||||
|  | import cn.linghang.mywust.core.request.undergrade.BkjxRequestFactory; | ||||||
|  | import cn.linghang.mywust.network.RequestClientOption; | ||||||
|  | import cn.linghang.mywust.network.Requester; | ||||||
|  | import cn.linghang.mywust.network.entitys.HttpRequest; | ||||||
|  | import cn.linghang.mywust.network.entitys.HttpResponse; | ||||||
|  | import cn.linghang.mywust.util.PasswordEncoder; | ||||||
|  | import org.slf4j.Logger; | ||||||
|  | import org.slf4j.LoggerFactory; | ||||||
|  | 
 | ||||||
|  | import java.io.IOException; | ||||||
|  | 
 | ||||||
|  | public class UndergraduateLogin { | ||||||
|  |     private static final Logger log = LoggerFactory.getLogger(UndergraduateLogin.class); | ||||||
|  | 
 | ||||||
|  |     private final Requester requester; | ||||||
|  | 
 | ||||||
|  |     private final UnionLogin unionLogin; | ||||||
|  | 
 | ||||||
|  |     public UndergraduateLogin(Requester requester) { | ||||||
|  |         this.requester = requester; | ||||||
|  |         this.unionLogin = new UnionLogin(requester); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public String getLoginCookie(String username, String password, RequestClientOption requestOption) throws IOException, ApiException { | ||||||
|  |         // 获取service ticket以进行进一步的登录
 | ||||||
|  |         String serviceTicket = unionLogin.getServiceTicket(username, password, UnionAuthUrls.Service.BKJX_SSO_SERVICE, requestOption); | ||||||
|  | 
 | ||||||
|  |         // 获取登录cookie(session)
 | ||||||
|  |         HttpRequest sessionRequest = BkjxRequestFactory.sessionCookieRequest(serviceTicket); | ||||||
|  |         HttpResponse sessionResponse = requester.get(sessionRequest, requestOption); | ||||||
|  | 
 | ||||||
|  |         String cookies = sessionResponse.getCookies(); | ||||||
|  |         if (checkCookies(cookies, requestOption)) { | ||||||
|  |             log.warn("[mywust]: Cookie检查不通过:{}", cookies); | ||||||
|  |             throw new ApiException(ApiException.Code.UNKNOWN_EXCEPTION, "登录获取的Cookie无效"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return cookies; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * <p>旧版的登录方式,既相当于直接登录<a href="http://bkjx.wust.edu.cn">bkjx系统</a>,不建议使用</p> | ||||||
|  |      * <p>注意,这种登录方式的密码和新版可能是不一样的</p> | ||||||
|  |      * <p>不过不论使用哪种登录方式获取到的cookie都是可用的</p> | ||||||
|  |      * | ||||||
|  |      * @return 获取到的Cookies | ||||||
|  |      */ | ||||||
|  |     @Deprecated | ||||||
|  |     public String getLoginCookieLegacy(String username, String password, RequestClientOption requestOption) throws IOException, ApiException { | ||||||
|  |         // 获取某段神秘的dataStr(反正官网代码是这么叫的)
 | ||||||
|  |         HttpRequest dataStringRequest = BkjxRequestFactory.Legacy.dataStringRequest(); | ||||||
|  |         HttpResponse dataStringResponse = requester.post(dataStringRequest, requestOption); | ||||||
|  |         if (dataStringResponse.getBody() == null) { | ||||||
|  |             log.warn("[mywust]: 本科教学系统旧版登录方式:获取dataStr时发生错误"); | ||||||
|  |             throw new ApiException(ApiException.Code.UNKNOWN_EXCEPTION); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         String dataString = new String(dataStringResponse.getBody()); | ||||||
|  | 
 | ||||||
|  |         // 获取登录ticket
 | ||||||
|  |         String encoded = PasswordEncoder.legacyPassword(username, password, dataString); | ||||||
|  |         HttpRequest ticketRequest = BkjxRequestFactory.Legacy.ticketRedirectRequest(encoded); | ||||||
|  |         ticketRequest.setCookies(dataStringResponse.getCookies()); | ||||||
|  | 
 | ||||||
|  |         HttpResponse ticketResponse = requester.post(ticketRequest, requestOption); | ||||||
|  |         if (ticketResponse.getBody() == null) { | ||||||
|  |             log.warn("[mywust]: 本科教学系统旧版登录方式:获取登录ticket时发生错误"); | ||||||
|  |             throw new ApiException(ApiException.Code.UNKNOWN_EXCEPTION); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // 使用跳转得到的链接获取cookies
 | ||||||
|  |         String sessionRedirect = ticketResponse.getHeaders().get("Location"); | ||||||
|  |         if (sessionRedirect == null) { | ||||||
|  |             throw new ApiException(ApiException.Code.BKJX_LEGACY_LOGIN_PASSWORD_WRONG); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         HttpRequest sessionRequest = BkjxRequestFactory.makeHttpRequest(sessionRedirect); | ||||||
|  |         HttpResponse sessionResponse = requester.get(sessionRequest, requestOption); | ||||||
|  | 
 | ||||||
|  |         String cookies = sessionResponse.getCookies(); | ||||||
|  |         if (checkCookies(cookies, requestOption)) { | ||||||
|  |             log.warn("[mywust]: Cookie检查不通过:{}", cookies); | ||||||
|  |             throw new ApiException(ApiException.Code.UNKNOWN_EXCEPTION, "登录获取的Cookie无效"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return cookies; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private boolean roughCheckCookie(String cookies) { | ||||||
|  |         return cookies == null || !cookies.contains("JSESSIONID") || !cookies.contains("SERVERID"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private static final int COOKIES_ERROR_RESPONSE_LENGTH = | ||||||
|  |             ("<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 checkCookies(String cookies, RequestClientOption option) throws IOException { | ||||||
|  |         HttpRequest testRequest = BkjxRequestFactory.makeHttpRequest(UndergradUrls.BKJX_TEST_API, null, cookies); | ||||||
|  |         HttpResponse testResponse = requester.get(testRequest, option); | ||||||
|  | 
 | ||||||
|  |         // 判断响应长度是否为这么多个字,登录跳转响应长度
 | ||||||
|  |         // 这种办法虽然在极端情况下可能会出错(而且还挺蠢的),但是是最快的办法中比较准确的了
 | ||||||
|  |         return Math.abs(COOKIES_ERROR_RESPONSE_LENGTH - testResponse.getBody().length) > 8; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,43 @@ | |||||||
|  | package cn.linghang.mywust.core.service.captcha.solver; | ||||||
|  | 
 | ||||||
|  | import cn.linghang.mywust.captcha.SolvedImageCaptcha; | ||||||
|  | import cn.linghang.mywust.captcha.UnsolvedImageCaptcha; | ||||||
|  | import cn.linghang.mywust.core.exception.ApiException; | ||||||
|  | import cn.linghang.mywust.core.request.RequestFactory; | ||||||
|  | import cn.linghang.mywust.network.Requester; | ||||||
|  | import cn.linghang.mywust.network.entitys.HttpRequest; | ||||||
|  | import cn.linghang.mywust.network.entitys.HttpResponse; | ||||||
|  | 
 | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.util.Base64; | ||||||
|  | 
 | ||||||
|  | public class DdddOcrCaptchaSolver implements CaptchaSolver { | ||||||
|  |     private final String baseUrl; | ||||||
|  | 
 | ||||||
|  |     private final Requester requester; | ||||||
|  | 
 | ||||||
|  |     public DdddOcrCaptchaSolver(String baseUrl, Requester requester) { | ||||||
|  |         this.baseUrl = baseUrl; | ||||||
|  |         this.requester = requester; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public SolvedImageCaptcha solve(UnsolvedImageCaptcha unsolvedImageCaptcha) throws ApiException { | ||||||
|  |         try { | ||||||
|  |             SolvedImageCaptcha solvedImageCaptcha = new SolvedImageCaptcha(unsolvedImageCaptcha); | ||||||
|  | 
 | ||||||
|  |             String result = this.ocr(unsolvedImageCaptcha.getImage()); | ||||||
|  |             solvedImageCaptcha.setResult(result); | ||||||
|  |             return solvedImageCaptcha; | ||||||
|  |         } catch (IOException e) { | ||||||
|  |             throw new ApiException(ApiException.Code.NETWORK_EXCEPTION); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private String ocr(byte[] data) throws IOException { | ||||||
|  |         HttpRequest request = RequestFactory.makeHttpRequest(baseUrl + "/ocr/b64/text", Base64.getEncoder().encode(data)); | ||||||
|  |         HttpResponse response = requester.post(request); | ||||||
|  | 
 | ||||||
|  |         return response.getStringBody(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -1,11 +0,0 @@ | |||||||
| package cn.linghang.mywust.core.service.captcha.solver; |  | ||||||
| 
 |  | ||||||
| import cn.linghang.mywust.captcha.SolvedImageCaptcha; |  | ||||||
| import cn.linghang.mywust.captcha.UnsolvedImageCaptcha; |  | ||||||
| 
 |  | ||||||
| public class LocalPaddleOcrCaptchaSolver implements CaptchaSolver { |  | ||||||
|     @Override |  | ||||||
|     public SolvedImageCaptcha solve(UnsolvedImageCaptcha unsolvedImageCaptcha) { |  | ||||||
|         return null; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,33 +0,0 @@ | |||||||
| package cn.linghang.mywust.core.service.undergraduate; |  | ||||||
| 
 |  | ||||||
| import cn.linghang.mywust.core.exception.ApiException; |  | ||||||
| import cn.linghang.mywust.core.exception.ParseException; |  | ||||||
| import cn.linghang.mywust.core.parser.undergraduate.CourseTableParser; |  | ||||||
| import cn.linghang.mywust.core.request.undergrade.BkjxRequestFactory; |  | ||||||
| import cn.linghang.mywust.model.global.Course; |  | ||||||
| import cn.linghang.mywust.network.RequestClientOption; |  | ||||||
| import cn.linghang.mywust.network.Requester; |  | ||||||
| import cn.linghang.mywust.network.entitys.HttpRequest; |  | ||||||
| import cn.linghang.mywust.network.entitys.HttpResponse; |  | ||||||
| import org.slf4j.Logger; |  | ||||||
| import org.slf4j.LoggerFactory; |  | ||||||
| 
 |  | ||||||
| import java.io.IOException; |  | ||||||
| import java.util.List; |  | ||||||
| 
 |  | ||||||
| public class CourseTableApiService extends UndergraduateApiService { |  | ||||||
| 
 |  | ||||||
|     public CourseTableApiService(Requester requester) { |  | ||||||
|         super(requester); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public String getCourseTablePage(String term, String cookies, RequestClientOption requestClientOption) throws IOException, ApiException { |  | ||||||
|         HttpRequest request = BkjxRequestFactory.courseTablePageRequest(term, cookies); |  | ||||||
|         HttpResponse response = requester.post(request, requestClientOption); |  | ||||||
| 
 |  | ||||||
|         super.checkResponse(response); |  | ||||||
| 
 |  | ||||||
|         return new String(response.getBody()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
| @ -0,0 +1,49 @@ | |||||||
|  | package cn.linghang.mywust.core.service.undergraduate; | ||||||
|  | 
 | ||||||
|  | import cn.linghang.mywust.core.exception.ApiException; | ||||||
|  | import cn.linghang.mywust.core.request.undergrade.BkjxRequestFactory; | ||||||
|  | import cn.linghang.mywust.network.RequestClientOption; | ||||||
|  | import cn.linghang.mywust.network.Requester; | ||||||
|  | import cn.linghang.mywust.network.entitys.HttpRequest; | ||||||
|  | import cn.linghang.mywust.network.entitys.HttpResponse; | ||||||
|  | 
 | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.util.Map; | ||||||
|  | 
 | ||||||
|  | public class UndergradCourseTableApiService extends UndergradApiServiceBase { | ||||||
|  | 
 | ||||||
|  |     private static final String[] NECESSARY_PARAMS = {"term"}; | ||||||
|  | 
 | ||||||
|  |     public UndergradCourseTableApiService(Requester requester) { | ||||||
|  |         super(requester); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public String getPage(String cookie, Map<String, String> params, RequestClientOption option) throws ApiException, IOException { | ||||||
|  |         for (String key : NECESSARY_PARAMS) { | ||||||
|  |             if (params.get(key) == null) { | ||||||
|  |                 throw new ApiException(ApiException.Code.PARAM_WRONG_EXCEPTION); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return this.getPage(params.get(NECESSARY_PARAMS[0]), cookie, option); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public String getPage(String cookie, Map<String, String> params) throws ApiException, IOException { | ||||||
|  |         return this.getPage(cookie, params, null); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public String getPage(String term, String cookies, RequestClientOption requestClientOption) throws IOException, ApiException { | ||||||
|  |         HttpRequest request = BkjxRequestFactory.courseTablePageRequest(term, cookies); | ||||||
|  |         HttpResponse response = requester.post(request, requestClientOption); | ||||||
|  | 
 | ||||||
|  |         super.checkResponse(response); | ||||||
|  | 
 | ||||||
|  |         return new String(response.getBody()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public String getPage(String term, String cookies) throws IOException, ApiException { | ||||||
|  |         return getPage(term, cookies, null); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -1,27 +1,32 @@ | |||||||
| package cn.linghang.mywust.core.service.undergraduate; | package cn.linghang.mywust.core.service.undergraduate; | ||||||
| 
 | 
 | ||||||
| import cn.linghang.mywust.core.exception.ApiException; | import cn.linghang.mywust.core.exception.ApiException; | ||||||
| import cn.linghang.mywust.core.exception.ParseException; |  | ||||||
| import cn.linghang.mywust.core.parser.undergraduate.ExamInfoParser; |  | ||||||
| import cn.linghang.mywust.core.request.undergrade.BkjxRequestFactory; | import cn.linghang.mywust.core.request.undergrade.BkjxRequestFactory; | ||||||
| import cn.linghang.mywust.model.global.ExamInfo; |  | ||||||
| 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.entitys.HttpRequest; | import cn.linghang.mywust.network.entitys.HttpRequest; | ||||||
| import cn.linghang.mywust.network.entitys.HttpResponse; | import cn.linghang.mywust.network.entitys.HttpResponse; | ||||||
| import org.slf4j.Logger; |  | ||||||
| import org.slf4j.LoggerFactory; |  | ||||||
| 
 | 
 | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| import java.util.List; | import java.util.Map; | ||||||
| 
 | 
 | ||||||
| public class ExamInfoApiService extends UndergraduateApiService { | public class UndergradScoreApiService extends UndergradApiServiceBase { | ||||||
| 
 | 
 | ||||||
|     public ExamInfoApiService(Requester requester) { |     public UndergradScoreApiService(Requester requester) { | ||||||
|         super(requester); |         super(requester); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public String getExamInfoPage(String cookies, RequestClientOption requestClientOption) throws IOException, ApiException { |     @Override | ||||||
|  |     public String getPage(String cookie, Map<String, String> params, RequestClientOption option) throws ApiException, IOException { | ||||||
|  |         return this.getPage(cookie, option); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public String getPage(String cookie, Map<String, String> params) throws ApiException, IOException { | ||||||
|  |         return this.getPage(cookie, params, null); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public String getPage(String cookies, RequestClientOption requestClientOption) throws IOException, ApiException { | ||||||
|         HttpRequest request = BkjxRequestFactory.examScoreInfoRequest(cookies, "", "", ""); |         HttpRequest request = BkjxRequestFactory.examScoreInfoRequest(cookies, "", "", ""); | ||||||
|         HttpResponse response = requester.post(request, requestClientOption); |         HttpResponse response = requester.post(request, requestClientOption); | ||||||
| 
 | 
 | ||||||
| @ -1,27 +1,70 @@ | |||||||
| package cn.linghang.mywust.network; | package cn.linghang.mywust.network; | ||||||
| 
 | 
 | ||||||
|  | import lombok.Builder; | ||||||
| import lombok.Data; | import lombok.Data; | ||||||
| 
 | 
 | ||||||
| @Data | @Data | ||||||
|  | @Builder | ||||||
| public class RequestClientOption { | public class RequestClientOption { | ||||||
|     private Proxy proxy; |     private Proxy proxy; | ||||||
|     private long timeout; |     private long timeout; | ||||||
|     private boolean fallowUrlRedirect = false; |     private boolean fallowUrlRedirect; | ||||||
|  | 
 | ||||||
|  |     private boolean ignoreSSLError; | ||||||
| 
 | 
 | ||||||
|     @Data |     @Data | ||||||
|  |     @Builder | ||||||
|     public static class Proxy { |     public static class Proxy { | ||||||
|         private String address; |         private String address; | ||||||
|         private int port; |         private int port; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     public RequestClientOption() { | ||||||
|  |         this.proxy = null; | ||||||
|  |         this.timeout = 5; | ||||||
|  |         this.fallowUrlRedirect = false; | ||||||
|  |         this.ignoreSSLError = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public RequestClientOption(Proxy proxy, long timeout, boolean fallowUrlRedirect, boolean ignoreSSLError) { | ||||||
|  |         this.proxy = proxy; | ||||||
|  |         this.timeout = timeout; | ||||||
|  |         this.fallowUrlRedirect = fallowUrlRedirect; | ||||||
|  |         this.ignoreSSLError = ignoreSSLError; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     public static final RequestClientOption DEFAULT_OPTION = newDefaultOption(); |     public static final RequestClientOption DEFAULT_OPTION = newDefaultOption(); | ||||||
| 
 | 
 | ||||||
|     private static RequestClientOption newDefaultOption() { |     private static RequestClientOption newDefaultOption() { | ||||||
|         RequestClientOption option = new RequestClientOption(); |         return new ConstantRequestClientOption(); | ||||||
|         option.setTimeout(5); |     } | ||||||
|         option.setProxy(null); | 
 | ||||||
|         option.setFallowUrlRedirect(false); |     /** | ||||||
|  |      * 常量化的RequestClientOption,对其使用setter时不会起任何作用 | ||||||
|  |      */ | ||||||
|  |     public static class ConstantRequestClientOption extends RequestClientOption { | ||||||
|  |         @Override | ||||||
|  |         public Proxy getProxy() { | ||||||
|  |             return super.getProxy(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         @Override | ||||||
|  |         public long getTimeout() { | ||||||
|  |             return super.getTimeout(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         @Override | ||||||
|  |         public boolean isFallowUrlRedirect() { | ||||||
|  |             return super.isFallowUrlRedirect(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         @Override | ||||||
|  |         public void setProxy(Proxy proxy) {} | ||||||
|  | 
 | ||||||
|  |         @Override | ||||||
|  |         public void setTimeout(long timeout) {} | ||||||
| 
 | 
 | ||||||
|         return option; |         @Override | ||||||
|  |         public void setFallowUrlRedirect(boolean fallowUrlRedirect) {} | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
					Loading…
					
					
				
		Reference in new issue