Squid代理同时生成TCP_DENIED/407与TCP_TUNNEL/200日志问题咨询
原因分析
这条TCP_DENIED/407日志的出现,是因为你使用的HttpClient默认采用**"质询-响应"模式**处理代理认证:
- 第一次请求时,HttpClient没有主动携带代理BasicAuth凭证,直接向Squid发送
CONNECT请求; - Squid检测到请求缺少认证信息,返回
407 Proxy Authentication Required,对应第一条日志; - HttpClient收到407响应后,才会从CredentialsProvider中取出凭证,重新发送带代理认证头的
CONNECT请求,这次Squid验证通过,建立隧道返回200,对应第二条日志。
而浏览器的行为不同:浏览器在发起代理请求前,会主动识别代理的认证要求,提前在请求头中带上Proxy-Authorization,因此只需要一次请求就能通过验证,不会产生407日志。
解决办法(让HttpClient提前发送代理认证)
你可以修改HttpClient配置,开启抢先式代理认证,让第一次请求就携带代理凭证,避免两次请求的情况。修改后的代码如下:
var restTemplate = RestTemplate() val credentialsProvider = BasicCredentialsProvider() credentialsProvider.setCredentials( AuthScope(PROXY_SERVER_HOST, PROXY_SERVER_PORT), UsernamePasswordCredentials("username", "password") ) val proxy = HttpHost(PROXY_SERVER_HOST, PROXY_SERVER_PORT) val httpClient = HttpClientBuilder.create() .setRoutePlanner(object : DefaultProxyRoutePlanner(proxy){ override fun determineProxy(target: HttpHost, request: HttpRequest, context: HttpContext): HttpHost { return super.determineProxy(target, request, context) } }) .setDefaultCredentialsProvider(credentialsProvider) // 添加抢先式认证拦截器 .addInterceptorFirst(PreemptiveAuthInterceptor()) .disableCookieManagement() .build() restTemplate.setRequestFactory(HttpComponentsClientHttpRequestFactory(httpClient)) val headers = HttpHeaders() headers.contentType = MediaType.APPLICATION_JSON headers.accept = Collections.singletonList(MediaType.APPLICATION_JSON) val request: HttpEntity<*> = HttpEntity<Any?>(headers) val response: ResponseEntity<String> = restTemplate.exchange<String>( uri, HttpMethod.GET, request, String::class.java ) // 定义抢先式代理认证拦截器 class PreemptiveAuthInterceptor : HttpRequestInterceptor { override fun process(request: HttpRequest, context: HttpContext) { val authState = context.getAttribute(HttpClientContext.TARGET_AUTH_STATE) as AuthState if (authState.authScheme == null) { val credsProvider = context.getAttribute(HttpClientContext.CREDS_PROVIDER) as CredentialsProvider val authScope = AuthScope(proxy.hostName, proxy.port) val credentials = credsProvider.getCredentials(authScope) if (credentials != null) { authState.update(AuthSchemes.BASIC, credentials) } } } }
修改后,HttpClient会在第一次请求时主动带上Proxy-Authorization头,Squid日志中就只会出现一条TCP_TUNNEL/200的记录,和浏览器的行为一致。
内容的提问来源于stack exchange,提问作者Akul Tomar
相关产品推荐
相关产品推荐

