先说一下项目的背景,系统传参为json格式,token为其中一个必传参数,此时如果在过滤器中直接读取request,则后续controller中通过RequestBody注解封装请求参数是会报stream closed异常,一位InputStream是一个基础流,只能被读取一次。代码如下:

package com.hellobike.scm.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Strings;
import com.hellobike.basic.model.sys.ScmUser;
import com.hellobike.basic.util.Utils;
import com.hellobike.scm.conf.UserThread;
import com.hellobike.scm.service.RedisCacheService;
import com.hellobike.scm.service.RedisService;
import com.hellobike.scm.util.LoginUserUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver; import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
import java.util.Set; public class AuthFilter implements Filter { private static final Logger logger = LoggerFactory.getLogger(AuthFilter.class); @Autowired
private RedisCacheService redisCacheService; @Override
public void init(FilterConfig filterConfig) throws ServletException {
SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, filterConfig.getServletContext());
} @Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String method = httpRequest.getMethod();
httpResponse.setHeader("Access-Control-Allow-Origin", "*");
httpResponse.setContentType("application/json; charset=utf-8");
httpResponse.setHeader("pragma", "no-cache");
httpResponse.setHeader("cache-control", "no-cache");
LoginUserUtil.removeCurrentUserInfo(); String requestUrl = httpRequest.getRequestURI();
//|| requestUrl.contains("/systemConfig/getUpgradeInfo")
if (requestUrl.contains(".css") || requestUrl.contains(".js") || requestUrl.contains(".png")
|| requestUrl.contains(".jpg") || requestUrl.contains("/execption/")||requestUrl.contains("/sys/main/login") ||requestUrl.contains("/sys/main/checkCode")) {
// 如果发现是css或者js文件,直接放行
filterChain.doFilter(request, response);
return;
}
String userAgent = httpRequest.getHeader("User-Agent");
String reqCxtType = httpRequest.getContentType();
boolean isJsonType = shouldLog(reqCxtType);
String token = null;
Map<String, String[]> param = request.getParameterMap();
if (param.containsKey("token")) {
token = param.get("token")[0];
logger.info("authFilter中get请求token值为{}"+token);
}
if (token == null) {
String body = null;
// 再从contentType取token
if (isJsonType) {
httpRequest = new RequestWrapper((HttpServletRequest) request);
body = ((RequestWrapper) httpRequest).getBody();
JSONObject params = JSON.parseObject(body);
logger.info("post param is {}", body);
if (params == null) {
request.getRequestDispatcher("/admin/execption/tokenError").forward(request, response);
return;
}
token = params.getString("token");
}
}
// 最后从form_data里面取token
if (null == token) {
if (request.getContentType() != null && request.getContentType().contains("multipart/form")) {
MultipartHttpServletRequest multiReq = null;
try {
multiReq = new CommonsMultipartResolver().resolveMultipart(httpRequest);
token = multiReq.getParameter("token");
logger.info("multiReq token=" + token);
// request = multiReq;
} catch (Exception e) {
logger.info("MultipartHttpServletRequest异常");
e.printStackTrace();
}
}
}
if (isJsonType && !tokenAvailable(token, userAgent)) {
request.getRequestDispatcher("/admin/execption/tokenError").forward(request, response);
return;
}
if (!tokenAvailable(token, userAgent)) {
request.getRequestDispatcher("/admin/execption/tokenError").forward(request, response);
return;
}
// url权限校验
ScmUser adminUser = JSON.parseObject(redisCacheService.getToken(token),ScmUser.class);
UserThread.setValue(adminUser);
String userName = null;
if (adminUser != null) {
userName=adminUser.getUserName();
LoginUserUtil.setCurrentUserName(userName);
}
//判断是否系统升级
if(upgrade(userName,requestUrl)){
request.getRequestDispatcher("/systemConfig/getUpgradeInfo").forward(httpRequest, response);
return;
} logger.info("data的值{}", userName);
String permiCode = (String) redisCacheService.getUserPermission(userName, requestUrl);
logger.info("requestUrl={},permiCode={}", requestUrl, permiCode);
if (permiCode != null && "None".equals(permiCode)) {
// returnTokenError(httpResponse,ErrorCode.UNAUTHORIZED);
request.getRequestDispatcher("/admin/execption/unauthorized").forward(request, response);
return;
}
filterChain.doFilter(httpRequest, httpResponse);
} private boolean upgrade(String userName,String requestUrl){
Set<String> userNames = redisCacheService.get("scm_upgrade_tag" , Set.class);
if (userNames != null&& (!requestUrl.equals("/systemConfig/getUpgradeInfo")) &&(!userNames.contains(userName))) {
return true;
}
return false;
} private boolean tokenAvailable(String token, String userAgent) {
if (Strings.isNullOrEmpty(token)) {
return false;
}
try {
String data = redisCacheService.getToken(token);
if (Utils.isEmpty(data)) {
return false;
}
redisCacheService.set(token, data, 1200);
} catch (Exception e) {
logger.info("校验token失败");
}
return true;
} @Override
public void destroy() { } private boolean shouldLog(String contentType) {
String jsonType = "application/json"; if (contentType == null) {
return false;
}
String[] cells = contentType.split(";");
for (String cell : cells) {
if (cell.trim().toLowerCase().equals(jsonType)) {
return true;
}
} return false;
}
}
package com.hellobike.manage.flter;

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.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map; public class RequestWrapper extends HttpServletRequestWrapper {
private final String body;
public RequestWrapper(HttpServletRequest request) throws IOException {
super(request);
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = null;
try {
InputStream inputStream = request.getInputStream();
if (inputStream != null) {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
char[] charBuffer = new char[128];
int bytesRead = -1;
while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
stringBuilder.append(charBuffer, 0, bytesRead);
}
} else {
stringBuilder.append("");
}
} catch (IOException ex) {
throw ex;
} finally {
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException ex) {
throw ex;
}
}
}
body = stringBuilder.toString();
} @Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
ServletInputStream servletInputStream = 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();
}
};
return servletInputStream;
} @Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
} public String getBody() {
return this.body;
} private Map<String , String[]> params = new HashMap<String, String[]>(); public void setParameter(String name, Object value) {
if (value != null) {
System.out.println(value);
if (value instanceof String[]) {
params.put(name, (String[]) value);
} else if (value instanceof String) {
params.put(name, new String[]{(String) value});
} else {
params.put(name, new String[]{String.valueOf(value)});
}
}
} @Override
public String getParameter(String name) {
String[]values = params.get(name);
if(values == null || values.length == 0) {
return null;
}
return values[0];
} }

  下面说一下我遇到的坑,系统要做一个简单的系统升级界面,此时用户不可访问,因此做了个请求转发,在springboot1.5.1版本中,FilterRegistrationBean默认会拦截转发的请求,此时request.getRequestDispatcher("/systemConfig/getUpgradeInfo").forward(request, response)会被再次拦截,因为request中流已被读取,所以此时RequestWrapper会报stream closed。在springboot2.0.4版本中,FilterRegistrationBean默认不会拦截转发(FORWARD)类型的请求,所以不会报stream closed,所以第一个坑就是FilterRegistrationBean默认的拦截类型的设置。修改转发方法传参为httpRequest,即使用RequestWrapper封装后的httpRquest,转发方式变为request.getRequestDispatcher("/systemConfig/getUpgradeInfo").forward(httpRquest, response),此时在配置FilterRegistrationBean拦截转发(FORWARD)类型的请求后,会出现无限循环的现象,一直转发下去,这也是第二个坑,因此我们要在转发是否的判断条件是将我们要转发的URL排除在外,即

