Java中HttpServletRequestWrapper的使用与原理详解
介绍
- 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使用与原理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
最新评论