利用Filter和HttpServletRequestWrapper实现请求体中token校验
先说一下项目的背景,系统传参为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校验的更多相关文章
- springboot请求体中的流只能读取一次的问题
场景交代 在springboot中添加拦截器进行权限拦截时,需要获取请求参数进行验证.当参数在url后面时(queryString)获取参数进行验证之后程序正常运行.但是,当请求参数在请求体中的时候, ...
- 如何对POST请求但是URL中也有参数/GET请求但是请求体中也有参数的情况进行安全扫描
通常情况下,GET的参数都在URL中,POST的参数都在请求体中,但是如题的情况也有,像使用方法PUT.DELETE的情况也有,这些情况该如何进行安全扫描呢?
- 利用Filter解决跨域请求的问题
1.为什么出现跨域. 很简单的一句解释,A系统中使用ajax调用B系统中的接口,此时就是一个典型的跨域问题,此时浏览器会出现以下错误信息,此处使用的是chrome浏览器. 错误信息如下: jquery ...
- 微信小程序:post请求参数放在请求体中还是拼接到URL中需要看后台是如何接收的
前端发送post请求时,请求参数可以放在请求中,代码如下: function post(url, data, callback) { wx.request({ method: 'POST', url: ...
- javaweb利用filter拦截未授权请求
项目上有个小需求,要限制访问者的IP,屏蔽未授权的请求.该场景使用过滤器来做再合适不过了. SecurityFilter.java: public class SecurityFilter imple ...
- 请求体中需要的true和requests包put请求冲突了
python put请求,添加请求头 不知道怎么解决
- 使用restTemplate发送post请求,传入参数是在requestBody请求体中,以json形式传输
@PostMapping public ResponseResult add(User user){ HttpHeaders httpHeaders = new HttpHeaders(); Medi ...
- Django中获取参数(路径,查询,请求头,请求体)
一.通常HTTP协议向服务器传参有几种途径 : 提取URL的特定部分,如/weather/shanghai/2018,可以在服务器端的路由中用正则表达式截取: 查询字符串(query string), ...
- HTTP请求头中各字段解释
Accept : 浏览器(或者其他基于HTTP的客户端程序)可以接收的内容类型(Content-types),例如 Accept: text/plain Accept-Charset:浏览器能识别的字 ...
随机推荐
- Linux单独打包工具-Ubuntu
Electron-Packager 使用electron-packager打包:https://github.com/electron/electron-packagerelectron-packag ...
- Path.Combine Method
https://docs.microsoft.com/en-us/dotnet/api/system.io.path.combine?view=netframework-4.8#System_IO_P ...
- linux 之oracle静默安装
一.安装前准备工作1.修改主机名#vi /etc/hosts //并添加内网IP地址对应的hostname,如下127.0.0.1 localhost::1 ...
- mysql中的sql-mode导致的datetime类型字段不能为0000
问题描述: 在执行建表语句的时候,出现invalid default datetime value '0000-00-00 00:00:00',从字面意思看,就是不合法的默认值'0000-00-00 ...
- Carve ifc failed
Detected IFC version: IFC2X3 Warning: Sweeper::createTriangulated3DFace, carve::triangulate::incorpo ...
- linux下nginx结合keepalived实现主从切换的配置
linux下nginx结合keepalived实现主从切换的配置 解决方法: 实现一个主nginx宕机,请求转到另一个nginx中. 1.确保两台nginx已启动,假如端口分别是192.168.0 ...
- CentOS7下搭建zabbix监控(一)——Zabbix监控端配置
zabbix 是一个基于 WEB 界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案.zabbix 能监视各种网络参数,保证服务器系统的安全运营:并提供灵活的通知机制以让系统管理员快速定位 ...
- 123457123457#1#-----com.threeapp.ErTongHuaXue01----儿童滑雪大冒险
123456123456#0#-----com.threeapp.ErTongHuaXue01----儿童滑雪大冒险
- java cpu 使用率100%
--宝典开始 top :查看 进程 ,选CPU使用率高的 获取进程ID,pid top -Hp pid:查看线程,选CPU使用率高的 获取线程ID,threadid printf "%X\n ...
- 查看php 某个服务的进程数
查看进程就是使用ps命令而已,只不顾ps的参数太多了. 使用php查询的话,必须要开启几个函数(可以执行外部程序的函数),参考官网:http://php.net/manual/zh/book.exec ...