前言

这几天在写一个爬虫,当然http请求选择了OKHttp,但是在今天调试一个接口时候,却发生了一个小意外,很是尴尬,从中午找到晚上才发现原因。

先说一下他这个后台的登录过程,首先http://xxx/login.html地址是表单提交的地址,但是他做了一些加密操作,在进入到登录页面后,通过ajax请求http://xxx/getPublicKey,用来获取RSA的指数和模数,然后又是base64、hex等加密用户的密码,最后再进行提交。

下面是加密部分的一小段,摘这段的原因是我那会不知道他怎么算出最终的提交密码,然后去debug他的js,其他的算法java自带的类都可以实现,但是下面这段hex转b64,我了解到的知识,貌似想不到有直接的方法,所以根据他的js代码,翻译成java代码(这似乎难度不大)。

private static String hex2b64(String h) {
        int i;
        String b64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
        String b64pad = "=";
        int c;
        String ret = "";
        for (i = 0; i + 3 <= h.length(); i += 3) {
            c = parseInt(h.substring(i, i + 3), 16);
            ret += b64map.charAt(c >> 6) + "" + b64map.charAt(c & 63);
        }
        if (i + 1 == h.length()) {
            c = parseInt(h.substring(i, i + 1), 16);
            ret += b64map.charAt(c << 2);
        } else if (i + 2 == h.length()) {
            c = parseInt(h.substring(i, i + 2), 16);
            ret += b64map.charAt(c >> 2) + "" + b64map.charAt((c & 3) << 4);
        }
        while ((ret.length() & 3) > 0) ret += b64pad;
        return ret;
    }
 private static int parseInt(String h, int r) {
        return new BigInteger(h, 16).intValue();
    }

这里要说一下,这个网站在第一次请求时会返回Set-Cookie头,如果客户端存在Cookie,则不会。在获取指数和模数的时候,也会附带这个Cookie,如果没有Cookie,getPublicKey也会返回Set-Cookie头。

提交之后如果后台验证成功,则会发生重定向,状态码为302,响应头中的Location为要跳转的地址:http://xxx/index.html,这个地址就是用户登录后的主界面,由于OKHttp默认会发生跳转,则自动在去请求http://xxx/index.html,但是我在运行后,返回状态码为200,返回的结果却还是http://xxx/login.html的页面。

有点蒙了就,仔细检查了Cookie的设置,发现并没有问题。

最终在晚上,通过抓IDEA发起的http包,才发现了问题。

原来这个网站在登录后,要重定向到新的地址,但是此时响应头中多了Set-Cookie,这个新的Cookie去访问的http://xxx/index.html,也就是说这个新的Cookie才具有真正的登录状态。如果还是第一次的那个Cookie,那么index.html则会发生重定向,重定向到login.html。

Okhttp在这个过程中虽然指明了header,但是在重定向的时候他没自动把Set-Cookie作为新的Cookie去请求。所以出现了以上情景,如果当时抓包就好了,直到晚上才做。

所以,可以这样解决,设置为false后,不会自动重定向,后续就需要自己处理了。

static OkHttpClient client = new OkHttpClient.Builder()
            .connectTimeout(21, TimeUnit.DAYS)
            .followRedirects(false)
            .readTimeout(21, TimeUnit.DAYS)
            .build();

另外可能还要复习期末,有点着急了,没有想到。


你也许会说我无情无义吧