前言
网上有很多模拟登陆 LeetCode 的教程,但是基本都是使用 Python 来实现的。作为一个 Java 语言爱好者,因此想用 Java 来实现下。在实现的过程中,也遇到了一些坑点,故在此作为记录。
过程
根据浏览器F12分析登陆页面
从上图可以看出,LeetCode 生成一个 token ,然后在登陆的时候带上这个信息,因此我们模拟登陆的大致思路:首先获取得到 cookie(包含有token),然后在登陆的时候带上这个 cookie 信息,完成 LeetCode 的验证机制,进行模拟登陆。
但是直接进行模拟带上 login(用户名)、password(密码)、csrfmiddlewaretoken(验证信息)是失败的,提示 Forbidden。思考无果,另开思路。
用 fiddler 进行抓包,未登陆状态,数据如下图(也可用浏览器F12来进行分析):
登陆状态,数据如下图,从图中,我们可以发现,其 Content-Type 字段与我们之前常见的值不一样,其是 multipart/form-data 格式,因此我们在模拟登陆,要将其考虑进来。
创建一个 multipart/form-data 的媒体格式,然后生成我们要的请求体,至于我们要的是哪种请求体,其格式可以从 fiddler 抓包的结果获悉。
在SyntaxView中具体的详情如下:
值得注意的是 Content-Type 中的boundary只有四个“-”,而在请求体中有六个“-”,之前因为忽略了这个,一直被拒绝访问(挺坑爹的
public static final String boundary = "----WebKitFormBoundaryhG2vKxp7y2GAwhPX";public static final MediaType MULTIPART = MediaType.parse("multipart/form-data; boundary=" + boundary);String form_data = "--" + boundary + "\r\n" ???????????????+ "Content-Disposition: form-data; name=\"login\"" + "\r\n\r\n" ???????????????+ usrname + "\r\n" ???????????????+ "--" + boundary + "\r\n" ???????????????+ "Content-Disposition: form-data; name=\"password\"" + "\r\n\r\n" ???????????????+ passwd ???????????????+ "--" + boundary + "\r\n" ???????????????+ "Content-Disposition: form-data; name=\"csrfmiddlewaretoken\"" + "\r\n\r\n" ???????????????+ csrftoken + "\r\n" ???????????????+ "--" + boundary + "--"; ???????RequestBody requestBody = RequestBody.create(MULTIPART,form_data);
结果
将其返回的报文打印出来,得到如下信息则表示模拟登陆成功
从 fiddler 的抓包结果中也可以证实这点
代码
package LeetCodeLogin;import okhttp3.*;import org.jsoup.Connection;import org.jsoup.Jsoup;import java.io.IOException;import java.util.*;import static java.lang.System.out;public class Login { ???public static final String boundary = "----WebKitFormBoundaryhG2vKxp7y2GAwhPX"; ???public static final MediaType MULTIPART = MediaType.parse("multipart/form-data; boundary=" + boundary); ???public static void main(String... args) throws IOException { ???????Scanner scanner = new Scanner(System.in); ???????String url = "https://leetcode.com/accounts/login/"; ???????String usrname = "xxx"; ???????String passwd = "xxx"; ???????Connection.Response response1 = Jsoup.connect(url) ???????????????.method(Connection.Method.GET) ???????????????.execute(); ???????String csrftoken = response1.cookie("csrftoken"); ???????String __cfduid = response1.cookie("__cfduid"); ???????out.println("csrftoken = " + csrftoken); ???????out.println("__cfduid = " + __cfduid ); ???????OkHttpClient client = new OkHttpClient().newBuilder() ???????????????.followRedirects(false) ???????????????.followSslRedirects(false) ???????????????.build(); ???????String form_data = "--" + boundary + "\r\n" ???????????????+ "Content-Disposition: form-data; name=\"login\"" + "\r\n\r\n" ???????????????+ usrname + "\r\n" ???????????????+ "--" + boundary + "\r\n" ???????????????+ "Content-Disposition: form-data; name=\"password\"" + "\r\n\r\n" ???????????????+ passwd ???????????????+ "--" + boundary + "\r\n" ???????????????+ "Content-Disposition: form-data; name=\"csrfmiddlewaretoken\"" + "\r\n\r\n" ???????????????+ csrftoken + "\r\n" ???????????????+ "--" + boundary + "--"; ???????RequestBody requestBody = RequestBody.create(MULTIPART,form_data); ???????Request request = new Request.Builder() ???????????????.addHeader("Content-Type", "multipart/form-data; boundary=" + boundary) ???????????????.addHeader("Connection","keep-alive") ???????????????.addHeader("Accept","*/*") ???????????????.addHeader("Origin","https://leetcode.com") ???????????????.addHeader("Referer",url) ???????????????.addHeader("Cookie","__cfduid=" + __cfduid + ";" + "csrftoken=" + csrftoken) ???????????????.post(requestBody) ???????????????.url(url) ???????????????.build(); ???????Response response = client.newCall(request).execute(); ???????out.println(response.message()); ???????out.println(response.headers()); ???????out.println(response.body().string()); ???}}
需要注意的是,在上述代码中,我们通过下述代码禁止了重定向,来自己处理重定向请求,可参考使用OkHttp进行重定向拦截处理,若是没有进行重定向拦截,也会使得模拟登陆失败。
???.followRedirects(false) ???.followSslRedirects(false)
写在最后
本次模拟登陆,虽然代码很简单,但是确实也经历了一些波折,比对过 Python 和 Js 写的模拟登陆的代码,用 Java 来进行模拟似乎多了一些琐碎的细节,对于具体的为何 Python 和 Js 能如此简介的处理的原理还在琢磨中。此次,也得到了朋友 faberry 的帮助,在一些地方给了意见。
使用OkHttp模拟登陆LeetCode
原文地址:https://www.cnblogs.com/ZhaoxiCheung/p/9302510.html