利用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:浏览器能识别的字 ...
随机推荐
- Sentinel 快速入门
Sentinel 简介 什么是 Sentinel? 『Sentinel』是阿里中间件团队开源的,面向分布式服务架构的轻量级高可用流量控制组件,主要以流量为切入点,从流量控制.熔断降级.系统负载保护等多 ...
- Python 中路径的有效使用
import arcpy arcpy.GetCount_management("c:/temp/streams.shp") arcpy.GetCount_management(&q ...
- Cross-Site Request Forgery (CSRF)
https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF) Overview Cross-Site Request Forger ...
- VM 15 永久激活密钥
VMware Workstation 15 永久激活密钥 YG5H2-ANZ0H-M8ERY-TXZZZ-YKRV8 UG5J2-0ME12-M89WY-NPWXX-WQH88 UA5 ...
- windows7 10 windows2008 windws2012 nfs客户端的安装
servermanagercmd.exe -install FS-NFS-Services for windows2008 windows2012 在添加功能角色-添加文件打印服务选择nfs客户端 ...
- Oracle 中的 Profile
一.目的: Oracle系统中的profile可以用来对用户所能使用的数据库资源进行限制,使用Create Profile命令创建一个Profile,用它来实现对数据库资源的限制使用,如果把该prof ...
- 手把手教你MyEclipseUML建模(上)
手把手教你MyEclipseUML建模(上) 转 https://blog.csdn.net/qq_37939251/article/details/83444359 1.用UML 1建模 MyEcl ...
- Mysql读写分离(Mycat版)
(1).读写分离概述 1)工作原理 读写分离是让主数据库处理事务性增删改操作(insert.delete.update),让从数据库处理查询查询操作(select). 2)作用 1.分担负载 2.主从 ...
- JSONP实现Ajax跨域请求
前言 由于浏览器存在同源策略的机制,所谓同源策略就是阻止从一个源(域名,包括同一个根域名下的不同二级域名)加载的文档或者脚本获取/或者设置另一个源加载的文档属性. 但比较特别的是:由于同源策略是浏览器 ...
- SAP R3和SAP Business One的区别
SAP R3是SAP开发的 开发语言是ABAP. 之前叫SAP R/2 然后叫R/3 后又改叫ECC 现在叫A1了. 现在有新的版本S4 HANA : SAP发展史 SAP Business One是 ...