场景
- 修改请求中的参数,某字段加密(接口需要解密后的数据),此处统一解密
- 请求参数和响应结果,记录日志/存数据库
- 在请求参数中添加 额外字段(标识某些系统等)
OncePerRequestFilter
GET方式
getParameterMap(),只能够获取到GET请求的参数
//请求参数
parameterMap = httpRequest.getParameterMap();
//下为通用
//请求方式
String requestMethod = httpRequest.getMethod();
String remoteAddr = httpRequest.getRemoteAddr();
int remotePort = httpRequest.getRemotePort();
注意:异常 No modifications are allowed to a locked ParameterMap
//类 org.apache.catalina.util.ParameterMap
@Override
public V put(K key, V value) {
checkLocked();
return delegatedMap.put(key, value);
}
private void checkLocked() {
if (locked) {
throw new IllegalStateException(sm.getString("parameterMap.locked"));
}
}
//类 org.apache.tomcat.util.res.StringManager
public String getString(String key) {
if (key == null){
String msg = "key may not have a null value";
throw new IllegalArgumentException(msg);
}
String str = null;
try {
// Avoid NPE if bundle is null and treat it like an MRE
if (bundle != null) {
str = bundle.getString(key);
}
} catch (MissingResourceException mre) {
//bad: shouldn't mask an exception the following way:
// str = "[cannot find message associated with key '" + key +
// "' due to " + mre + "]";
// because it hides the fact that the String was missing
// from the calling code.
//good: could just throw the exception (or wrap it in another)
// but that would probably cause much havoc on existing
// code.
//better: consistent with container pattern to
// simply return null. Calling code can then do
// a null check.
str = null;
}
return str;
}
/*错误码对应文件 tomcat下 org.apache.catalina.util.LocalStrings.properties
PropertyResourceBundle bundle会在服务启动加载到内存
*/
parameterMap.locked=No modifications are allowed to a locked ParameterMap
//类 java.util.PropertyResourceBundle
private final Map<String,Object> lookup;
public PropertyResourceBundle (Reader reader) throws IOException {
Properties properties = new Properties();
properties.load(reader);
lookup = new HashMap(properties);
}
public Object handleGetObject(String key) {
if (key == null) {
throw new NullPointerException();
}
return lookup.get(key);
}
POST方式
POST的请求参数是在请求体body中,而body参数是以流形式存在的。
ServletInputStream inputStream = httpRequest.getInputStream();
InputStreamReader reader = new InputStreamReader(inputStream,StandardCharsets.UTF_8);
BufferedReader bfReader = new BufferedReader(reader);
StringBuilder sb = new StringBuilder();
String line;
while ((line = bfReader.readLine()) != null){
sb.append(line);
}
System.out.println(sb.toString());
注意:异常 request body missing
过滤器获得POST请求参数,但是controller层报错。
httpRequest.getInputStream() 只能使用一次,再次使用报错
InputStream read方法内部有一个postion标志。
当前流读取到的位置,每读取一次,位置就会移动一次,如果读到最后,InputStream.read方法会返回-1。
如果想再次读取,可调用inputstream.reset方法,position就会移动到上次调用mark的位置,mark默认是0,所以就能从头再读。
是否能reset又是由markSupported决定的,为true能reset,为false就不能reset。
从源码可以看到,markSupported是为false的,而且一调用reset就是直接异常。
需要将读取到的InputStream 保存下来,使用HttpServletRequestWrapper进行处理
继承并重写方法
@Component
@Slf4j
public class ParamFilter extends OncePerRequestFilter {
@Value("${app.self.name}")
private String selfName;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain chain) throws ServletException, IOException {
RequestParameterWrapper requestWrapper=null;
String method=request.getMethod();
if("GET".equalsIgnoreCase(method)){
try {
requestWrapper = new RequestParameterWrapper(request, RequestMethod.GET);
Map<String, String[]> parameterMap = new HashMap<>(requestWrapper.getParameterMap());
log.info("get 参数={}", parameterMap.toString());
parameterMap.put("selfName", new String[]{selfName});
requestWrapper.setParameterMap(parameterMap);
}catch (Exception e){
e.printStackTrace();
}
}else if("POST".equals(method)){
try {
requestWrapper = new RequestParameterWrapper(request, RequestMethod.POST);
//获取post消息体
String body = requestWrapper.getBody();
JSONObject jsonObject = new JSONObject(body);
String dataAuthCode = (String) jsonObject.get("selfName");
if (StringUtils.isNotBlank(dataAuthCode)) {
chain.doFilter(request, response);
} else {
jsonObject.put("selfName", selfName);
requestWrapper.setBody(jsonObject);
}
}catch (Exception e){
e.printStackTrace();
}
}
if(requestWrapper == null) {
chain.doFilter(request, response);
} else {
chain.doFilter(requestWrapper, response);
}
}
}
继承HttpServletRequestWrapper
public class RequestParameterWrapper extends HttpServletRequestWrapper {
private String body;
private Map<String, String[]> parameterMap;
public RequestParameterWrapper(HttpServletRequest request, RequestMethod method) throws Exception{
super(request);
if(RequestMethod.POST.equals(method)){
StringBuilder stringBuilder = new StringBuilder();
try(BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(request.getInputStream(),"UTF-8"));) {
char[] charBuffer = new char[128];
int bytesRead;
while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
stringBuilder.append(charBuffer, 0, bytesRead);
}
} catch (Exception ex) {
throw ex;
}
body = stringBuilder.toString();
}else if(RequestMethod.GET.equals(method)){
parameterMap=request.getParameterMap();
}
}
public String getBody() {
return this.body;
}
/**
* post方式使用
* @param object
*/
public void setBody(JSONObject object) {
this.body=object.toString();
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
public int read() throws IOException {
return byteArrayInputStream.read();
}
};
}
@Override
public Enumeration<String> getParameterNames() {
Vector<String> vector = new Vector<String>(parameterMap.keySet());
return vector.elements();
}
@Override
public String getParameter(String name) {
String[] results = parameterMap.get(name);
return results[0];
}
@Override
public Map<String, String[]> getParameterMap() {
return parameterMap;
}
@Override
public String[] getParameterValues(String name) {
return parameterMap.get(name);
}
public void setParameterMap(Map<String, String[]> parameterMap) {
this.parameterMap = parameterMap;
}
}