diff --git a/mywust-core/src/main/java/cn/linghang/mywust/core/api/PhysicsSystem.java b/mywust-core/src/main/java/cn/linghang/mywust/core/api/PhysicsSystem.java new file mode 100644 index 0000000..874a82c --- /dev/null +++ b/mywust-core/src/main/java/cn/linghang/mywust/core/api/PhysicsSystem.java @@ -0,0 +1,9 @@ +package cn.linghang.mywust.core.api; + +public class PhysicsSystem { + 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_MAIN_INDEX_URL = "http://wlsy.wust.edu.cn/Page/BI/BI0000.aspx"; +} diff --git a/mywust-core/src/main/java/cn/linghang/mywust/core/parser/physics/PhysicsIndexPageParser.java b/mywust-core/src/main/java/cn/linghang/mywust/core/parser/physics/PhysicsIndexPageParser.java new file mode 100644 index 0000000..4989a57 --- /dev/null +++ b/mywust-core/src/main/java/cn/linghang/mywust/core/parser/physics/PhysicsIndexPageParser.java @@ -0,0 +1,20 @@ +package cn.linghang.mywust.core.parser.physics; + +import cn.linghang.mywust.core.exception.HtmlPageParseException; +import cn.linghang.mywust.core.parser.Parser; +import cn.linghang.mywust.core.parser.physics.xpath.PhysicsIndexXpath; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.select.Elements; + +public class PhysicsIndexPageParser implements Parser { + public String parse(String html) throws HtmlPageParseException { + Document page = Jsoup.parse(html); + Elements linkElements = page.selectXpath(PhysicsIndexXpath.PHYSICS_LINK_XPATH); + if (linkElements.isEmpty()) { + throw new HtmlPageParseException(); + } + + return linkElements.get(0).attr("href"); + } +} diff --git a/mywust-core/src/main/java/cn/linghang/mywust/core/parser/physics/xpath/PhysicsIndexXpath.java b/mywust-core/src/main/java/cn/linghang/mywust/core/parser/physics/xpath/PhysicsIndexXpath.java new file mode 100644 index 0000000..da3ec34 --- /dev/null +++ b/mywust-core/src/main/java/cn/linghang/mywust/core/parser/physics/xpath/PhysicsIndexXpath.java @@ -0,0 +1,5 @@ +package cn.linghang.mywust.core.parser.physics.xpath; + +public class PhysicsIndexXpath { + public static final String PHYSICS_LINK_XPATH = "//*[@id=\"rolemenu\"]/tbody/tr[1]/td[1]/a[2]"; +} diff --git a/mywust-core/src/main/java/cn/linghang/mywust/core/request/PhysicsSystemRequestFactory.java b/mywust-core/src/main/java/cn/linghang/mywust/core/request/PhysicsSystemRequestFactory.java new file mode 100644 index 0000000..a71e986 --- /dev/null +++ b/mywust-core/src/main/java/cn/linghang/mywust/core/request/PhysicsSystemRequestFactory.java @@ -0,0 +1,63 @@ +package cn.linghang.mywust.core.request; + +import cn.linghang.mywust.core.api.PhysicsSystem; +import cn.linghang.mywust.network.HttpRequest; +import cn.linghang.mywust.util.StringUtil; + +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + +public class PhysicsSystemRequestFactory extends RequestFactory { + public static HttpRequest loginIndexRequest() { + return makeHttpRequest(PhysicsSystem.PHYSICS_LOGIN_INDEX); + } + + public static HttpRequest loginCookiesRequest(String username, String password, String cookies) { + byte[] queryData = StringUtil.generateQueryString(makeLoginQueryParam(username, password)).getBytes(StandardCharsets.UTF_8); + return makeHttpRequest(PhysicsSystem.PHYSICS_LOGIN_COOKIES_API, queryData, cookies); + } + + public static HttpRequest mainIndexRequest(String cookies) { + return makeHttpRequest(PhysicsSystem.PHYSICS_MAIN_INDEX_URL, null, cookies); + } + + public static HttpRequest physicsSystemIndexRequest(String redirect, String cookies) { + return makeHttpRequest(redirect, null, cookies); + } + + private static Map makeLoginQueryParam(String username, String password) { + // 这几个算是是写死了的,也能用 + // 但其实最好还是从首页动态获取某些关键字段("__VIEWSTATE") + // 这种办法等后面有时间了再实现 + Map queryParams = new HashMap<>(12); + queryParams.put("__EVENTTARGET", ""); + queryParams.put("__EVENTARGUMENT", ""); + queryParams.put("__VIEWSTATE", VIEW_STATE); + queryParams.put("__VIEWSTATEGENERATOR", "F42971E6"); + queryParams.put("hidUserID", ""); + queryParams.put("hidUserPassWord", ""); + queryParams.put("txtUserID", "请输入职工号或学号"); + queryParams.put("txtUsername", username); + queryParams.put("txtPassword", password); + queryParams.put("btnLogin.x", "83"); + queryParams.put("btnLogin.y", "29"); + queryParams.put("cbSaveInfo", "on"); + + return queryParams; + } + + private static final String VIEW_STATE = + "/wEPDwULLTEwNTgzMzY4NTgPZBYCZg9kFgICCQ9kFgJmD2QWBgINDw9kFgIeBVN0" + + "eWxlBZsBbGluZS1oZWlnaHQ6NThweDtwYWRkaW5nLWxlZnQ6NDVweDtwYWRkaW5n" + + "LXRvcDowcHg7d2lkdGg6MzE1cHg7aGVpZ2h0OjU0cHg7YmFja2dyb3VuZDp1cmwo" + + "Li4vLi4vUmVzb3VyY2UvQmFzaWNJbmZvL25ld0JHL+eUqOaIt+WQjS5wbmcpIG5v" + + "LXJlcGVhdCAwcHggMHB4OztkAg8PD2QWAh8ABZsBbGluZS1oZWlnaHQ6NThweDtw" + + "YWRkaW5nLWxlZnQ6NDVweDtwYWRkaW5nLXRvcDowcHg7d2lkdGg6MzE1cHg7aGVp" + + "Z2h0OjU0cHg7YmFja2dyb3VuZDp1cmwoLi4vLi4vUmVzb3VyY2UvQmFzaWNJbmZv" + + "L25ld0JHL+Wvhueggeepui5wbmcpIG5vLXJlcGVhdCAwcHggMHB4OztkAhkPDxYC" + + "HgRUZXh0BRvns7vnu5/orr/pl67kurrmrKHvvJozNjc2MDJkZBgBBR5fX0NvbnRy" + + "b2xzUmVxdWlyZVBvc3RCYWNrS2V5X18WBQULaW1nQnRuQ2xvc2UFCGxidGJTYXZl" + + "BQpsYnRuQ2FuY2VsBQhidG5Mb2dpbgUKY2JTYXZlSW5mb6K8UayJuWe2OSRVqLDo" + + "2J4wMKAT"; +} diff --git a/mywust-core/src/main/java/cn/linghang/mywust/core/service/auth/PhysicsLogin.java b/mywust-core/src/main/java/cn/linghang/mywust/core/service/auth/PhysicsLogin.java new file mode 100644 index 0000000..4520e6a --- /dev/null +++ b/mywust-core/src/main/java/cn/linghang/mywust/core/service/auth/PhysicsLogin.java @@ -0,0 +1,58 @@ +package cn.linghang.mywust.core.service.auth; + +import cn.linghang.mywust.core.exception.BasicException; +import cn.linghang.mywust.core.exception.PasswordWornException; +import cn.linghang.mywust.core.parser.physics.PhysicsIndexPageParser; +import cn.linghang.mywust.core.request.PhysicsSystemRequestFactory; +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 PhysicsLogin { + private static final Logger log = LoggerFactory.getLogger(PhysicsLogin.class); + + private final Requester requester; + + private static final PhysicsIndexPageParser physicsIndexPageParser = new PhysicsIndexPageParser(); + + public PhysicsLogin(Requester requester) { + this.requester = requester; + } + + public String getLoginCookie(String username, String password, RequestClientOption requestClientOption) throws IOException, BasicException { + // 直接登录,ASP.NET_SessionId其实在这步就能获取到,不需要再请求一遍首页获取 + HttpRequest loginCookieRequest = PhysicsSystemRequestFactory.loginCookiesRequest(username, password, null); + HttpResponse loginCookieResponse = requester.post(loginCookieRequest, requestClientOption); + if (loginCookieResponse.getStatusCode() != HttpResponse.HTTP_REDIRECT_302) { + throw new PasswordWornException(); + } + + String loginCookies = loginCookieResponse.getCookies(); + if (loginCookies == null) { + throw new PasswordWornException(); + } + + // 请求主页,获取实验选课系统的链接,顺便测试获取到的cookie是否有效,主页能否正常访问 + HttpRequest indexRequest = PhysicsSystemRequestFactory.mainIndexRequest(loginCookies); + HttpResponse indexResponse = requester.get(indexRequest, requestClientOption); + if (indexResponse.getStatusCode() != HttpResponse.HTTP_OK || indexResponse.getBody() == null) { + throw new BasicException(); + } + + // 登录实验选课系统,因为之前使用的登录接口和真正的物理实验选课系统是两个不一样的系统, + // 因此需要再次带上cookie请求一遍真正的物理实验选课系统,cookie才能在后面的接口中生效 + String indexHtml = new String(indexResponse.getBody()); + String redirect = physicsIndexPageParser.parse(indexHtml); + HttpRequest systemIndexRequest = PhysicsSystemRequestFactory.physicsSystemIndexRequest(redirect, loginCookies); + + requestClientOption.setFallowUrlRedirect(true); + requester.get(systemIndexRequest, requestClientOption); + + return loginCookies; + } +} \ No newline at end of file diff --git a/mywust-network/src/main/java/cn/linghang/mywust/network/HttpRequest.java b/mywust-network/src/main/java/cn/linghang/mywust/network/HttpRequest.java index c2941dc..eb7a169 100644 --- a/mywust-network/src/main/java/cn/linghang/mywust/network/HttpRequest.java +++ b/mywust-network/src/main/java/cn/linghang/mywust/network/HttpRequest.java @@ -38,6 +38,11 @@ public class HttpRequest { return this; } + public HttpRequest addHeaders(String key, String value) { + this.headers.put(key, value); + return this; + } + @Override public String toString() { final StringBuffer sb = new StringBuffer("HttpRequest{"); diff --git a/mywust-network/src/main/java/cn/linghang/mywust/network/HttpResponse.java b/mywust-network/src/main/java/cn/linghang/mywust/network/HttpResponse.java index 97c13b9..fcbbdc9 100644 --- a/mywust-network/src/main/java/cn/linghang/mywust/network/HttpResponse.java +++ b/mywust-network/src/main/java/cn/linghang/mywust/network/HttpResponse.java @@ -7,8 +7,14 @@ import java.util.Map; @Data public class HttpResponse { public static final int HTTP_OK = 200; + public static final int HTTP_RESOURCE_CREATED = 201; + + public static final int HTTP_REDIRECT_301 = 301; + public static final int HTTP_REDIRECT_302 = 302; + 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; diff --git a/mywust-test/src/test/java/WlsyLoginTest.java b/mywust-test/src/test/java/WlsyLoginTest.java new file mode 100644 index 0000000..e246516 --- /dev/null +++ b/mywust-test/src/test/java/WlsyLoginTest.java @@ -0,0 +1,44 @@ +import cn.linghang.mywust.core.exception.BasicException; +import cn.linghang.mywust.core.service.auth.PhysicsLogin; +import cn.linghang.mywust.network.RequestClientOption; +import cn.linghang.mywust.network.Requester; +import cn.linghang.mywust.network.okhttp.SimpleOkhttpRequester; + +import java.io.IOException; +import java.util.Scanner; + +public class WlsyLoginTest { + public static void main(String[] args) throws BasicException, IOException { + new WlsyLoginTest().run(); + } + + private void run() throws BasicException, IOException { + System.out.println("物理实验登陆测试"); + System.out.println("输入账号(学号)和密码,用“ ”(空格)分割"); + + Scanner scanner = new Scanner(System.in); + + String input = scanner.nextLine(); + + String username = input.split(" ")[0]; + String password = input.split(" ")[1]; + + System.out.println("账号:" + username); + System.out.println("密码:" + password); + + Requester requester = new SimpleOkhttpRequester(); + PhysicsLogin physicsLogin = new PhysicsLogin(requester); + + RequestClientOption option = RequestClientOption.DEFAULT_OPTION; + RequestClientOption.Proxy proxy = new RequestClientOption.Proxy(); + proxy.setAddress("127.0.0.1"); + proxy.setPort(8794); + option.setProxy(proxy); + + String cookies = physicsLogin.getLoginCookie(username, password, option); + + System.out.printf("获取到的cookies: %s \n", cookies); + +// System.out.printf("检查Cookies: %s", libraryLogin.checkCookie(cookies)); + } +} diff --git a/mywust-util/src/main/java/cn/linghang/mywust/util/StringUtil.java b/mywust-util/src/main/java/cn/linghang/mywust/util/StringUtil.java index e260a24..286a4ef 100644 --- a/mywust-util/src/main/java/cn/linghang/mywust/util/StringUtil.java +++ b/mywust-util/src/main/java/cn/linghang/mywust/util/StringUtil.java @@ -2,6 +2,8 @@ package cn.linghang.mywust.util; import com.google.common.base.Joiner; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -14,6 +16,9 @@ public class StringUtil { * @return 生成的表单请求字符串 */ public static String generateQueryString(Map queryParams) { + // 自动对value值进行url编码 + queryParams.forEach((k, v) -> queryParams.put(k, URLEncoder.encode(v, StandardCharsets.UTF_8))); + return Joiner.on('&') .useForNull("") .withKeyValueSeparator('=')