在Java中的HttpServletRequest中设置参数

2025/04/07

1. 概述

使用Servlet API开发Java中的Web应用程序时,HttpServletRequest对象在处理传入的HTTP请求中起着关键作用,它提供对请求的各个方面(例如参数、标头和属性)的访问。

请求参数始终由HTTP客户端提供。但是,有些情况下,我们可能需要在应用程序处理HttpServletRequest对象之前以编程方式在HttpServletRequest对象中设置参数。

需要注意的是,HttpServletRequest缺少用于添加新参数或更改参数值的Setter方法。在本文中,我们将探讨如何通过扩展原始HttpServletRequest的功能来实现这一点。

2. Maven依赖

除了标准Java Servlet API之外:

<dependency>
    <groupId>jakarta.servlet</groupId>
    <artifactId>jakarta.servlet-api</artifactId>
    <version>6.1.0</version>
    <scope>provided</scope>
</dependency>

我们还将在我们的用例中使用commons-text库,通过转义HTML实体来清理请求参数:

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

3. 必需的Servlet组件

在深入研究实际示例之前,让我们快速了解一下我们将要使用的某些基础Servlet组件。

3.1 HttpServletRequest

HttpServletRequest类是客户端和Servlet之间通信的主要方式,它封装传入的HTTP请求,提供对参数、标头和其他与请求相关的信息的访问。

3.2 HttpServletRequestWrapper

HttpServletRequestWrapper通过充当现有HttpServletRequest对象的装饰器来扩展HttpServletRequest的功能,这使我们能够根据特定需求附加其他职责。

3.3 Filter

Filter会在请求和响应遍历Servlet容器时捕获并处理它们,这些过滤器旨在在Servlet执行之前调用,使其能够更改传入请求和传出响应。

4. 参数清理

在HttpServletRequest中以编程方式设置参数的应用之一是清理请求参数,从而有效缓解跨站点脚本(XSS)漏洞。此过程涉及从用户输入中消除或编码潜在有害字符,从而增强Web应用程序的安全性

4.1 示例

现在,让我们详细探索这个过程。首先,我们必须设置一个Servlet过滤器来拦截请求。过滤器提供了一种在请求和响应到达目标Servlet或JSP之前对其进行修改的方法。

下面是一个Servlet过滤器的具体示例,它拦截对特定URL模式的所有请求,确保过滤器链通过原始HttpServletRequest对象返回SanitizeParametersRequestWrapper:

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

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpSerlvetRequest httpReq = (HttpSerlvetRequest) request;
        chain.doFilter(new SanitizeParametersRequestWrapper(httpReq), response);
    }
}

SanitizeParameterRequestWrapper类扩展了HttpServletRequestWrapper,这在参数清理过程中起着至关重要的作用,该类旨在清理来自原始HttpServletRequest的请求参数,并仅将清理后的参数公开给调用JSP:

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来清理值,处理后的参数收集在新的sanitizedMap中。

我们重写getParameter方法,以便从sanitizedMap中返回相应的参数,而不是原始请求参数。尽管getParameter方法是主要使用重点,但重写getParameterMap和getParameterValues也至关重要。我们确保所有参数检索方法的行为一致,并在整个过程中保持安全标准

根据规范,getParameterMap方法保证不可修改Map,从而防止更改内部值。因此,遵守此约定并确保重写也返回不可修改的Map非常重要。同样,重写的getParameterValues方法返回克隆数组而不是其内部值。

现在,让我们创建一个JSP来演示,它只是在屏幕上呈现输入请求参数的值:

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

4.2 结果

现在,让我们在未激活清理过滤器的情况下运行JSP,我们将向请求参数注入一个脚本标记作为反射型XSS:

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

我们将看到参数中嵌入的JavaScript正在被浏览器执行:

接下来,我们将运行净化后的版本:

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

在这种情况下,过滤器将捕获请求并将SanitizeParameterRequestWrapper传递给调用JSP。因此,弹出窗口将不再出现;相反,我们将观察到HTML实体被转义并明显呈现在屏幕上:

The text below comes from request parameter "input":
<script>alert('Hello');</script>

5. 第三方资源访问

让我们考虑另一种情况,第三方模块接收请求参数locale来更改模块显示的语言。请注意,我们无法直接修改第三方模块的源代码。

在我们的示例中,我们将locale参数设置为默认系统区域设置。但是,它也可以从其他来源获取,例如HTTP会话。

5.1 示例

第三方模块是一个JSP页面:

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

如果提供了locale设置参数,模块将显示语言名称。

我们将为目标请求参数提供默认的系统语言Locale.getDefault().getLanguage(),为此,我们在SetParameterRequestWrapper中设置locale参数,该参数装饰原始HttpServletRequest:

@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);
    }
}

在创建SetParameterRequestWrapper时,我们将采用与上一节类似的方法。此外,我们将实现setParameter方法,以将新参数添加到现有参数Map中:

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() and getParameterValues() are the same as SanitizeParametersRequestWrapper
}

getParameter方法检索存储在paramMap中的参数而不是原始请求。

5.2 结果

LanguageServlet通过SetParameterRequestWrapper将locale参数传递给第三方模块,当我们在英语语言服务器上访问第三方模块时,我们将看到以下内容:

The language you have selected: English

6. 总结

在本文中,我们了解了一些重要的Servlet组件,这些组件可用于Java Web应用程序开发中处理客户端请求,包括HttpServletRequest、HttpServletRequestWrapper和Filter。

通过具体的示例,我们演示了如何扩展HttpServletRequest的功能以通过编程方式设置和修改请求参数

无论是通过清理用户输入来增强安全性还是与第三方资源集成,这些技术都能帮助开发人员应对各种场景。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章