1. 概述

Web 应用程序主要基于请求-响应模型运作,这个模型描述了客户端与基于HTTP协议的Web服务器之间的数据交换。在服务器端,处理或拒绝请求时,理解发起请求的客户端至关重要。

在这个教程中,我们将学习如何从HTTP请求中获取客户端信息。

2. HTTP 请求对象

在了解HTTP请求之前,我们首先需要理解Servlet。Servlet是Java实现Web开发扩展能力的基础部分,用于处理HTTP请求并生成动态内容。

在Java Servlet API中,HttpServletRequest接口代表客户端发送的HTTP请求。HttpServletRequest对象非常方便,可以用来捕获关于客户端的重要信息。HttpServletRequest提供了现成的方法,如getRemoteAddr()getRemoteHost()getHeader()getRemoteUser(),这些方法有助于提取客户端信息。

2.1. 获取客户端IP地址

我们可以使用getRemoteAddr()方法获取客户端的IP地址:

String remoteAddr = request.getRemoteAddr(); // 198.167.0.1

需要注意的是,这个方法返回的是服务器看到的IP地址,可能并不总是代表真实的客户端IP,因为可能会受到代理服务器、负载均衡器等因素的影响。

2.2. 获取远程主机

我们可以使用getRemoteHost()方法获取客户端的主机名:

String remoteHost = request.getRemoteHost(); // baeldung.com

2.3. 获取远程用户

如果客户端已认证,我们可以使用getRemoteUser()方法获取用户名:

String remoteUser = request.getRemoteUser(); // baeldung

值得注意的是,如果客户端未认证,那么将得到null

2.4. 获取客户端头信息

我们可以使用getHeader(headerName)方法读取客户端传递的头信息:

String contentType = request.getHeader("content-type"); // application/json

获取客户端信息的一个重要头信息是User-Agent头。它包含了关于客户端软件、系统等的信息。一些关键信息可能包括浏览器类型、操作系统、设备信息、插件、扩展等。

以下是一个User-Agent字符串的例子:

Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36

我们可以使用HttpServletRequest提供的getHeader(String headerName)方法来读取User-Agent头。由于User-Agent字符串的动态性,解析起来可能较为复杂。然而,不同编程语言中有相应的库可以帮助处理。对于Java生态系统,一个流行的选择是uap-java库。

除了上述方法外,还有其他方法,如getSessionID()getMethod()getRequestURL()等,具体取决于应用场景。

3. 提取客户端信息

如上一节所述,为了解析User-Agent,我们可以使用uap-java库。为此,我们需要在pom.xml文件中添加如下XML片段:

<dependency> 
    <groupId>com.github.ua-parser</groupId> 
    <artifactId>uap-java</artifactId> 
    <version>1.5.4</version> 
</dependency>

配置好依赖后,我们可以创建一个简单的AccountServlet,作为客户端的HTTP端点,并接收请求:

@WebServlet(name = "AccountServlet", urlPatterns = "/account")
public class AccountServlet extends HttpServlet {
    public static final Logger log = LoggerFactory.getLogger(AccountServlet.class);

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        AccountLogic accountLogic = new AccountLogic();
        Map<String, String> clientInfo = accountLogic.getClientInfo(request);
        log.info("Request client info: {}, " + clientInfo);

        response.setStatus(HttpServletResponse.SC_OK);
    }
}

然后,我们可以将请求对象传递给AccountLogic,它从用户请求中提取客户端信息。接着,我们可以创建一个AccountLogic类,其中包含获取客户端信息的所有逻辑。我们可以使用前面讨论过的通用辅助方法:

public class AccountLogic {
    public Map<String, String> getClientInfo(HttpServletRequest request) {
        String remoteAddr = request.getRemoteAddr();
        String remoteHost = request.getRemoteHost();
        String remoteUser = request.getRemoteUser();
        String contentType = request.getHeader("content-type");
        String userAgent = request.getHeader("user-agent");

        Parser uaParser = new Parser();
        Client client = uaParser.parse(userAgent);

        Map<String, String> clientInfo = new HashMap<>();
        clientInfo.put("os_family", client.os.family);
        clientInfo.put("device_family", client.device.family);
        clientInfo.put("userAgent_family", client.userAgent.family);
        clientInfo.put("remote_address", remoteAddr);
        clientInfo.put("remote_host", remoteHost);
        clientInfo.put("remote_user", remoteUser);
        clientInfo.put("content_type", contentType);
        return clientInfo;
    }
}

最后,我们准备好编写一个简单的单元测试来验证功能:

@Test
void givenMockHttpServletRequestWithHeaders_whenGetClientInfo_thenReturnsUserAGentInfo() {
    HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
    when(request.getHeader("user-agent")).thenReturn("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36, acceptLanguage:en-US,en;q=0.9");
    when(request.getHeader("content-type")).thenReturn("application/json");
    when(request.getRemoteAddr()).thenReturn("198.167.0.1");
    when(request.getRemoteHost()).thenReturn("baeldung.com");
    when(request.getRemoteUser()).thenReturn("baeldung");

    AccountLogic accountLogic = new AccountLogic();
    Map<String, String> clientInfo = accountLogic.getClientInfo(request);
    assertThat(clientInfo.get("os_family")).isEqualTo("Mac OS X");
    assertThat(clientInfo.get("device_family")).isEqualTo("Mac");
    assertThat(clientInfo.get("userAgent_family")).isEqualTo("Chrome");
    assertThat(clientInfo.get("content_type")).isEqualTo("application/json");
    assertThat(clientInfo.get("remote_user")).isEqualTo("baeldung");
    assertThat(clientInfo.get("remote_address")).isEqualTo("198.167.0.1");
    assertThat(clientInfo.get("remote_host")).isEqualTo("baeldung.com");
}

4. 总结

在这篇文章中,我们了解了HttpServletRequest对象,它提供了获取请求客户端信息的有用方法。我们还学习了User-Agent头,它提供了关于浏览器家族、操作系统家族等系统级别的信息。

随后,我们实现了从请求对象中获取客户端信息的逻辑。

如往常一样,示例代码可在GitHub上找到。