1. 概述

在使用Servlet API开发Java Web应用时,HttpServletRequest对象是处理HTTP请求的核心组件。它提供了访问请求参数、请求头和属性的能力。

请求参数通常由HTTP客户端提供,但某些场景下我们需要在请求被处理前以编程方式修改或添加参数。⚠️ 注意:HttpServletRequest本身没有提供直接修改参数的方法。本文将介绍如何通过扩展其功能来实现参数设置。

2. Maven依赖

除了标准的Java Servlet API:

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.1</version>
    <scope>provided</scope>
</dependency>

我们还会在参数清理场景中使用commons-text库:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-text</artifactId>
    <version>1.10.0</version>
</dependency>

3. 核心Servlet组件

3.1. HttpServletRequest

封装HTTP请求的核心类,提供对请求参数、头信息等的访问。

3.2. HttpServletRequestWrapper

HttpServletRequest的装饰器实现,允许我们动态扩展原始请求对象的功能

3.3. Filter

请求处理链中的拦截器,能在请求到达Servlet前修改请求和响应对象

4. 参数清理:防止XSS攻击

通过编程方式设置参数的典型场景是清理用户输入,防止XSS攻击核心思路是转义HTML特殊字符,确保恶意脚本无法执行。

4.1. 实现步骤

4.1.1. 创建过滤器

@WebFilter(urlPatterns = {"/sanitize/with-sanitize.jsp"})
public class SanitizeParametersFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        HttpServletRequest httpReq = (HttpServletRequest) request;
        chain.doFilter(new SanitizeParametersRequestWrapper(httpReq), response);
    }
}

4.1.2. 实现请求包装器

public class SanitizeParametersRequestWrapper extends HttpServletRequestWrapper {

    private final Map<String, String[]> sanitizedMap;

    public SanitizeParametersRequestWrapper(HttpServletRequest request) {
        super(request);
        sanitizedMap = Collections.unmodifiableMap(
            request.getParameterMap().entrySet().stream()
                .collect(Collectors.toMap(
                  Map.Entry::getKey,
                  entry -> Arrays.stream(entry.getValue())
                    .map(StringEscapeUtils::escapeHtml4)
                    .toArray(String[]::new)
                )));
    }

    @Override
    public Map<String, String[]> getParameterMap() {
        return sanitizedMap;
    }

    @Override
    public String[] getParameterValues(String name) {
        return Optional.ofNullable(getParameterMap().get(name))
            .map(values -> Arrays.copyOf(values, values.length))
            .orElse(null);
    }

    @Override
    public String getParameter(String name) {
        return Optional.ofNullable(getParameterValues(name))
            .map(values -> values[0])
            .orElse(null);
    }
}

关键点:

  • ✅ 使用StringEscapeUtils.escapeHtml4转义HTML实体
  • ✅ 重写所有参数访问方法确保一致性
  • ✅ 返回不可修改的Map(符合Servlet规范)

4.1.3. 测试JSP

The text below comes from request parameter "input":<br/>
<%=request.getParameter("input")%>

4.2. 效果对比

未启用过滤器

访问URL:

http://localhost:8080/sanitize/without-sanitize.jsp?input=<script>alert('Hello');</script>

结果:弹出JavaScript警告框 hello popup

启用过滤器

访问URL:

http://localhost:8080/sanitize/with-sanitize.jsp?input=<script>alert('Hello');</script>

结果:HTML实体被转义并安全显示

The text below comes from request parameter "input":
&lt;script&gt;alert('Hello');&lt;/script&gt;

5. 第三方模块参数注入

当需要与无法修改源码的第三方模块交互时,可通过编程方式注入所需参数。

5.1. 实现方案

5.1.1. 第三方模块示例

<%
    String localeStr = request.getParameter("locale");
    Locale currentLocale = (localeStr != null ? new Locale(localeStr) : null);
%>
The language you have selected: <%=currentLocale != null ? currentLocale.getDisplayLanguage(currentLocale) : " None"%>

5.1.2. 参数注入Servlet

@WebServlet(name = "LanguageServlet", urlPatterns = "/setparam/lang")
public class LanguageServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
            throws IOException, ServletException {

        SetParameterRequestWrapper requestWrapper = new SetParameterRequestWrapper(request);
        requestWrapper.setParameter("locale", Locale.getDefault().getLanguage());
        request.getRequestDispatcher("/setparam/3rd_party_module.jsp")
              .forward(requestWrapper, response);
    }
}

5.1.3. 参数设置包装器

public class SetParameterRequestWrapper extends HttpServletRequestWrapper {

    private final Map<String, String[]> paramMap;

    public SetParameterRequestWrapper(HttpServletRequest request) {
        super(request);
        paramMap = new HashMap<>(request.getParameterMap());
    }

    @Override
    public Map<String, String[]> getParameterMap() {
        return Collections.unmodifiableMap(paramMap);
    }

    public void setParameter(String name, String value) {
        paramMap.put(name, new String[] {value});
    }

    // getParameter()和getParameterValues()实现同前例
}

5.2. 执行结果

访问/setparam/lang时,系统自动注入默认语言参数:

The language you have selected: English

6. 总结

通过本文我们掌握了:

  1. ✅ 使用HttpServletRequestWrapper扩展请求功能
  2. ✅ 通过Filter拦截并修改请求
  3. ✅ 两种典型应用场景:
    • 参数清理(安全增强)
    • 第三方模块参数注入

这些技术为处理特殊业务需求提供了灵活的解决方案。完整代码示例可在GitHub获取。