Java中HttpServletRequestWrapper的使用与原理详解

 更新时间:2024年01月10日 08:47:19   作者:爱coding的同学  
这篇文章主要介绍了Java中HttpServletRequestWrapper的使用与原理详解,HttpServletRequestWrapper 实现了 HttpServletRequest 接口,可以让开发人员很方便的改造发送给 Servlet 的请求,需要的朋友可以参考下

介绍

  • HttpServletRequestWrapper 实现了 HttpServletRequest 接口,可以让开发人员很方便的改造发送给 Servlet 的请求.HttpServletRequest 对参数值的获取实际调的是org.apache.catalina.connector.Request没有提供对应的set方法修改属性所以不能对前端传来的参数进行修改,实际场所像过滤xss攻击,取认证token统一去除token前缀等需要进行请求参数的处理,此时HttpServletRequestWrapper 就应运而生了。
  • 应用了装饰模式.HttpServletRequestWrapper 采用装饰者模式对HttpServletRequest进行包装,我们可以通过继承HttpServletRequestWrapper 类去重写getParameterValues,getParameter等方法,实际还是调用HttpServletRequest的相对应方法,但是可以对方法的结果进行改装。
  • 一般要和 Filter 配合应用

应用场景

需要修改客户端请求参数的场合,例如

  • 将不支持的语言参数修改为默认语言
  • 将加密的 DeviceId 解密,并解析出其中的 imei 和 sn,同时在客户端请求里添加这 2 个参数 ** deviceId = hex(rc4(imei + ‘_’ + sn))

示例

就以上面所说的解密 DeviceId 为例

web.xml 配置

添加一个解析 DeviceId 的 Filter

<!-- 解析加密的 deviceId 得到 imei 和 sn -->
	<filter>
		<filter-name>deviceIdParseFilter</filter-name>
		<filter-class>com.xxxxxx.DeviceIdParseFilter</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
		<init-param>
			<param-name>forceEncoding</param-name>
			<param-value>true</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>deviceIdParseFilter</filter-name>
		<url-pattern>*.do</url-pattern>
	</filter-mapping>

Filter 代码

public class DeviceIdParseFilter implements Filter {
    private static final String KEY = "xxxxxxx";
    private static final Logger log = Logger.getLogger(DeviceIdParseFilter.class);
    private static final String[] DEFAULT_RESULT = {"",""};
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        String deviceId = request.getParameter("deviceId");
        if (deviceId != null && deviceId.length() > 0) {
            String[] result = parseDeviceId(deviceId);
            DeviceIdParseRequest req = new DeviceIdParseRequest((HttpServletRequest) request,
                    result[0], result[1]);
            chain.doFilter(req, response);
        } else {
            chain.doFilter(request, response);
        }
    }
    /**
     * 
     * 从 deviceId 里解析出 imei 和 sn
     * 
     * imei = result[0]
     * sn   = result[1]
     * 
     * 
     * @param deviceId
     * @return
     */
    private static final String[] parseDeviceId(String deviceId) {
        try {
            String src = Rc4Util.decrypt(deviceId, KEY);
            if (src.indexOf('_') >= 0) {
                return src.split("_");
            }
        } catch (Exception e) {
            log.error(e, e);
        }
        return DEFAULT_RESULT;
    }
    @Override
    public void destroy() {
    }
}

这个 Filter 会将包含 deviceId 参数的请求进行如下处理

  • 将 deviceId 的值用 RC4 进行解密
  • 从解密出来的 deviceId 里解析出 imei 和 sn
  • 将请求改造成 DeviceIdParseRequest,这就是我们的 HttpServletRequestWrapper
    而不包含 deviceId 参数的请求不做任何处理

HttpServletRequestWrapper 代码

public class DeviceIdParseRequest extends HttpServletRequestWrapper {
    private String imei;
    private String sn;
    /**
     * @param request
     */
    public DeviceIdParseRequest(HttpServletRequest request) {
        super(request);
        this.imei = "";
        this.sn = "";
    }
    /**
     * @param request
     * @param imei
     * @param sn
     */
    public DeviceIdParseRequest(HttpServletRequest request, String imei, String sn) {
        super(request);
        this.imei = imei;
        this.sn = sn;
    }
    @Override
    public String getParameter(String name) {
        if ("imei".equals(name)) {
            return imei;
        } else if ("sn".equals(name)) {
            return sn;
        } else {
            return super.getParameter(name);
        }
    }
    @Override
    public String[] getParameterValues(String name) {
        if ("imei".equals(name)) {
            return new String[] { imei };
        } else if ("sn".equals(name)) {
            return new String[] { sn };
        } else {
            return super.getParameterValues(name);
        }
    }
}

这里针对 imei 和 sn 进行了特殊处理,返回的不是客户端提交的参数,而是在 Filter 里通过解析 deviceId 得到的 imei 和 sn

