Hutool HttpRequest 首次请求正常 第二次被系统拦截

Hutool HttpRequest 首次请求正常 第二次被系统拦截

功能描述

需要请求第三方某个接口,获取接口中的数据。

异常现象

使用main 方法 通过Hutool 工具类发出请求,获取数据信息时,发现第一次请求接口可以正常获取数据项,但是循环遍历请求接口时,除首次请求外,其他请求都被第三方接口拦截,提示需要登录。

错误代码

java 复制代码
String url = "http://xxx/kk/hh/f?page=1&limit=15";
	for(int i = 0;i<10;i++){
        HttpRequest http = HttpRequest.get(url);
        http.header("Accept", "application/json, text/javascript, */*; q=0.01")
                .header("Accept-Encoding", "gzip, deflate, br")
                .header("Connection", "keep-alive")
                .header("Cookie", "kkid=sdf456sadf45dsf6ds4f; Token=15sd4f5ds6ads54f5sdf45dsf")
                HttpResponse tt = http.execute();
            	String body = tt.body();
     }

异常排查

1.由于每次的首次请求都可以成功,排除接口无法请求或做了防重复请求之类的限制。

2.使用 java.net.HttpURLConnection 请求可以正常使用,再次排除接口问题,同时锁定可能是Hutool的问题

问题跟踪

1.跟踪Hutool HttpRequest 的 execute() -> doExecute();

java 复制代码
	// 最终执行到这个方法
    private HttpResponse doExecute(boolean isAsync, Chain<HttpRequest> requestInterceptors, Chain<HttpResponse> responseInterceptors) {
    	// 请求前的拦截方法,可以实现该方法,对请求拦截后处理
        if (null != requestInterceptors) {
            Iterator var4 = requestInterceptors.iterator();

            while(var4.hasNext()) {
                HttpInterceptor<HttpRequest> interceptor = (HttpInterceptor)var4.next();
                interceptor.process(this);
            }
        }
		// 获取请求参数
        this.urlWithParamIfGet();
        // 初始化连接,此次问题出现在改方法中
        this.initConnection();
        // 发送请求
        this.send();
        //接受响应信息
        HttpResponse httpResponse = this.sendRedirectIfPossible(isAsync);
        if (null == httpResponse) {
            httpResponse = new HttpResponse(this.httpConnection, this.config, this.charset, isAsync, this.isIgnoreResponseBody());
        }

	// 请求响应体拦截,如果项对响应信息做处理,可以实现该方法
        if (null != responseInterceptors) {
            Iterator var7 = responseInterceptors.iterator();

            while(var7.hasNext()) {
                HttpInterceptor<HttpResponse> interceptor = (HttpInterceptor)var7.next();
                interceptor.process(httpResponse);
            }
        }

        return httpResponse;
    }

2.根据首次和其他次的请求,锁定了原因是请求头中的cookie不一致导致,上述方法中的this.initConnection()方法。

java 复制代码
	// 初始化httpConnection
    private void initConnection() {
    	// 判断当前hutool的httpConnection 是否不为空,不为空则关闭连接
        if (null != this.httpConnection) {
            this.httpConnection.disconnectQuietly();
        }
		// 创建一个新的httpConnection 
        this.httpConnection = HttpConnection.create(this.url.setCharset(this.charset).toURL(this.urlHandler), this.config.proxy).setConnectTimeout(this.config.connectionTimeout).setReadTimeout(this.config.readTimeout).setMethod(this.method).setHttpsInfo(this.config.hostnameVerifier, this.config.ssf).setInstanceFollowRedirects(false).setChunkedStreamingMode(this.config.blockSize).header(this.headers, true);
        // 判断cookie 是否为空,不为空就设置cookie
        if (null != this.cookie) {
            this.httpConnection.setCookie(this.cookie);
        } else { // 否则就从已经建立的连接中获取cookie(响应体的coolie),添加到cookie中
        	// 由于此处我的cookie 为空,所以直接执行此处,此方法将上一次请求中响应回来的cookie,自动设置到我下一次的请求的请求头中,导致请求头中 key 为Cookie中,导致请求头中有三个key为Cookie的参数,从而导致第三方接口取cookie 时异常,判断非法。
            GlobalCookieManager.add(this.httpConnection);
        }

        if (this.config.isDisableCache) {
            this.httpConnection.disableCache();
        }

    }