requestUrl.equals("/systemConfig/getUpgradeInfo")不再进行转发。

  问题虽然简单,但是前前后后还是浪费不少时间,特记录一下

利用Filter和HttpServletRequestWrapper实现请求体中token校验的更多相关文章

  1. springboot请求体中的流只能读取一次的问题

    场景交代 在springboot中添加拦截器进行权限拦截时,需要获取请求参数进行验证.当参数在url后面时(queryString)获取参数进行验证之后程序正常运行.但是,当请求参数在请求体中的时候, ...

  2. 如何对POST请求但是URL中也有参数/GET请求但是请求体中也有参数的情况进行安全扫描

    通常情况下,GET的参数都在URL中,POST的参数都在请求体中,但是如题的情况也有,像使用方法PUT.DELETE的情况也有,这些情况该如何进行安全扫描呢?

  3. 利用Filter解决跨域请求的问题

    1.为什么出现跨域. 很简单的一句解释,A系统中使用ajax调用B系统中的接口,此时就是一个典型的跨域问题,此时浏览器会出现以下错误信息,此处使用的是chrome浏览器. 错误信息如下: jquery ...

  4. 微信小程序:post请求参数放在请求体中还是拼接到URL中需要看后台是如何接收的

    前端发送post请求时,请求参数可以放在请求中,代码如下: function post(url, data, callback) { wx.request({ method: 'POST', url: ...

  5. javaweb利用filter拦截未授权请求

    项目上有个小需求,要限制访问者的IP,屏蔽未授权的请求.该场景使用过滤器来做再合适不过了. SecurityFilter.java: public class SecurityFilter imple ...

  6. 请求体中需要的true和requests包put请求冲突了

    python  put请求,添加请求头 不知道怎么解决

  7. 使用restTemplate发送post请求,传入参数是在requestBody请求体中,以json形式传输

    @PostMapping public ResponseResult add(User user){ HttpHeaders httpHeaders = new HttpHeaders(); Medi ...

  8. Django中获取参数(路径,查询,请求头,请求体)

    一.通常HTTP协议向服务器传参有几种途径 : 提取URL的特定部分,如/weather/shanghai/2018,可以在服务器端的路由中用正则表达式截取: 查询字符串(query string), ...

  9. HTTP请求头中各字段解释

    Accept : 浏览器(或者其他基于HTTP的客户端程序)可以接收的内容类型(Content-types),例如 Accept: text/plain Accept-Charset:浏览器能识别的字 ...

随机推荐

  1. 009-多线程-JUC集合-Queue-LinkedBlockingDeque

    一.概述 LinkedBlockingDeque是双向链表实现的双向并发阻塞队列.该阻塞队列同时支持FIFO和FILO两种操作方式,即可以从队列的头和尾同时操作(插入/删除):并且,该阻塞队列是支持线 ...

  2. myeclipse打开jsp后卡死的问题详解

    myeclipse打开jsp后卡死的问题详解   1,打开 Window -> Preferences -> General -> Editors -> File Associ ...

  3. List根据多个字段分组

    List<ClassEntity> distinctClass = classEntities.stream().collect(Collectors.collectingAndThen( ...

  4. OO ALV事件里使用E消息,下一步会退出到系统初始界面

    在OO ALV  data_change事件时(选中行),锁定KEY值, 继续,取消选择,退出到系统初始界面 改成 pv_status = 'E'. pv_msg = '采购订单' && ...

  5. WPF引入OCX控件

    (方法一) https://www.cnblogs.com/guaniu/archive/2013/04/07/3006445.html (方法二) 1.先注册OCX控件:(有的把OCX 控件封装到E ...

  6. Python3之高阶函数sorted

    排序算法 Python内置的sorted()函数可以对list进行排序 >>> sorted([36,5,-12,9,-21]) [-21, -12, 5, 9, 36] 此外,so ...

  7. Linux - 对比net-tools与iproute2

    简介 net-tools包含ifconfig.route.arp和netstat等命令行工具,用于管理和排查各种网络配置. 起源于BSD TCP/IP工具箱,旨在配置老式Linux内核的网络功能. 自 ...

  8. iOS-UIView的layoutSubviews和drawRect方法何时调用(转)

    转自:http://jianyu996.blog.163.com/blog/static/112114555201305113018814/ 首先两个方法都是异步执行.layoutSubviews方便 ...

  9. Docker 跨主机网络 overlay(十六)

    目录 一.跨主机网络概述 二.准备 overlay 环境 1.环境描述 2.创建 consul 3.修改 docker 配置文件 4.准备就绪 三.创建 overlay 网络 1.在 host1 中创 ...

  10. Object.defineProperty()方法学习笔记

    这是js中一个非常重要的方法,ES6中某些方法的实现依赖于它,VUE通过它实现双向绑定 此方法会直接在一个对象上定义一个新属性,或者修改一个已经存在的属性, 并返回这个对象 参数 Object.def ...