需要注意的是

  • 如果用 request.getParameter() 获取客户端请求参数的值,那么只需要重写该方法就行了
  • 如果用 SpringMVC 的 @RequestParam 注解来获取请求参数的值,那么需要重写 getParameterValues 方法:因为 SpringMVC 是用这个方法来获取参数值的

运行结果

用于测试的 controller

这个测试类把接收到的参数直接返回

@Controller
@RequestMapping("/api/")
public class TestController {


    @ResponseBody
    @RequestMapping("test.do")
    public Result test(String deviceId, String imei, String sn) {
        Map<String, String> map = new HashMap<>();
        map.put("deviceId", deviceId);
        map.put("imei", imei);
        map.put("sn", sn);
        return new Result(map);
    }


}

请求参数不包含 deviceId.请求 url 如下:

http://xxxxx.in.xxxxx.com/api/test.do?reqno=123456&imei=imei&sn=1001&model=mx6&os=flyme6&ver=1.0.0&locale=en_US

返回结果

{
	"code": "200",
	"message": "",
	"redirect": "",
	"value": {
		"sn": "1001",
		"imei": "imei",
		"deviceId": null
	}
}

请求参数包含 deviceId。请求 url

http://xxxxxxx.com/api/test.do?reqno=123456&sn=1001&model=mx6&os=flyme6&ver=1.0.0&locale=en_US&deviceId=7cfbf5cbd70bcf1c006d7d0aa77688518444497a2b45683ea41ce690e92d6d38

返回结果

{

	"code": "200",
	"message": "",
	"redirect": "",
	"value": {
		"sn": "111",
		"imei": "org.testng.annotations.Test;",
		"deviceId": "7cfbf5cbd70bcf1c006d7d0aa77688518444497a2b45683ea41ce690e92d6d38"
	}
}

可以看到

  • 请求参数里不存在的 imei 能获取到值
  • 请求参数里存在的 sn 值被修改了

到此这篇关于Java中HttpServletRequestWrapper的使用与原理详解的文章就介绍到这了,更多相关HttpServletRequestWrapper使用与原理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 用Java生成二维码并附带文字信息

    用Java生成二维码并附带文字信息

    这篇文章主要介绍了用Java生成二维码并附带文字信息,文中有非常详细的代码示例,对正在学习java的小伙伴们有非常好的帮助,需要的朋友可以参考下
    2021-04-04
  • Java中outer标签的用法实例代码

    Java中outer标签的用法实例代码

    这篇文章主要介绍了Java中outer标签的用法,在这里需要大家注意这里的outer并不是关键字,而仅仅是一个标签,本文结合实例代码给大家详细讲解,需要的朋友可以参考下
    2023-01-01
  • 详解Java线程池如何统计线程空闲时间

    详解Java线程池如何统计线程空闲时间

    这篇文章主要和大家分享一个面试题:Java线程池是怎么统计线程空闲时间?文中的示例代码讲解详细,对我们掌握Java有一定帮助,需要的可以参考一下
    2022-11-11
  • 关于mybatis使用${}时sql注入的问题

    关于mybatis使用${}时sql注入的问题

    这篇文章主要介绍了关于mybatis使用${}时sql注入的问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • 一文带你解读Java序列化和反序列化机制

    一文带你解读Java序列化和反序列化机制

    序列化是将对象转换成二进制字节流的过程;反序列化是从二进制字节流中恢复对象的过程。本文将大家详细讲讲二者的原理与实现,希望对大家有所帮助
    2023-01-01
  • Java获取七牛云存储空间中图片外链

    Java获取七牛云存储空间中图片外链

    本文主要介绍了Java获取七牛云存储空间中图片外链,需要获取在七牛云中存储的所有图片,并返回外链地址,具有一定的参考价值,感兴趣的可以了解一下
    2023-10-10
  • Dubbo本地调试的几种方式总结

    Dubbo本地调试的几种方式总结

    dubbo服务方启动时需要加载的东西太多,如果跑单元测试把服务开启会浪费不少时间,而且单元测试没法保持服务一直开启的状态,这篇文章主要给大家介绍了关于Dubbo本地调试的几种方式,需要的朋友可以参考下
    2022-11-11
  • Spring Boot 2.0快速构建服务组件全步骤

    Spring Boot 2.0快速构建服务组件全步骤

    这篇文章主要给大家介绍了关于Spring Boot 2.0快速构建服务组件的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Spring Boot 2.0具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-04-04
  • 浅谈java中六大时间类的使用和区别

    浅谈java中六大时间类的使用和区别

    下面小编就为大家带来一篇浅谈java中六大时间类的使用和区别。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-01-01
  • Java中常见的语法糖分享

    Java中常见的语法糖分享

    Java语法糖是指Java编译器在编译Java源代码时所做的一些特殊处理,使得Java源代码在编译后生成的字节码更加简洁、易读、易维护,Java 中有许多常见的语法糖,本文给大家列举了一些常见的例子,需要的朋友可以参考下
    2023-10-10

最新评论