• 使用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 + "
    "
                    + "Content-Disposition: form-data; name="csrfmiddlewaretoken"" + "
    
    "
                    + csrftoken + "
    "
                    + "--" + boundary + "
    "
                    + "Content-Disposition: form-data; name="login"" + "
    
    "
                    + usrname + "
    "
                    + "--" + boundary + "
    "
                    + "Content-Disposition: form-data; name="password"" + "
    
    "
                    + passwd + "
    "
                    + "--" + boundary + "
    "
                    + "Content-Disposition: form-data; name="next"" + "
    
    "
                    + "/problems" + "
    "
                    + "--" + 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 + "
    "
                    + "Content-Disposition: form-data; name="csrfmiddlewaretoken"" + "
    
    "
                    + csrftoken + "
    "
                    + "--" + boundary + "
    "
                    + "Content-Disposition: form-data; name="login"" + "
    
    "
                    + usrname + "
    "
                    + "--" + boundary + "
    "
                    + "Content-Disposition: form-data; name="password"" + "
    
    "
                    + passwd + "
    "
                    + "--" + boundary + "
    "
                    + "Content-Disposition: form-data; name="next"" + "
    
    "
                    + "/problems" + "
    "
                    + "--" + 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 的帮助,在一些地方给了意见。

  • 相关阅读:
    Android Tween和Frame 动画
    Android Tween和Frame 动画
    android:descendantFocusability用法简析
    android:descendantFocusability用法简析
    CodeForces 686A Free Ice Cream (水题模拟)
    UVa 11136 Hoax or what (STL)
    UVa 1616 Caravan Robbers (二分+贪心)
    UVa 10570 Meeting with Aliens (暴力)
    UVa 1153 Keep the Customer Satisfied (贪心+优先队列)
    HDU 2044 一只小蜜蜂...(递推,Fibonacci)
  • 原文地址:https://www.cnblogs.com/ZhaoxiCheung/p/9302510.html
Copyright © 2020-2023  润新知