遇到 401 或 407 时 Java 会怎么处理?谁来提供口令、会重试几次、凭证被缓存多久、全局设置会影响谁?这篇用本机的真实 HTTP 服务逐项验证。
[urlconn]
没有设置 Authenticator:401
服务端记录:GET /secret → 无凭证 → 401
Authenticator 返回正确口令:200;getPasswordAuthentication 被调用 1 次
请求者信息:type=SERVER,scheme=basic,prompt(realm)=realm-b,host=127.0.0.1,port>0=true,protocol=http,site=<addr>,url=/secret,thread=main
服务端记录:GET /secret → 无凭证 → 401
服务端记录:GET /secret → Basic 凭证正确 → 200
同一个 URL 再请求一次:200;累计调用 1 次
服务端记录:GET /secret → Basic 凭证正确 → 200
同一主机另一个路径 /other/x:200;累计调用 1 次
服务端记录:GET /other/x → Basic 凭证正确 → 200
(新服务)Authenticator 一直返回错误口令:最终状态 401;getPasswordAuthentication 被调用 20 次
服务端记录:GET /secret → 无凭证 → 401
服务端记录:GET /secret → Basic 凭证错误 → 401
服务端记录:GET /secret → Basic 凭证错误 → 401
服务端记录:GET /secret → Basic 凭证错误 → 401
服务端记录:GET /secret → Basic 凭证错误 → 401
服务端记录:GET /secret → Basic 凭证错误 → 401
服务端记录:GET /secret → Basic 凭证错误 → 401
服务端记录:GET /secret → Basic 凭证错误 → 401
服务端记录:GET /secret → Basic 凭证错误 → 401
服务端记录:GET /secret → Basic 凭证错误 → 401
服务端记录:GET /secret → Basic 凭证错误 → 401
服务端记录:GET /secret → Basic 凭证错误 → 401
服务端记录:GET /secret → Basic 凭证错误 → 401
服务端记录:GET /secret → Basic 凭证错误 → 401
服务端记录:GET /secret → Basic 凭证错误 → 401
服务端记录:GET /secret → Basic 凭证错误 → 401
服务端记录:GET /secret → Basic 凭证错误 → 401
服务端记录:GET /secret → Basic 凭证错误 → 401
服务端记录:GET /secret → Basic 凭证错误 → 401
服务端记录:GET /secret → Basic 凭证错误 → 401
(新服务)Authenticator 返回 null:最终状态 401;调用 1 次
服务端记录:GET /secret → 无凭证 → 401
(新服务)URL 里带 userinfo(amy:s3cret@):401
服务端记录:GET /userinfo → 无凭证 → 401
手工设置 Authorization 头:200
服务端记录:GET /secret4 → Basic 凭证正确 → 200
(新服务)先用正确口令认证成功,再换成返回错误口令的 Authenticator,请求 /b:200
服务端记录:GET /a → 无凭证 → 401
服务端记录:GET /a → Basic 凭证正确 → 200
服务端记录:GET /b → Basic 凭证正确 → 200 [client]
HttpClient(authenticator):200;调用 1 次;请求者:type=SERVER,scheme=basic,prompt(realm)=realm-b,host=127.0.0.1,port>0=true,protocol=http,site=null,url=/secret,thread=HttpClient-N-Worker-N
服务端记录:GET /secret → 无凭证 → 401
服务端记录:GET /secret → Basic 凭证正确 → 200
同一个 HttpClient 同一个 URL 再请求:200;累计调用 1 次
服务端记录:GET /secret → Basic 凭证正确 → 200
同一个 HttpClient 更深的路径 /secret/sub/deeper:200;累计调用 1 次
服务端记录:GET /secret/sub/deeper → Basic 凭证正确 → 200
同一个 HttpClient 兄弟路径 /elsewhere:200;累计调用 1 次
服务端记录:GET /elsewhere → Basic 凭证正确 → 200
没有 authenticator 的 HttpClient:401
服务端记录:GET /secret → 无凭证 → 401
只有 Authenticator.setDefault、HttpClient 没设 authenticator:401(HttpClient 不使用全局默认 Authenticator)
服务端记录:GET /secret → 无凭证 → 401
手工加 Authorization 头(预先认证):200
服务端记录:GET /secret → Basic 凭证正确 → 200
sendAsync 时 getPasswordAuthentication 所在线程:HttpClient-N-Worker-N
(新服务)authenticator 一直返回错误口令:IOException: too many authentication attempts. Limit: 3;调用 4 次;耗时 4 ms
服务端记录:GET /x → 无凭证 → 401
服务端记录:GET /x → Basic 凭证错误 → 401
服务端记录:GET /x → Basic 凭证错误 → 401
服务端记录:GET /x → Basic 凭证错误 → 401
服务端记录:GET /x → Basic 凭证错误 → 401
(新服务)authenticator 返回 null:IOException: No credentials provided;调用 1 次
服务端记录:GET /x → 无凭证 → 401 [digest]
HttpURLConnection + Digest(服务端 algorithm=MD5):401;Authenticator 调用 1 次;请求者:type=SERVER,scheme=digest,prompt(realm)=realm-d,host=127.0.0.1,port>0=true,protocol=http,site=<addr>,url=/d1,thread=main
服务端记录:GET /d1 → 无凭证 → 401
HttpClient + 同一个 Digest 服务:401;authenticator 调用 0 次
服务端记录:GET /hc → 无凭证 → 401
HttpURLConnection + Digest(服务端 algorithm=SHA-256):200;Authenticator 调用 1 次;请求者:type=SERVER,scheme=digest,prompt(realm)=realm-d,host=127.0.0.1,port>0=true,protocol=http,site=<addr>,url=/d1,thread=main
服务端记录:GET /d1 → 无凭证 → 401
服务端记录:GET /d1 → Digest 凭证(algorithm=SHA-256)正确(nc=00000001) → 200
同一服务再请求 /d2:200;累计调用 1 次
服务端记录:GET /d2 → 无凭证 → 401
服务端记录:GET /d2 → Digest 凭证(algorithm=SHA-256)正确(nc=00000002) → 200
再请求 /d3:200
服务端记录:GET /d3 → 无凭证 → 401
服务端记录:GET /d3 → Digest 凭证(algorithm=SHA-256)正确(nc=00000003) → 200
HttpClient + 同一个 Digest 服务:401;authenticator 调用 0 次
服务端记录:GET /hc → 无凭证 → 401
Oct 04, 2026 3:50:08 AM sun.net.www.protocol.http.DigestAuthentication log
INFO: Rejecting digest authentication with insecure algorithm: MD5 This constraint may be relaxed by setting the "http.auth.digest.reEnabledAlgorithms" system property.
[digest](JVM 参数 -Dhttp.auth.digest.reEnabledAlgorithms=MD5)
HttpURLConnection + Digest(服务端 algorithm=MD5):200;Authenticator 调用 1 次;请求者:type=SERVER,scheme=digest,prompt(realm)=realm-d,host=127.0.0.1,port>0=true,protocol=http,site=<addr>,url=/d1,thread=main
服务端记录:GET /d1 → 无凭证 → 401
服务端记录:GET /d1 → Digest 凭证(algorithm=MD5)正确(nc=00000001) → 200
HttpClient + 同一个 Digest 服务:401;authenticator 调用 0 次
服务端记录:GET /hc → 无凭证 → 401
HttpURLConnection + Digest(服务端 algorithm=SHA-256):200;Authenticator 调用 1 次;请求者:type=SERVER,scheme=digest,prompt(realm)=realm-d,host=127.0.0.1,port>0=true,protocol=http,site=<addr>,url=/d1,thread=main
服务端记录:GET /d1 → 无凭证 → 401
服务端记录:GET /d1 → Digest 凭证(algorithm=SHA-256)正确(nc=00000001) → 200
同一服务再请求 /d2:200;累计调用 1 次
服务端记录:GET /d2 → 无凭证 → 401
服务端记录:GET /d2 → Digest 凭证(algorithm=SHA-256)正确(nc=00000002) → 200
再请求 /d3:200
服务端记录:GET /d3 → 无凭证 → 401
服务端记录:GET /d3 → Digest 凭证(algorithm=SHA-256)正确(nc=00000003) → 200
HttpClient + 同一个 Digest 服务:401;authenticator 调用 0 次
服务端记录:GET /hc → 无凭证 → 401 [proxy]
经代理、没有 Authenticator:407
服务端记录:代理收到 GET http://127.0.0.1:62395/via-proxy,Proxy-Authorization = 无 → 407
经代理、Authenticator 提供代理口令:200;调用 1 次;请求者:type=PROXY,scheme=basic,prompt(realm)=proxy-realm,host=127.0.0.1,port>0=true,protocol=http,site=<addr>,url=/via-proxy2,thread=main
服务端记录:代理收到 GET http://127.0.0.1:62395/via-proxy2,Proxy-Authorization = 无 → 407
服务端记录:代理收到 GET http://127.0.0.1:62395/via-proxy2,Proxy-Authorization = 正确 → 转发
服务端记录:目标服务器收到 GET /via-proxy2,Proxy-Authorization = null
按 RequestorType 区分(PROXY 返回代理口令,SERVER 返回别的)请求 /via-proxy3:200
服务端记录:代理收到 GET http://127.0.0.1:62395/via-proxy3,Proxy-Authorization = 正确 → 转发
服务端记录:目标服务器收到 GET /via-proxy3,Proxy-Authorization = null
HttpClient 经代理 + authenticator:200;调用 1 次;请求者:type=PROXY,scheme=basic,prompt(realm)=proxy-realm,host=127.0.0.1,port>0=true,protocol=http,site=null,url=/via-proxy4,thread=HttpClient-N-Worker-N
服务端记录:代理收到 GET http://127.0.0.1:62395/via-proxy4,Proxy-Authorization = 无 → 407
服务端记录:代理收到 GET http://127.0.0.1:62395/via-proxy4,Proxy-Authorization = 正确 → 转发
服务端记录:目标服务器收到 GET /via-proxy4,Proxy-Authorization = null
HttpClient 第二次经代理请求:200;累计调用 1 次
服务端记录:代理收到 GET http://127.0.0.1:62395/via-proxy5,Proxy-Authorization = 正确 → 转发
服务端记录:目标服务器收到 GET /via-proxy5,Proxy-Authorization = null [scope]
全局 Authenticator 按端口返回不同口令:服务 1 → 200;服务 2 → 200
服务端记录:GET /a → 无凭证 → 401
服务端记录:GET /a → Basic 凭证正确 → 200
服务端记录:GET /b → 无凭证 → 401
服务端记录:GET /b → Basic 凭证正确 → 200
两个线程并发访问两个服务(路径 /t 与之前同主机,可能已有缓存):200 / 200(全局 Authenticator 对所有线程生效)
两个 HttpClient 各带自己的 authenticator:200 / 200
用 c1 的 authenticator(amy)访问服务 6(要 bob):IOException: too many authentication attempts. Limit: 3
服务端记录:GET /d → 无凭证 → 401
服务端记录:GET /d → Basic 凭证错误 → 401
服务端记录:GET /d → Basic 凭证错误 → 401
服务端记录:GET /d → Basic 凭证错误 → 401
服务端记录:GET /d → Basic 凭证错误 → 401
静态方法 Authenticator.requestPasswordAuthentication(JDK 9 起 deprecated):x
Authenticator.getDefault()(JDK 14+)= null