HTTP Header 攜帶客戶端信息、認證憑證、代理鏈路、緩存控制等關鍵元數據。本文系統講解在 Servlet、Spring MVC、JSP 中獲取 Header 的多種方式,含單值/多值獲取、遍歷所有 Header、常見 Header 含義、偽造風險與防範。
// 單值獲取 (取首個)
String userAgent = request.getHeader("User-Agent");
String token = request.getHeader("Authorization");
// 多值獲取 (如多個 Cookie、X-Forwarded-For)
Enumeration headers = request.getHeaders("X-Forwarded-For");
while (headers.hasMoreElements()) {
System.out.println(headers.nextElement());
}
// 獲取所有 Header 名稱
Enumeration names = request.getHeaderNames();
while (names.hasMoreElements()) {
String name = names.nextElement();
String value = request.getHeader(name); // 取首個
System.out.println(name + ": " + value);
}
@GetMapping("/api/user")
public UserInfo getUser(
@RequestHeader("User-Agent") String ua,
@RequestHeader(value = "X-Request-Id", required = false) String requestId,
@RequestHeader Map allHeaders // 全量綁定為 Map
) {
// 自動處理缺失、類型轉換
}
@Autowired HttpServletRequest request; // 請求作用域 Bean,線程安全
@GetMapping("/legacy")
public void legacy() {
String ua = request.getHeader("User-Agent");
}
@ControllerAdvice
public class HeaderInterceptor {
private static final Logger log = LoggerFactory.getLogger(HeaderInterceptor.class);
@ModelAttribute
public void extractCommonHeaders(HttpServletRequest request, Model model) {
String traceId = request.getHeader("X-Trace-Id");
String userId = request.getHeader("X-User-Id");
model.addAttribute("traceId", traceId);
model.addAttribute("userId", userId);
// 結構化日誌
log.info("Request: {} {} | TraceId: {} | UA: {}",
request.getMethod(), request.getRequestURI(), traceId,
request.getHeader("User-Agent"));
}
}
<%-- header.jsp --%>
<%
out.println("User-Agent: " + request.getHeader("User-Agent") + "
");
out.println("Referer: " + request.getHeader("Referer") + "
");
out.println("Accept-Language: " + request.getHeader("Accept-Language") + "
");
out.println("X-Forwarded-For: " + request.getHeader("X-Forwarded-For") + "
");
// 遍歷所有
Enumeration e = request.getHeaderNames();
while (e.hasMoreElements()) {
String name = e.nextElement();
out.println(name + " = " + request.getHeader(name) + "
");
}
%>
| Header | 含義 | 典型值 | 應用場景 |
|---|---|---|---|
| Host | 請求目標主機 | api.example.com:8080 | 虛擬主機路由 |
| User-Agent | 客戶端標識 | Mozilla/5.0..., PostmanRuntime/7.29 | 瀏覽器兼容、爬蟲識別、統計 |
| Referer | 來源頁面 URL | https://google.com/search?q=java | 防盜鏈、來源統計、CSRF 檢查 |
| Authorization | 認證憑證 | Bearer eyJhbGciOiJIUzI1NiJ9... | OAuth2、JWT、Basic Auth |
| Cookie | 攜帶 Cookie | JSESSIONID=abc; token=xyz | 會話維持 |
| Content-Type | 請求體媒體類型 | application/json; charset=UTF-8 | 解析器選擇 |
| Content-Length | 請求體長度 | 1024 | 限流、防洪 |
| Accept | 可接受響應類型 | application/json, text/html | 內容協商 |
| Accept-Encoding | 支援壓縮格式 | gzip, deflate, br | 響應壓縮 |
| Accept-Language | 偏好語言 | zh-CN,zh;q=0.9,en;q=0.8 | 國際化 |
| X-Forwarded-For | 代理鏈路客戶端真實 IP | 203.0.113.195, 70.41.3.18 | 獲取真實 IP 關鍵 |
| X-Real-IP | Nginx 透傳真實 IP | 203.0.113.195 | 獲取真實 IP |
| X-Request-Id / Trace-Id | 分布式追蹤 ID | a1b2c3d4-e5f6-... | 鏈路追蹤、日誌關聯 |
| If-None-Match / If-Modified-Since | 條件請求緩存驗證 | W/"abc123" / Wed, 21 Oct 2023 | 304 Not Modified |
public static String getRealIp(HttpServletRequest request) {
// 1. 優先從代理 Header 獲取 (Nginx/網關配置透傳)
String ip = request.getHeader("X-Forwarded-For");
if (ip != null && !ip.isEmpty() && !"unknown".equalsIgnoreCase(ip)) {
// 可能多級代理,取第一個非未知 IP
return ip.split(",")[0].trim();
}
// 2. 備選 Header
ip = request.getHeader("X-Real-IP");
if (ip != null && !ip.isEmpty() && !"unknown".equalsIgnoreCase(ip)) {
return ip;
}
// 3. 最後回退到 Servlet API
return request.getRemoteAddr(); // 可能是代理 IP
}
前提:反向代理 (Nginx/網關) 必須正確設置:
# Nginx 配置
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
| 風險 | 攻擊示例 | 防範措施 |
|---|---|---|
| X-Forwarded-For 偽造 | 攻擊者發送 `X-Forwarded-For: 1.2.3.4` 繞過 IP 白名單 | 只信任**受信代理層**設置的 Header,應用層忽略客戶端直接發送的同名 Header |
| User-Agent 偽造 | 爬蟲偽裝瀏覽器 UA | 結合行為分析、JS 挑戰、設備指紋,不單獨依賴 UA |
| Referer 偽造/隱藏 | 防盜鏈繞過、隱私保護模式 | 不將 Referer 作為唯一授權依據,配合 Token/簽名 |
| 自定義 Header 注入 | 繞過內部服務間鑒權 | 網關層清洗非白名單 Header,內部服務只信任網關注入的 Header |
@RequestHeader 聲明式綁定,類型安全、可選參數、文檖化友好。HttpServletRequest。