當服務器返回 401 Unauthorized 並附帶 WWW-Authenticate 頭時,客戶端需提供憑證。兩種主流方案:
username:password,明文等價,必須配合 HTTPSjava.net.Authenticator 提供全局認證策略,支持 HTTP/HTTPS/FTP 等協議。核心流程:
Authenticator.setDefault(new MyAuthenticator())URL.openConnection()getPasswordAuthentication()PasswordAuthentication → 自動添加 Authorization 頭重試
import java.net.*;
import java.io.*;
public class BasicAuthClient {
static final String USER = "username";
static final String PASS = "password";
static class BasicAuthenticator extends Authenticator {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(USER, PASS.toCharArray());
}
}
public static void main(String[] args) throws Exception {
// 設置全局認證器(應用啟動時一次性配置)
Authenticator.setDefault(new BasicAuthenticator());
URL url = new URL("https://api.example.com/protected");
try (InputStream in = url.openStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in, "UTF-8"))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}
}
}
static class MultiRealmAuthenticator extends Authenticator {
private final Map credentials = new HashMap<>();
public void addCredential(String host, int port, String realm, String user, String pass) {
credentials.put(host + ":" + port + ":" + realm, new PasswordAuthentication(user, pass.toCharArray()));
}
@Override
protected PasswordAuthentication getPasswordAuthentication() {
String key = getRequestingHost() + ":" + getRequestingPort() + ":" + getRequestingPrompt();
return credentials.getOrDefault(key, credentials.get(getRequestingHost() + ":" + getRequestingPort()));
}
}
// 手動設置 Authorization 頭
String credentials = Base64.getEncoder().encodeToString((USER + ":" + PASS).getBytes());
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("Authorization", "Basic " + credentials);
適用場景:明知需要認證的 API,減少一次網絡往返。
toCharArray() 可主動清零,減少內存洩漏風險conn.setConnectTimeout(5000); conn.setReadTimeout(10000);| 方案 | 優勢 | 適用場景 |
|---|---|---|
| Apache HttpClient 4.5+ | 連接池、重試、攔截器、異步支持 | 企業級應用、高併發 |
| Java 11+ HttpClient | 標準庫、響應式、HTTP/2、WebSocket | 現代化項目、無第三方依賴 |
| Spring WebClient | 響應式流、與 Spring 生態集成 | Spring Boot/WebFlux 項目 |
| OkHttp | 連接復用、透明壓縮、現代 API | Android、高性能客戶端 |
HttpClient client = HttpClient.newBuilder()
.authenticator(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(USER, PASS.toCharArray());
}
})
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/protected"))
.GET()
.build();
HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
| 現象 | 原因 | 解決 |
|---|---|---|
| 401 循環 | 憑證錯誤或 Realm 不匹配 | 檢查用戶名/密碼、Realm 名稱、大小寫 |
| Authenticator 不生效 | 未設置 setDefault 或被覆蓋 | 應用啟動早期統一設置,避免多處調用 |
| HTTPS 握手失敗 | 證書不信任 | 導入 CA 證書或配置信任存儲 |
| 中文亂碼 | 編碼不一致 | 指定 StandardCharsets.UTF_8 |
Java Authenticator 提供了簡潔的聲明式 HTTP 認證支持,適合簡單場景。生產系統建議:使用成熟 HTTP 客戶端庫、強制 HTTPS、外部化憑證管理、實施預emptive 認證優化性能。理解底層 401 握手流程、Realm 機制、Base64 編碼本質,有助於排查認證失敗、安全審計及遷移至現代認證方案(OAuth2、JWT、mTLS)。