You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 09:35:18