Hutool HttpRequest 首次請求正常 第二次被系統攔截
- 功能描述
- 異常現象
- 錯誤代碼
- 異常排查
- 問題跟蹤
- 問題總結
- 處理方案
- 最終修改后的代碼
功能描述
需要請求第三方某個接口,獲取接口中的數據。
異常現象
使用main 方法 通過Hutool 工具類發出請求,獲取數據信息時,發現第一次請求接口可以正常獲取數據項,但是循環遍歷請求接口時,除首次請求外,其他請求都被第三方接口攔截,提示需要登錄。
錯誤代碼
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();
// 最終執行到這個方法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()方法。
// 初始化httpConnectionprivate 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 是否為空,不為空就設置cookieif (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 時異常,判斷非法。
// 添加全局Cookiepublic static void add(HttpConnection conn) {// 判斷cookie 管理器是否為空,為空則不對cookie進行操作,此處便是解決問題的關鍵。if (null != cookieManager) {Map cookieHeader;try {// 從連接中獲取上次響應體返回的cookiecookieHeader = cookieManager.get(getURI(conn), new HashMap(0));} catch (IOException var3) {throw new IORuntimeException(var3);}// 將其添加至請求頭中,使用的是addRequestProperty,不是setRequestPropertyconn.header(cookieHeader, false);}}
問題總結
Hutool HttpRequest 將上一次請求中響應回來的cookie 添加到下一次的請求頭中(key為cookie),
導致第三方使用cookie 驗證登錄信息的地方失效,從而請求被攔截
處理方案
將 問題跟蹤-> 步驟3中 cookieManager 對象設置為空,使其跳過 cookie 設置
由于cookieManager 是個靜態對象,其類GlobalCookieManager 中提供setCookieManager 方法
HttpRequest 中也提供 了關閉cookie 的方法(此關閉cookie,就是讓cookieManager 對象為空的動作),所以處理方案有兩個,經測試,兩者都可以實現關閉操作
- GlobalCookieManager.setCookieManager(null);
- HttpRequest .closeCookie();
最終修改后的代碼
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();}