Servlet 請求對象攜帶豐富的客戶端環境信息。本文系統匯總 HttpServletRequest 核心屬性獲取方法、User-Agent 解析策略、真實 IP 提取、跨語言對比 (ASP.NET/PHP/JS) 及生產環境注意事項。
public final class ClientInfoExtractor {
public static ClientInfo extract(HttpServletRequest request) {
ClientInfo info = new ClientInfo();
// 1. User-Agent 原始字符串
String ua = request.getHeader("User-Agent");
info.setUserAgent(ua);
// 2. 解析瀏覽器與作業系統 (簡易版,生產建議用 ua-parser)
if (ua != null) {
info.setBrowser(parseBrowser(ua));
info.setOs(parseOs(ua));
}
// 3. Servlet 標準屬性
info.setMethod(request.getMethod());
info.setRequestURI(request.getRequestURI());
info.setServletPath(request.getServletPath());
info.setServerName(request.getServerName());
info.setServerPort(request.getServerPort());
info.setRemoteAddr(request.getRemoteAddr()); // 直接客戶端 IP (可能是代理)
info.setRemoteHost(request.getRemoteHost()); // 客戶端主機名
info.setProtocol(request.getProtocol()); // HTTP/1.1
info.setScheme(request.getScheme()); // http/https
info.setQueryString(request.getQueryString());
info.setAuthType(request.getAuthType());
info.setContentLength(request.getContentLengthLong());
info.setContentType(request.getContentType());
info.setCharacterEncoding(request.getCharacterEncoding());
info.setSessionId(request.getRequestedSessionId());
// 4. 所有 Header (調試用)
Map headers = new HashMap<>();
Enumeration names = request.getHeaderNames();
while (names.hasMoreElements()) {
String name = names.nextElement();
headers.put(name, request.getHeader(name));
}
info.setHeaders(headers);
return info;
}
// 簡易解析示例 (生產請用 ua-parser-js / Yauaa)
private static String parseBrowser(String ua) {
if (ua.contains("Edg/")) return "Edge";
if (ua.contains("Chrome/")) return "Chrome";
if (ua.contains("Firefox/")) return "Firefox";
if (ua.contains("Safari/")) return "Safari";
if (ua.contains("MSIE") || ua.contains("Trident")) return "IE";
return "Unknown";
}
private static String parseOs(String ua) {
if (ua.contains("Windows NT 10.0")) return "Windows 10/11";
if (ua.contains("Windows NT 6.3")) return "Windows 8.1";
if (ua.contains("Windows NT 6.1")) return "Windows 7";
if (ua.contains("Mac OS X")) return "macOS";
if (ua.contains("Linux")) return "Linux";
if (ua.contains("Android")) return "Android";
if (ua.contains("iPhone") || ua.contains("iPad")) return "iOS";
return "Unknown";
}
}
| 方法 | 返回類型 | 含義 | 典型值 |
|---|---|---|---|
| getMethod() | String | HTTP 方法 | GET, POST, PUT, DELETE |
| getRequestURI() | String | 請求 URI (含 context path) | /myapp/api/users |
| getRequestURL() | StringBuffer | 完整請求 URL (不含查詢串) | https://host:8080/myapp/api/users |
| getServletPath() | String | 匹配的 Servlet 路徑 | /api/users |
| getPathInfo() | String | 額外路徑信息 | /123/detail |
| getQueryString() | String | 查詢字符串 | page=1&size=20 |
| getRemoteAddr() | String | 客戶端 IP (直接連接者) | 192.168.1.100 |
| getRemoteHost() | String | 客戶端主機名 (DNS 反解) | client.example.com |
| getRemoteUser() | String | 認證用戶 principal | john_doe |
| getAuthType() | String | 認證方案 | BASIC, FORM, CLIENT_CERT |
| getProtocol() | String | 協議版本 | HTTP/1.1 |
| getScheme() | String | 協議方案 | http, https |
| getServerName() | String | 服務器主機名 | localhost |
| getServerPort() | int | 服務器端口 | 8080 |
| getContentType() | String | 請求體 MIME 類型 | application/json |
| getContentLengthLong() | long | 請求體長度 | 1024 |
| getCharacterEncoding() | String | 請求體編碼 | UTF-8 |
| getRequestedSessionId() | String | 客戶端攜帶的 Session ID | JSESSIONID=abc123 |
| getHeader(name) | String | 單個 Header 值 | 見下表 |
| getHeaders(name) | Enumeration | 多值 Header | Cookie, X-Forwarded-For |
| getHeaderNames() | Enumeration | 所有 Header 名稱 | 遍歷調試 |
| Header | 獲取方法 | 用途 |
|---|---|---|
| User-Agent | request.getHeader("User-Agent") | 客戶端標識、瀏覽器/OS 統計 |
| Referer | request.getHeader("Referer") | 來源頁面、防盜鏈、CSRF 檢查 |
| Accept | request.getHeader("Accept") | 內容協商 |
| Accept-Language | request.getHeader("Accept-Language") | 國際化語言偏好 |
| Accept-Encoding | request.getHeader("Accept-Encoding") | 響應壓縮 (gzip, br) |
| Cookie | request.getHeader("Cookie") | 原始 Cookie 字符串 |
| Authorization | request.getHeader("Authorization") | Bearer Token、Basic Auth |
| X-Forwarded-For | request.getHeader("X-Forwarded-For") | 真實客戶端 IP (代理鏈路) |
| X-Real-IP | request.getHeader("X-Real-IP") | Nginx 透傳真實 IP |
| X-Request-Id | request.getHeader("X-Request-Id") | 分布式追蹤 ID |
| 語言/平台 | 獲取方式 |
|---|---|
| Java Servlet | request.getHeader("User-Agent") |
| ASP.NET Core | Request.Headers["User-Agent"] 或 Request.Headers.UserAgent |
| ASP.NET Framework | Request.UserAgent 或 Request.Headers["User-Agent"] |
| PHP | $_SERVER['HTTP_USER_AGENT'] |
| Node.js (Express) | req.get('User-Agent') 或 req.headers['user-agent'] |
| Python (Flask/Django) | request.headers.get('User-Agent') / request.META['HTTP_USER_AGENT'] |
| Go (net/http) | r.Header.Get("User-Agent") |
| JavaScript (瀏覽器) | navigator.userAgent |
手寫正則不可維護,推薦:
nl.basjes.parse.useragent
yauaa
7.3.0
// 使用
UserAgentAnalyzer analyzer = UserAgentAnalyzer.newBuilder().build();
UserAgent agent = analyzer.parse(request.getHeader("User-Agent"));
String browser = agent.getValue(UserAgent.BROWSER_NAME); // Chrome
String version = agent.getValue(UserAgent.BROWSER_VERSION); // 119.0.0.0
String os = agent.getValue(UserAgent.OPERATING_SYSTEM_NAME); // Windows 10
String device = agent.getValue(UserAgent.DEVICE_CLASS); // Desktop
public static String getRealIp(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (isValidIp(ip)) return ip.split(",")[0].trim();
ip = request.getHeader("X-Real-IP");
if (isValidIp(ip)) return ip;
return request.getRemoteAddr();
}
private static boolean isValidIp(String ip) {
return ip != null && !ip.isEmpty() && !"unknown".equalsIgnoreCase(ip);
}
前提:Nginx/網關必須正確配置透傳,且應用層**只信任**受信代理層設置的 Header。
<%@ page import="java.util.*,javax.servlet.http.*" %>
<%
out.println("Protocol: " + request.getProtocol() + "
");
out.println("Scheme: " + request.getScheme() + "
");
out.println("Server: " + request.getServerName() + ":" + request.getServerPort() + "
");
out.println("Remote: " + request.getRemoteAddr() + " / " + request.getRemoteHost() + "
");
out.println("Method: " + request.getMethod() + "
");
out.println("URI: " + request.getRequestURI() + "
");
out.println("Query: " + request.getQueryString() + "
");
out.println("UA: " + request.getHeader("User-Agent") + "
");
out.println("Referer: " + request.getHeader("Referer") + "
");
out.println("X-Forwarded-For: " + request.getHeader("X-Forwarded-For") + "
");
out.println("All Headers:
");
Enumeration e = request.getHeaderNames();
while (e.hasMoreElements()) {
String n = e.nextElement();
out.println(" " + n + " = " + request.getHeader(n) + "
");
}
%>
HttpServletRequest 標準 API 獲取,Header 用 getHeader/getHeaders。