使用OkHttp模拟登陆LeetCode
前言
网上有很多模拟登陆 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=\"csrfmiddlewaretoken\"" + "\r\n\r\n"
                + csrftoken + "\r\n"
                + "--" + 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 + "\r\n"
                + "--" + boundary + "\r\n"
                + "Content-Disposition: form-data; name=\"next\"" + "\r\n\r\n"
                + "/problems" + "\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=\"csrfmiddlewaretoken\"" + "\r\n\r\n"
                + csrftoken + "\r\n"
                + "--" + 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 + "\r\n"
                + "--" + boundary + "\r\n"
                + "Content-Disposition: form-data; name=\"next\"" + "\r\n\r\n"
                + "/problems" + "\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的更多相关文章
- 使用OKHttp模拟登陆知乎,兼谈OKHttp中Cookie的使用!
		
本文主要是想和大家探讨技术,让大家学会Cookie的使用,切勿做违法之事! 很多Android初学者在刚开始学习的时候,或多或少都想自己搞个应用出来,把自己学的十八般武艺全都用在这个APP上,其实这个 ...
 - Python 爬虫模拟登陆知乎
		
在之前写过一篇使用python爬虫爬取电影天堂资源的博客,重点是如何解析页面和提高爬虫的效率.由于电影天堂上的资源获取权限是所有人都一样的,所以不需要进行登录验证操作,写完那篇文章后又花了些时间研究了 ...
 - Python模拟登陆新浪微博
		
上篇介绍了新浪微博的登陆过程,这节使用Python编写一个模拟登陆的程序.讲解与程序如下: 1.主函数(WeiboMain.py): import urllib2 import cookielib i ...
 - PHP 之 CURL 模拟登陆并获取数据
		
1.CURL模拟登陆的流程和步骤 2.tempnam 创建一个临时文件 3.使用CURL模拟登陆到PHP100论坛 <?php $cookie_file = tempnam('./temp',' ...
 - NetworkComms V3   模拟登陆
		
演示NetworkComms V3的用法 例子很简单 界面如下: 服务器端代码: 开始监听: //服务器开始监听客户端的请求 Connection.StartListening(ConnectionT ...
 - pytho简单爬虫_模拟登陆西电流量查询_实现一键查询自己的校园网流量
		
闲来无事,由于校园内网络是限流量的,查询流量很是频繁,于是萌生了写一个本地脚本进行一键查询自己的剩余流量. 整个部分可以分为三个过程进行: 对登陆时http协议进行分析 利用python进行相关的模拟 ...
 - python模拟登陆知乎并爬取数据
		
一些废话 看了一眼上一篇日志的时间 已然是5个月前的事情了 不禁感叹光阴荏苒其实就是我懒 几周前心血来潮想到用爬虫爬些东西 于是先后先重写了以前写过的求绩点代码 爬了草榴贴图,妹子图网,后来想爬婚恋网 ...
 - php模拟登陆的两种实现方法分析
		
php模拟登陆的实现方法分析 本文实例分析了php模拟登陆的实现方法.分享给大家供大家参考.具体分析如下: php模拟登陆的实现方法,这里分别列举两种方法实现模拟登陆人人网.具体实例代码如下: 1)使 ...
 - python 模拟登陆,请求包含cookie信息
		
需求: 1.通过GET方法,访问URL地址一,传入cookie参数 2.根据地址一返回的uuid,通过POST方法,传入cooki参数 实现思路: 1.理解http的GET和POST差别 (网上有很多 ...
 
随机推荐
- lmhosts - samba的NetBIOS主机列表文件
			
lmhosts是一个samba的NetBIOS名字到IP地址映射文件. 描述 此文件是samba套件的一部分. lmhosts是一个samba的NetBIOS名字到IP地址映射文件.它与/etc/ho ...
 - Mongo--04  Mongo分片集群
			
目录 一.分片的概念 二. 分片工作原理 三.IP端口目录规划 1.IP端口规划 2.目录规划 四.分片集群搭建副本集步骤 1.安装软件 2.创建目录 3.创建配置文件 4.优化警告 5.启动服务 6 ...
 - PAT Basic 1027 打印沙漏 (20 分)
			
本题要求你写个程序把给定的符号打印成沙漏的形状.例如给定17个“*”,要求按下列格式打印 ***** *** * *** ***** 所谓“沙漏形状”,是指每行输出奇数个符号:各行符号中心对齐:相邻两 ...
 - 2019长安大学ACM校赛网络同步赛 M	LCM (数论)
			
链接:https://ac.nowcoder.com/acm/contest/897/M来源:牛客网 LCM 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 32768K,其他语言65 ...
 - kettle中使用mysql的tinyint 类型到slqserver的tinyint类型
			
各个数据库之间的类型 定义还是有差别的 一下是我在工作中遇到的一个很奇葩的问题 mysql 中的 tinyint 类型 插入到sqlserver 的tinyint 类型 插入到 sqlserver的 ...
 - puppet使用rsync模块
			
puppet使用rsync模块同步目录和文件 环境说明: OS : CentOS5.4 i686puppet版本: ...
 - 解决使用脚手架构建项目缺失node_modules文件夹文件问题
			
昨晚,在教我前端交流群里面的朋友搭建vue开发环境和构建vue项目的时候发现我自己之前能正常构建vue项目的现在却不行了,排查之下发现 通过脚手架构建项目的时候项目缺失了node_modules文件夹 ...
 - spring boot mapper层传参数是用main的arg0(第一个参数),arg1(第二个参数)
			
spring boot mapper层传参数是用main的arg0(第一个参数),arg1(第二个参数) 大于三个参数,用map传递 public interface FrontMapper{ //= ...
 - POJ 1252 Euro Efficiency ( 完全背包变形 && 物品重量为负 )
			
题意 : 给出 6 枚硬币的面值,然后要求求出对于 1~100 要用所给硬币凑出这 100 个面值且要求所用的硬币数都是最少的,问你最后使用硬币的平均个数以及对于单个面值所用硬币的最大数. 分析 : ...
 - ASCII,Unicode,UTF-8
			
ASCII ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,主要用于显示现代英 ...