3.由于上述方法的cookie 为空,所以直接执行GlobalCookieManager.add(this.httpConnection),此方法将上一次请求中响应回来的cookie,自动设置到我下一次的请求的请求头中,导致请求头中 key 为Cookie中,导致请求头中有三个key为Cookie的参数,从而导致第三方接口取cookie 时异常,判断非法。

java 复制代码
	// 添加全局Cookie
    public static void add(HttpConnection conn) {
    	// 判断cookie 管理器是否为空,为空则不对cookie进行操作,此处便是解决问题的关键。
        if (null != cookieManager) {
            Map cookieHeader;
            try {
            	// 从连接中获取上次响应体返回的cookie
                cookieHeader = cookieManager.get(getURI(conn), new HashMap(0));
            } catch (IOException var3) {
                throw new IORuntimeException(var3);
            }
			// 将其添加至请求头中,使用的是addRequestProperty,不是setRequestProperty
            conn.header(cookieHeader, false);
        }
    }

问题总结

Hutool HttpRequest 将上一次请求中响应回来的cookie 添加到下一次的请求头中(key为cookie),

导致第三方使用cookie 验证登录信息的地方失效,从而请求被拦截

处理方案

将 问题跟踪-> 步骤3中 cookieManager 对象设置为空,使其跳过 cookie 设置

由于cookieManager 是个静态对象,其类GlobalCookieManager 中提供setCookieManager 方法

HttpRequest 中也提供 了关闭cookie 的方法(此关闭cookie,就是让cookieManager 对象为空的动作),所以处理方案有两个,经测试,两者都可以实现关闭操作

  1. GlobalCookieManager.setCookieManager(null);
  2. HttpRequest .closeCookie();

最终修改后的代码

java 复制代码
String url = "http://xxx/kk/hh/f?page=1&limit=15";
// 此方法为全局方法,如果确实需要此操作,在需要的地方还需再次设置cookieManager  对象
	HttpRequest .closeCookie();
	for(int i = 0;i<10;i++){
        HttpRequest http = HttpRequest.get(url);
        http.header("Accept", "application/json, text/javascript, */*; q=0.01")
                .header("Accept-Encoding", "gzip, deflate, br")
                .header("Connection", "keep-alive")
                .header("Cookie", "kkid=sdf456sadf45dsf6ds4f; Token=15sd4f5ds6ads54f5sdf45dsf")
                HttpResponse tt = http.execute();
            	String body = tt.body();
     }
相关推荐
夜月行者12 分钟前
如何使用ssm实现基于SSM的宠物服务平台的设计与实现+vue
java·后端·ssm
程序猿小D16 分钟前
第二百六十七节 JPA教程 - JPA查询AND条件示例
java·开发语言·前端·数据库·windows·python·jpa
潘多编程30 分钟前
Java中的状态机实现:使用Spring State Machine管理复杂状态流转
java·开发语言·spring
_阿伟_1 小时前
SpringMVC
java·spring
代码在改了1 小时前
springboot厨房达人美食分享平台(源码+文档+调试+答疑)
java·spring boot
wclass-zhengge1 小时前
数据结构篇(绪论)
java·数据结构·算法
何事驚慌1 小时前
2024/10/5 数据结构打卡
java·数据结构·算法
结衣结衣.1 小时前
C++ 类和对象的初步介绍
java·开发语言·数据结构·c++·笔记·学习·算法
TJKFYY1 小时前
Java.数据结构.HashSet
java·开发语言·数据结构
kylinxjd1 小时前
spring boot发送邮件
java·spring boot·后端·发送email邮件