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; |
||||
|
||||
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.PasswordWornException; |
||||
import cn.linghang.mywust.core.service.auth.UnionLogin; |
||||
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 JwcLogin { |
||||
// 默认5秒超时
|
||||
private static final int DEFAULT_TIMEOUT = 5; |
||||
private static final Logger log = LoggerFactory.getLogger(JwcLogin.class); |
||||
|
||||
private final Requester requester; |
||||
|
||||
public JwcLogin(Requester requester) { |
||||
private final UnionLogin unionLogin; |
||||
|
||||
public JwcLogin(Requester requester, UnionLogin unionLogin) { |
||||
this.requester = requester; |
||||
this.unionLogin = unionLogin; |
||||
} |
||||
|
||||
public String getLoginCookie(String username, String password, RequestClientOption requestOption) throws IOException, BasicException { |
||||
// 获取ticket granting ticket(TGT)
|
||||
HttpRequest TGTRequest = AuthRequestGenerator.unionLoginTGTRequest(username, password, JwcApiUrl.JWC_SSO_SERVICE); |
||||
/** |
||||
* <p>旧版的登录方式,既相当于直接登录<a href="http://bkjx.wust.edu.cn">bkjx系统</a></p> |
||||
* <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"); |
||||
if (redirectAddress == null) { |
||||
throw new PasswordWornException(); |
||||
// 获取登录ticket
|
||||
String encoded = PasswordEncoder.legacyPassword(username, password, dataString); |
||||
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)
|
||||
HttpRequest serviceTicketRequest = AuthRequestGenerator.loginTicketRequest(JwcApiUrl.JWC_SSO_SERVICE); |
||||
// 使用跳转得到的链接获取cookies
|
||||
String sessionRedirect = ticketResponse.getHeaders().get("Location"); |
||||
if (sessionRedirect == null) { |
||||
throw new PasswordWornException(); |
||||
} |
||||
HttpRequest sessionRequest = AuthRequestFactory.Legacy.dataStringRequest(); |
||||
|
||||
HttpResponse serviceTicketResponse = |
||||
requester.post(new URL(redirectAddress.replace("http:", "https:")), serviceTicketRequest, requestOption); |
||||
HttpResponse sessionResponse = requester.get(new URL(sessionRedirect), sessionRequest, requestOption); |
||||
|
||||
byte[] serviceTicketResponseData = serviceTicketResponse.getBody(); |
||||
if (serviceTicketResponseData == null) { |
||||
System.err.println("获取服务st出错,serviceTicketResponseData == null"); |
||||
String cookies = sessionResponse.getCookies(); |
||||
if (roughCheckCookie(cookies)) { |
||||
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)
|
||||
HttpRequest sessionRequest = AuthRequestGenerator.sessionCookieRequest(); |
||||
HttpResponse sessionResponse = requester.get(new URL(String.format(JwcApiUrl.JWC_TICKET_API, serviceTicket)), sessionRequest, requestOption); |
||||
HttpRequest sessionRequest = AuthRequestFactory.sessionCookieRequest(); |
||||
HttpResponse sessionResponse = requester.get(new URL(String.format(Bkjx.BKJX_SESSION_COOKIE_API, serviceTicket)), sessionRequest, requestOption); |
||||
|
||||
String cookies = sessionResponse.getCookies(); |
||||
if (cookies == null || !cookies.contains("JSESSIONID") || !cookies.contains("SERVERID")) { |
||||
System.err.println("获取服务session cookie出错,关键Cookie缺失"); |
||||
System.err.println("Cookies: " + cookies); |
||||
if (roughCheckCookie(cookies)) { |
||||
throw new BasicException(); |
||||
} |
||||
|
||||
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