parent
4bb358cc05
commit
6f35b17acc
@ -0,0 +1,21 @@ |
|||||||
|
package cn.linghang.mywust.core.api; |
||||||
|
|
||||||
|
import lombok.Getter; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p>Bkjx(本科教学)系统对应的API常量列表(拼音咱就别吐槽了吧...)</p> |
||||||
|
* <p>其实是本科生用的教务处系统</p> |
||||||
|
*/ |
||||||
|
@Getter |
||||||
|
public class Bkjx { |
||||||
|
public static final String BKJX_SSO_SERVICE = "http://bkjx.wust.edu.cn/jsxsd/sso.jsp"; |
||||||
|
public static final String BKJX_SESSION_COOKIE_API = "http://bkjx.wust.edu.cn/jsxsd/sso.jsp?ticket=%s"; |
||||||
|
|
||||||
|
public static final String BKJX_TEST_API = "http://bkjx.wust.edu.cn/jsxsd/xxwcqk/xxwcqk_idxOnzh.do"; |
||||||
|
|
||||||
|
public static class Legacy { |
||||||
|
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_SESSION_COOKIE_API = "http://bkjx.wust.edu.cn/Logon.do?method=logon"; |
||||||
|
} |
||||||
|
} |
@ -1,15 +0,0 @@ |
|||||||
package cn.linghang.mywust.core.api; |
|
||||||
|
|
||||||
import lombok.Getter; |
|
||||||
|
|
||||||
@Getter |
|
||||||
public class JwcApiUrl { |
|
||||||
/** |
|
||||||
* 统一认证登录验证的api地址,请求之后可进一步获取ticket |
|
||||||
*/ |
|
||||||
public static final String UNION_AUTH_API = "https://auth.wust.edu.cn/lyuapServer/v1/tickets"; |
|
||||||
|
|
||||||
public static final String JWC_SSO_SERVICE = "http://bkjx.wust.edu.cn/jsxsd/sso.jsp"; |
|
||||||
public static final String JWC_TICKET_API = "http://bkjx.wust.edu.cn/jsxsd/sso.jsp?ticket=%s"; |
|
||||||
|
|
||||||
} |
|
@ -0,0 +1,8 @@ |
|||||||
|
package cn.linghang.mywust.core.api; |
||||||
|
|
||||||
|
public class UnionAuth { |
||||||
|
/** |
||||||
|
* 统一认证登录验证的api地址,请求之后可进一步获取service ticket来对具体的服务进行登录获取Cookies |
||||||
|
*/ |
||||||
|
public static final String UNION_AUTH_API = "https://auth.wust.edu.cn/lyuapServer/v1/tickets"; |
||||||
|
} |
@ -0,0 +1,58 @@ |
|||||||
|
package cn.linghang.mywust.core.service.auth; |
||||||
|
|
||||||
|
import cn.linghang.mywust.core.api.UnionAuth; |
||||||
|
import cn.linghang.mywust.core.exception.BasicException; |
||||||
|
import cn.linghang.mywust.core.exception.PasswordWornException; |
||||||
|
import cn.linghang.mywust.core.service.undergraduate.AuthRequestFactory; |
||||||
|
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 cn.linghang.mywust.util.PasswordEncoder; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.net.URL; |
||||||
|
|
||||||
|
/** |
||||||
|
* 统一认证登录 |
||||||
|
*/ |
||||||
|
public class UnionLogin { |
||||||
|
Logger log = LoggerFactory.getLogger(UnionLogin.class); |
||||||
|
|
||||||
|
private final Requester requester; |
||||||
|
|
||||||
|
public UnionLogin(Requester requester) { |
||||||
|
this.requester = requester; |
||||||
|
} |
||||||
|
|
||||||
|
public String getServiceTicket(String username, String password, String serviceUrl, RequestClientOption requestOption) throws IOException, BasicException { |
||||||
|
String encodedPassword = PasswordEncoder.encodePassword(password); |
||||||
|
|
||||||
|
// 获取ticket granting ticket(TGT),以获取ServiceTicket
|
||||||
|
HttpRequest TGTRequest = AuthRequestFactory.unionLoginTGTRequest(username, encodedPassword, serviceUrl); |
||||||
|
|
||||||
|
HttpResponse unionAuthResponse = requester.post(new URL(UnionAuth.UNION_AUTH_API), TGTRequest, requestOption); |
||||||
|
|
||||||
|
String redirectAddress = unionAuthResponse.getHeaders().get("Location"); |
||||||
|
if (redirectAddress == null) { |
||||||
|
throw new PasswordWornException(); |
||||||
|
} |
||||||
|
|
||||||
|
// 获取服务ticket(service ticket,ST)
|
||||||
|
HttpRequest serviceTicketRequest = AuthRequestFactory.loginTicketRequest(serviceUrl); |
||||||
|
|
||||||
|
HttpResponse serviceTicketResponse = |
||||||
|
requester.post(new URL(redirectAddress.replace("http:", "https:")), serviceTicketRequest, requestOption); |
||||||
|
|
||||||
|
byte[] serviceTicketResponseData = serviceTicketResponse.getBody(); |
||||||
|
if (serviceTicketResponseData == null) { |
||||||
|
log.warn("获取服务st出错,serviceTicketResponseData == null"); |
||||||
|
|
||||||
|
throw new BasicException(); |
||||||
|
} |
||||||
|
|
||||||
|
return new String(serviceTicketResponseData); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,68 @@ |
|||||||
|
package cn.linghang.mywust.core.service.undergraduate; |
||||||
|
|
||||||
|
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 AuthRequestFactory { |
||||||
|
public static HttpRequest unionLoginTGTRequest(String username, String password, String service) { |
||||||
|
Map<String, String> requestForm = new HashMap<>(4); |
||||||
|
requestForm.put("username", username); |
||||||
|
requestForm.put("password", password); |
||||||
|
requestForm.put("service", service); |
||||||
|
requestForm.put("loginType", ""); |
||||||
|
|
||||||
|
String queryString = StringUtil.generateQueryString(requestForm); |
||||||
|
|
||||||
|
HttpRequest request = new HttpRequest(); |
||||||
|
request.setData(queryString.getBytes(StandardCharsets.UTF_8)); |
||||||
|
|
||||||
|
return request; |
||||||
|
} |
||||||
|
|
||||||
|
public static HttpRequest loginTicketRequest(String service) { |
||||||
|
Map<String, String> requestForm = new HashMap<>(1); |
||||||
|
requestForm.put("service", service); |
||||||
|
|
||||||
|
String queryString = StringUtil.generateQueryString(requestForm); |
||||||
|
|
||||||
|
HttpRequest request = new HttpRequest(); |
||||||
|
request.setData(queryString.getBytes(StandardCharsets.UTF_8)); |
||||||
|
|
||||||
|
return request; |
||||||
|
} |
||||||
|
|
||||||
|
public static HttpRequest sessionCookieRequest() { |
||||||
|
return DEFAULT_HTTP_REQUEST; |
||||||
|
} |
||||||
|
|
||||||
|
private static final HttpRequest DEFAULT_HTTP_REQUEST = new HttpRequest(); |
||||||
|
|
||||||
|
public static class Legacy { |
||||||
|
public static HttpRequest dataStringRequest() { |
||||||
|
return DEFAULT_HTTP_REQUEST; |
||||||
|
} |
||||||
|
|
||||||
|
public static HttpRequest ticketRedirectRequest(String encode) { |
||||||
|
Map<String, String> queryParams = new HashMap<>(); |
||||||
|
queryParams.put("userAccount", ""); |
||||||
|
queryParams.put("userPassword", ""); |
||||||
|
queryParams.put("encoded", encode); |
||||||
|
|
||||||
|
String queryString = StringUtil.generateQueryString(queryParams); |
||||||
|
|
||||||
|
Map<String, String> extendHeaders = new HashMap<>(); |
||||||
|
extendHeaders.put("Referer", "http://bkjx.wust.edu.cn/"); |
||||||
|
extendHeaders.put("Origin", "http://bkjx.wust.edu.cn"); |
||||||
|
|
||||||
|
HttpRequest httpRequest = new HttpRequest(); |
||||||
|
httpRequest.setHeaders(extendHeaders); |
||||||
|
httpRequest.setData(queryString.getBytes(StandardCharsets.UTF_8)); |
||||||
|
|
||||||
|
return httpRequest; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -1,41 +0,0 @@ |
|||||||
package cn.linghang.mywust.core.service.undergraduate; |
|
||||||
|
|
||||||
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 AuthRequestGenerator { |
|
||||||
public static HttpRequest unionLoginTGTRequest(String username, String password, String service) { |
|
||||||
Map<String, String> requestForm = new HashMap<>(); |
|
||||||
requestForm.put("username", username); |
|
||||||
requestForm.put("password", password); |
|
||||||
requestForm.put("service", service); |
|
||||||
requestForm.put("loginType", ""); |
|
||||||
|
|
||||||
String queryString = StringUtil.generateQueryString(requestForm); |
|
||||||
|
|
||||||
HttpRequest request = new HttpRequest(); |
|
||||||
request.setData(queryString.getBytes(StandardCharsets.UTF_8)); |
|
||||||
|
|
||||||
return request; |
|
||||||
} |
|
||||||
|
|
||||||
public static HttpRequest loginTicketRequest(String service) { |
|
||||||
Map<String, String> requestForm = new HashMap<>(); |
|
||||||
requestForm.put("service", service); |
|
||||||
|
|
||||||
String queryString = StringUtil.generateQueryString(requestForm); |
|
||||||
|
|
||||||
HttpRequest request = new HttpRequest(); |
|
||||||
request.setData(queryString.getBytes(StandardCharsets.UTF_8)); |
|
||||||
|
|
||||||
return request; |
|
||||||
} |
|
||||||
|
|
||||||
public static HttpRequest sessionCookieRequest() { |
|
||||||
return new HttpRequest(); |
|
||||||
} |
|
||||||
} |
|
@ -1,65 +1,113 @@ |
|||||||
package cn.linghang.mywust.core.service.undergraduate; |
package cn.linghang.mywust.core.service.undergraduate; |
||||||
|
|
||||||
import cn.linghang.mywust.core.api.JwcApiUrl; |
import cn.linghang.mywust.core.api.Bkjx; |
||||||
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.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; |
||||||
import cn.linghang.mywust.network.Requester; |
import cn.linghang.mywust.network.Requester; |
||||||
|
import cn.linghang.mywust.util.PasswordEncoder; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
import java.io.IOException; |
import java.io.IOException; |
||||||
import java.net.URL; |
import java.net.URL; |
||||||
|
|
||||||
public class JwcLogin { |
public class JwcLogin { |
||||||
// 默认5秒超时
|
private static final Logger log = LoggerFactory.getLogger(JwcLogin.class); |
||||||
private static final int DEFAULT_TIMEOUT = 5; |
|
||||||
|
|
||||||
private final Requester requester; |
private final Requester requester; |
||||||
|
|
||||||
public JwcLogin(Requester requester) { |
private final UnionLogin unionLogin; |
||||||
|
|
||||||
|
public JwcLogin(Requester requester, UnionLogin unionLogin) { |
||||||
this.requester = requester; |
this.requester = requester; |
||||||
|
this.unionLogin = unionLogin; |
||||||
} |
} |
||||||
|
|
||||||
public String getLoginCookie(String username, String password, RequestClientOption requestOption) throws IOException, BasicException { |
/** |
||||||
// 获取ticket granting ticket(TGT)
|
* <p>旧版的登录方式,既相当于直接登录<a href="http://bkjx.wust.edu.cn">bkjx系统</a></p> |
||||||
HttpRequest TGTRequest = AuthRequestGenerator.unionLoginTGTRequest(username, password, JwcApiUrl.JWC_SSO_SERVICE); |
* <p>注意,这种登录方式的密码和新版可能是不一样的</p> |
||||||
|
* <p>不过不论使用哪种登录方式获取到的cookie都是可用的</p> |
||||||
|
* |
||||||
|
* @return 获取到的Cookies |
||||||
|
*/ |
||||||
|
public String getLoginCookieLegacy(String username, String password, RequestClientOption requestOption) throws IOException, BasicException { |
||||||
|
// 获取某段神秘的dataStr(反正官网代码是这么叫的)
|
||||||
|
HttpRequest dataStringRequest = AuthRequestFactory.Legacy.dataStringRequest(); |
||||||
|
HttpResponse dataStringResponse = requester.post(new URL(Bkjx.Legacy.BKJX_DATA_STRING_API), dataStringRequest, requestOption); |
||||||
|
if (dataStringResponse.getBody() == null) { |
||||||
|
throw new BasicException(); |
||||||
|
} |
||||||
|
|
||||||
HttpResponse unionAuthResponse = requester.post(new URL(JwcApiUrl.UNION_AUTH_API), TGTRequest, requestOption); |
String dataString = new String(dataStringResponse.getBody()); |
||||||
|
|
||||||
String redirectAddress = unionAuthResponse.getHeaders().get("Location"); |
// 获取登录ticket
|
||||||
if (redirectAddress == null) { |
String encoded = PasswordEncoder.legacyPassword(username, password, dataString); |
||||||
throw new PasswordWornException(); |
HttpRequest ticketRequest = AuthRequestFactory.Legacy.ticketRedirectRequest(encoded); |
||||||
|
ticketRequest.setCookies(dataStringResponse.getCookies()); |
||||||
|
HttpResponse ticketResponse = requester.post(new URL(Bkjx.Legacy.BKJX_SESSION_COOKIE_API), ticketRequest, requestOption); |
||||||
|
if (ticketResponse.getBody() == null) { |
||||||
|
throw new BasicException(); |
||||||
} |
} |
||||||
|
|
||||||
// 获取服务ticket(service ticket,st)
|
// 使用跳转得到的链接获取cookies
|
||||||
HttpRequest serviceTicketRequest = AuthRequestGenerator.loginTicketRequest(JwcApiUrl.JWC_SSO_SERVICE); |
String sessionRedirect = ticketResponse.getHeaders().get("Location"); |
||||||
|
if (sessionRedirect == null) { |
||||||
|
throw new PasswordWornException(); |
||||||
|
} |
||||||
|
HttpRequest sessionRequest = AuthRequestFactory.Legacy.dataStringRequest(); |
||||||
|
|
||||||
HttpResponse serviceTicketResponse = |
HttpResponse sessionResponse = requester.get(new URL(sessionRedirect), sessionRequest, requestOption); |
||||||
requester.post(new URL(redirectAddress.replace("http:", "https:")), serviceTicketRequest, requestOption); |
|
||||||
|
|
||||||
byte[] serviceTicketResponseData = serviceTicketResponse.getBody(); |
String cookies = sessionResponse.getCookies(); |
||||||
if (serviceTicketResponseData == null) { |
if (roughCheckCookie(cookies)) { |
||||||
System.err.println("获取服务st出错,serviceTicketResponseData == null"); |
|
||||||
throw new BasicException(); |
throw new BasicException(); |
||||||
} |
} |
||||||
String serviceTicket = new String(serviceTicketResponseData); |
|
||||||
|
return cookies; |
||||||
|
} |
||||||
|
|
||||||
|
public String getLoginCookie(String username, String password, RequestClientOption requestOption) throws IOException, BasicException { |
||||||
|
String serviceTicket = unionLogin.getServiceTicket(username, password, Bkjx.BKJX_SSO_SERVICE, requestOption); |
||||||
|
|
||||||
// 获取登录cookie(session)
|
// 获取登录cookie(session)
|
||||||
HttpRequest sessionRequest = AuthRequestGenerator.sessionCookieRequest(); |
HttpRequest sessionRequest = AuthRequestFactory.sessionCookieRequest(); |
||||||
HttpResponse sessionResponse = requester.get(new URL(String.format(JwcApiUrl.JWC_TICKET_API, serviceTicket)), sessionRequest, requestOption); |
HttpResponse sessionResponse = requester.get(new URL(String.format(Bkjx.BKJX_SESSION_COOKIE_API, serviceTicket)), sessionRequest, requestOption); |
||||||
|
|
||||||
String cookies = sessionResponse.getCookies(); |
String cookies = sessionResponse.getCookies(); |
||||||
if (cookies == null || !cookies.contains("JSESSIONID") || !cookies.contains("SERVERID")) { |
if (roughCheckCookie(cookies)) { |
||||||
System.err.println("获取服务session cookie出错,关键Cookie缺失"); |
|
||||||
System.err.println("Cookies: " + cookies); |
|
||||||
throw new BasicException(); |
throw new BasicException(); |
||||||
} |
} |
||||||
|
|
||||||
return cookies; |
return cookies; |
||||||
} |
} |
||||||
|
|
||||||
private void getLoginGTG(String username, String password) { |
private boolean roughCheckCookie(String cookies) throws BasicException { |
||||||
|
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%2Fxxwcqk%2Fxxwcqk_idxOnzh.do'" + |
||||||
|
"</script>").length(); |
||||||
|
|
||||||
|
|
||||||
|
public boolean checkCookies(String cookies) throws IOException { |
||||||
|
RequestClientOption option = new RequestClientOption(); |
||||||
|
option.setTimeout(5); |
||||||
|
option.setProxy(null); |
||||||
|
option.setFallowUrlRedirect(false); |
||||||
|
|
||||||
|
HttpRequest testRequest = AuthRequestFactory.sessionCookieRequest(); |
||||||
|
testRequest.setCookies(cookies); |
||||||
|
HttpResponse testResponse = requester.get(new URL(Bkjx.BKJX_TEST_API), testRequest, option); |
||||||
|
|
||||||
|
// 判断响应长度是否为178个字,登录跳转响应长度
|
||||||
|
// 这种办法虽然在极端情况下可能会出错(而且还挺蠢的),但是是最快的办法中比较准确的了
|
||||||
|
return testResponse.getBody().length != COOKIES_ERROR_RESPONSE_LENGTH; |
||||||
} |
} |
||||||
|
|
||||||
} |
} |
||||||
|
@ -0,0 +1,22 @@ |
|||||||
|
package cn.linghang.mywust.network.okhttp; |
||||||
|
|
||||||
|
import okhttp3.Interceptor; |
||||||
|
import okhttp3.Response; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
|
||||||
|
public class RedirectInterceptor implements Interceptor { |
||||||
|
|
||||||
|
@Override |
||||||
|
public Response intercept(Chain chain) throws IOException { |
||||||
|
okhttp3.Request request = chain.request(); |
||||||
|
Response response = chain.proceed(request); |
||||||
|
|
||||||
|
String location = response.headers().get("Location"); |
||||||
|
if (location != null) { |
||||||
|
System.out.println(("Request redirected to:" + location)); |
||||||
|
} |
||||||
|
|
||||||
|
return response; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,41 @@ |
|||||||
|
import cn.linghang.mywust.core.exception.BasicException; |
||||||
|
import cn.linghang.mywust.core.service.auth.UnionLogin; |
||||||
|
import cn.linghang.mywust.core.service.undergraduate.JwcLogin; |
||||||
|
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 JwcLegacyLoginTest { |
||||||
|
public static void main(String[] args) throws BasicException, IOException { |
||||||
|
new JwcLegacyLoginTest().run(); |
||||||
|
} |
||||||
|
|
||||||
|
private void run() throws BasicException, IOException { |
||||||
|
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(); |
||||||
|
JwcLogin jwcLogin = new JwcLogin(requester, new UnionLogin(requester)); |
||||||
|
|
||||||
|
RequestClientOption option = new RequestClientOption(); |
||||||
|
option.setTimeout(5); |
||||||
|
option.setProxy(null); |
||||||
|
option.setFallowUrlRedirect(false); |
||||||
|
|
||||||
|
String cookies = jwcLogin.getLoginCookieLegacy(username, password, option); |
||||||
|
|
||||||
|
System.out.println(cookies); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue