HttpServletRequestWrapper的应用

    最近的一个项目前后端分离,然后后端自定义Filter,通过继承org.springframework.web.filter.OncePerRequestFilter实现在请求到达Controller层之前进行统一的参数验签(MD5+盐)和token验证。其中,在进行验签时,需要获取body中的JSON数据,这时想着直接通过HttpServletRequest#getInputStream() 获取到请求的输入流,从该输入流中可以读取到请求体。但这里就有个坑了,这个流在被我们的代码 read 过后,之后的代码就会报错,因为流已经被我们读取过了 , 尝试使用 mark() , reset() 也是不行的,会抛出异常,这时,可以通过将 HttpServletRequest 对象包装一层的方式来实现这个功能。Servlet规范中也有相关说明:IO都是只能读取一次的,除非做了拷贝!也就是做了缓存!一个重要的概念就是Scope与Single Thread Model。

一. HttpServletRequestWrapper
通过继承HttpServletRequestWrapper类以实现在Filter中修改HttpServletRequest的参数。

  • UML结构图
    在这里插入图片描述
    这里其实是应用了装饰器模式对HttpServletRequest进行了增强。
  • ServletRequest 抽象组件
  • HttpServletRequest 抽象组件的一个子类,它的实例被称作“被装饰者”
  • ServletRequestWrapper 一个基本的装饰类,这里是非抽象的
  • HttpServletRequestWrapper 一个具体的装饰者,当然这里也继承了HttpServletRequest这个接口,是为了获取一些在ServletRequest中没有的方法

二.代码实现

  • 自定义Wrapper
package com.hong.security.common;

import org.springframework.util.StreamUtils;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * @author wanghong
 * @date 2020/05/11 22:47
 **/
public class WrappedRequest extends HttpServletRequestWrapper {

    private byte[] requestBody = null;

    public WrappedRequest(HttpServletRequest request) {
        super(request);
        // 缓存请求body
        try {
            requestBody = StreamUtils.copyToByteArray(request.getInputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        if (requestBody == null) {
            requestBody = new byte[0];
        }
        final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);
        return new ServletInputStream() {
            @Override
            public int read() throws IOException {
                return bais.read();
            }

            @Override
            public boolean isFinished() {
                // TODO Auto-generated method stub
                return true;
            }

            @Override
            public boolean isReady() {
                // TODO Auto-generated method stub
                return true;
            }

            @Override
            public void setReadListener(ReadListener listener) {
                // TODO Auto-generated method stub
            }
        };
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }
}
  • Filter中获取请求体数据
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

	@Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("JwtAuthenticationTokenFilter Begin");
			
	      ServletRequest requestWrapper = new WrappedRequest(request);
	      String requestParams = readJSONString(requestWrapper);
	      // 。。。		
	}
	
    public String readJSONString(ServletRequest request) {
        StringBuffer json = new StringBuffer();
        String line = null;
        try {
            BufferedReader reader = request.getReader();
            while ((line = reader.readLine()) != null) {
                json.append(StringUtils.trim(line));
            }
        } catch (Exception e) {
            log.info("readJSONString方法异常:[{}]", request, e);
        }
        return json.toString();
    }
}

三. 修改HttpServletRequest的参数或请求头

package com.hong.security.common;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.util.Enumeration;
import java.util.Map;
import java.util.Vector;

/**
 * @author wanghong
 * @date 2020/05/19 10:50
 * 继承HttpServletRequestWrapper,创建装饰类,以达到修改HttpServletRequest参数的目的
 **/
public class ModifyParametersWrapper extends HttpServletRequestWrapper {

    private Map<String, String[]> parameterMap; // 所有参数的Map集合

    public ModifyParametersWrapper(HttpServletRequest request) {
        super(request);
        parameterMap = request.getParameterMap();
    }

    // 重写几个HttpServletRequestWrapper中的方法

    /**
     * 获取所有参数名
     *
     * @return 返回所有参数名
     */
    @Override
    public Enumeration<String> getParameterNames() {
        Vector<String> vector = new Vector<>(parameterMap.keySet());
        return vector.elements();
    }

    /**
     * 获取指定参数名的值,如果有重复的参数名,则返回第一个的值 接收一般变量 ,如text类型
     *
     * @param name 指定参数名
     * @return 指定参数名的值
     */
    @Override
    public String getParameter(String name) {
        String[] results = parameterMap.get(name);
        if (results == null || results.length <= 0)
            return null;
        else {
            System.out.println("before modify: " + results[0]);
            return modify(results[0]);
        }
    }

    /**
     * 获取指定参数名的所有值的数组,如:checkbox的所有数据
     * 接收数组变量 ,如checkbox类型
     */
    @Override
    public String[] getParameterValues(String name) {
        String[] results = parameterMap.get(name);
        if (results == null || results.length <= 0)
            return null;
        else {
            int length = results.length;
            for (int i = 0; i < length; i++) {
                System.out.println("before modify2: " + results[i]);
                results[i] = modify(results[i]);
            }
            return results;
        }
    }

    /**
     * 自定义的一个简单修改原参数的方法,即:给原来的参数值前面添加了一个修改标志的字符串
     *
     * @param string 原参数值
     * @return 修改之后的值
     */
    private String modify(String string) {
        return "Modified: " + string;
    }
}
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
	 @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("JwtAuthenticationTokenFilter Begin");
		  // @RequestParam参数修改示例
        ModifyParametersWrapper mParametersWrapper = new ModifyParametersWrapper(request);
        filterChain.doFilter(mParametersWrapper, response);
	}
}
package com.hong.security.controller;

import com.hong.security.common.Result;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

/**
 * @author wanghong
 * @date 2020/05/12 17:27
 * 个人账号
 **/
@RestController
@RequestMapping("/account")
public class AccountController {
    @PostMapping("/modify")
    public void modify(@RequestParam("name") String name) {
        System.out.println("到达Controller层,after modify:" + name);
    }
}

在这里插入图片描述
在这里插入图片描述
参考:https://blog.51cto.com/983836259/1877592
https://www.jianshu.com/p/a8c9d45775ea
https://juejin.im/post/5e6aee11e51d452703137ce6

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章