web过滤器中获取请求的参数(content-type:multipart/form-data)防止csrf攻击

标签: 防止csrf攻击
21人阅读 评论(0) 收藏 举报
分类:

1.前言:

  1.1 在使用springMVC中,需要在过滤器中获取请求中的参数token,根据token判断请求是否合法;

  1.2 通过requst.getParameter(key)方法获得参数值;

    这种方法有缺陷:它只能获取  POST 提交方式中的Content-Type: application/x-www-form-urlencoded;

        HttpServletRequest request= (HttpServletRequest) req;
        String param = request.getParameter("param");

 

    

2.问题:

  在一般的请求中,content-type为:application/x-www-form-urlencoded;在此种请求中,使用request.getParam(key)方法可以获取到key对应的属性值;

  因为最近涉及到文件的上传操作,上传文件的请求中content-type为:multipart/form-data;此种请求无法直接用request.getParam(key)获取对应的属性值,request中获取的属性值全部为空,无法正常获取;

 

3.问题描述:

  3.1 在web.xml中配置的过滤器

复制代码
  <filter>
    <filter-name>requestFilter</filter-name>
    <filter-class>util.web.RequestFilter</filter-class>
  </filter>

  <filter-mapping>
    <filter-name>requestFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
复制代码

 

  

  3.2  过滤器RequestFilter.java中获取token做匹配

    在如下过滤器中,上传文件中的content-type:multipart/form-data使用获取request.getParameter(key)无法获取相应的值。需要借助Spring框架中的CommonsMultipartResolver.resolveMultipart(HttpServletRequest request)将request转为MultipartHttpServletRequest,从而使用getParameter(key)方法获取指定的值;

    在将对象转化完成后,要将转化完成的对象赋值给过滤链中的request参数中,即如下代码中的  req = multiReq; 赋值完成很重要,否则在controller层中依旧无法获取其他参数。

 

    如果不需要再filter中获取请求中的值,则无需如下的操作,在请求经过springMVC框架后,自动会识别请求方式,如果是文件请求,会自动调用CommonsMultipartResolver.resolveMultipart(HttpServletRequest request)方法转化;

复制代码
package util.web;public class RequestFilter implements Filter {
    protected FilterConfig filterConfig;
    protected boolean filterEnabled;
    protected int logLevel;
    protected boolean needVCode;
    protected List<String> noFilerList =null;
    private CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
    public RequestFilter() {
        this.filterConfig = null;
        this.filterEnabled = true;
        this.logLevel = -1;
    }
        
    @Override
    public void destroy() {
        // TODO Auto-generated method stub
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp,
            FilterChain chain) throws IOException, ServletException {
        if(this.filterEnabled){
            HttpServletRequest httpReq = (HttpServletRequest)req;
            HttpServletResponse httpResp = (HttpServletResponse)resp;
            String ctxPath = httpReq.getContextPath(); 
            String requestUri = httpReq.getRequestURI();        //请求的全路径,比如:         
            String uri = requestUri.substring(ctxPath.length());//全路径除去ctxPath
            String tarUri = uri.trim();    
            String operatorHtmlModel = (httpReq.getHeader("referer")!=null?httpReq.getHeader("referer"):"").trim(); //获取当前页面的url,判断url是否是后台而url(后台的html就两个)
            //不在过滤列表里的url请求,过滤列表包括t_sys_filter表中数据及visitor角色用户下的授权页面
            if(!this.isInNoFilerList(tarUri)){
                UserInfo uInfo = SessionUtil.getCurrentUser();
                LoginAccount regAccout=SessionUtil.getCurrentPlateLoginAccount();
                int type = 0 ;//平台账号未登录
                if(operatorHtmlModel.endsWith(Const.LOGIN_TYPE_HTML[0])||operatorHtmlModel.endsWith(Const.LOGIN_TYPE_HTML[1])){
                     type = 1;//后台账号未登录
                }
            //    int type = (SessionUtil.getAttribute(Const.LOGIN_TYPE)!=null)?Integer.valueOf(SessionUtil.getAttribute(Const.LOGIN_TYPE).toString()):0;
            if(regAccout==null){//平台账号未登录
                if(tarUri.endsWith(".do")){
                    httpResp.sendRedirect(ctxPath+"/"+Const.TIMEOUT_SERVICE);
                    return;                                
                }else if(tarUri.endsWith("/")){
                    httpResp.sendRedirect(ctxPath+"/"+Const.INDEX_PAGE);
                    return;
                }
            }else{//平台账号登录
                if(tarUri.endsWith("/")){
                    httpResp.sendRedirect(ctxPath+"/error/noSecurity.htm");
                    return;
                }else if(tarUri.endsWith(".do") && !isWithoutUri(tarUri)){
                    String contentType = httpReq.getContentType();//获取请求的content-type
                    String post_csrftoken = "";
                    if(contentType.contains("multipart/form-data")){//文件上传请求 *特殊请求
              /*
                
CommonsMultipartResolver 是spring框架中自带的类,使用multipartResolver.resolveMultipart(final HttpServletRequest request)方法可以将request转化为MultipartHttpServletRequest
                使用MultipartHttpServletRequest对象可以使用getParameter(key)获取对应属性的值
              */
                        MultipartHttpServletRequest multiReq = multipartResolver.resolveMultipart(httpReq);
                        post_csrftoken=multiReq.getParameter(Const.SESSION_CSRFTOKEN);//获取参数中的token
                        req = multiReq;//将转化后的reuqest赋值到过滤链中的参数 *重要
                    }else{//非文件上传请求
                        post_csrftoken=httpReq.getParameter(Const.SESSION_CSRFTOKEN);//获取参数中的token
                    }
                    //csrf防御:判断是否带token
                    //post_csrftoken=httpReq.getParameter(Const.SESSION_CSRFTOKEN);
                    String csrftoken=(String)SessionUtil.getAttribute(Const.SESSION_CSRFTOKEN);
                    if(post_csrftoken==null || !csrftoken.equals(post_csrftoken)){
                        //判断为不安全的访问
                        httpResp.sendRedirect(ctxPath+"/common/goNoSecurity.do");
                        return;
                    }
                }
            }
            // 设定网页的到期时间,一旦过期则必须到服务器上重新调用
            httpResp.setDateHeader("Expires", -1);
            // Cache-Control 指定请求和响应应遵循的缓存机制 no-cache指示请求或响应消息是不能缓存的
            httpResp.setHeader("Cache-Control", "no-cache");
            // 用于设定禁止浏览器从本地缓存中调用页面内容,设定后一旦离开页面就无法从Cache中再调出
            httpResp.setHeader("Pragma", "no-cache");
            }        
        chain.doFilter(req, resp);
        }
    }

    
    @SuppressWarnings("unchecked")
    @Override
    public void init(FilterConfig cfg) throws ServletException {
        //初始化操作
    }

    
    private Boolean isWithoutUri(String tarUri){
        String[] withoutUriStrings = {//无需匹配token的请求
                "/common/goNoSecurity.do",
                "/plateFormCommon/isLoginForPlateForm.do",
                "/supplierForPlateForm/getCompanyListByRegId.do"
                /*,"/PfTaskFileCtrl/addOrUpdateTaskImgFile1.do"*/
                /*,"/PfTaskFileCtrl/addOrUpdateTaskImgFileForUpdate.do"*/
                };
        
        for(String uri:withoutUriStrings){
            if(uri.equals(tarUri)){
                return true;
            }
        }
        return false;
    }
    
}
复制代码

 

 

  3.3 控制层 PfTaskFileCtrl.java

  如果在filter中未将转化的request值赋值给过滤链,则在这里无法获取fileType对应的值;

复制代码
package platform.common.controller;/**    
 * mogodb文件上传下载
 * 项目名称:outsideeasy    
 * 类名称:PfTaskFileCtrl    
 * 创建人:mishengliang    
 * 创建时间:2016-4-26 下午1:55:19    
 * 修改人:mishengliang    
 * 修改时间:2016-4-26 下午1:55:19    
 * @version     
 *     
 */
@Controller
@RequestMapping("PfTaskFileCtrl")
public class PfTaskFileCtrl {

    @Autowired
    private PfRegisterAttchedService registerAttchedService;
    @Autowired
    private FileOptService fileService;
    @Autowired
    private PfUpdateRegisterAttchedService updateRegisterAttchedService;
    @Autowired
    private CompanyForPlateFormService companyForPlateFormService;
    
    @RequestMapping(value = { "/companyImageView" }, method = { RequestMethod.GET })
    public ModelAndView gojsp_companyImageView(ModelAndView modelAndView ){
        modelAndView.setViewName("/companyWindow/companyImageView");
        return modelAndView;
    }
    
    /**
     * @Description:企业文件上传
     * PfTaskFileCtrl
     * addOrUpdateTaskImgFile1
     * @param request
     * @param response
     * @return
     * @throws Exception String
     * @author yukai
     * 2016-8-4 上午10:03:00
     */
    @DocLogger(explain="企业文件上传")
    @RequestMapping(value="addOrUpdateTaskImgFile1",method=RequestMethod.POST)
    @ResponseBody 
    public String addOrUpdateTaskImgFile1(HttpServletRequest request,HttpServletResponse response) throws Exception{
        Map<String,Object> qryParam = WebUtil.getDefaultParamsMap(request);
        LoginAccount regAccount = SessionUtil.getCurrentPlateLoginAccount();
        Map<String,Object> params=new HashMap<String, Object>();
        JSONObject json = new JSONObject();
        json.put("success", true);
        /*
         * 1.检查参数
         */
        if(regAccount == null){//获取任务id
            json.put("message", "未登录");
            return json.toString() ;
        }
     // *如果在filter中未将转化的request值赋值给过滤链,则在这里无法获取fileType对应的值
if(WebUtil.isEmpty(request.getParameter("fileType"))){//获取任务id json.put("message", "没有文件类型值"); return json.toString() ; } /* *2.赋值 */ int fileType = Integer.parseInt(request.getParameter("fileType")); String fileName = request.getParameter("fileName"); String formatType = request.getParameter("formatType"); PfRegisterAttched pfRegisterAttched = new PfRegisterAttched(); pfRegisterAttched.setFile_type_id(fileType);//文件类型值 pfRegisterAttched.setFile_name(fileName);//文件名 if(fileName.indexOf(",") != -1){ json.put("message", "文件名中存在非法字符(英文逗号),请先去除后上传"); return json.toString() ; } /* * 3.对文件信息的处理 */ MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request; if(WebUtil.isEmpty((CommonsMultipartFile) multipartRequest.getFile("file"))){ json.put("message", "没有文件"); return json.toString() ; } CommonsMultipartFile file = (CommonsMultipartFile) multipartRequest.getFile("file"); //对应前台文件对象 if(file!=null && file.getSize()>0){//检查文件大小和格式 if (file.getSize() >5*1024*1024) { json.put("message", "文件太大,超过5M"); return json.toString() ; } String originalName=file.getOriginalFilename(); String this_suffix = ""; params.put(Const.ISIMG, 0); params.put(Const.USE_TYPE, fileType); params.put(Const.USERNAME, regAccount.getLogin_name()); params.put(Const.COM_ID, qryParam.get("companyId")); params.put(Const.COM_NAME,companyForPlateFormService.getCompanyNameByCompanyId(qryParam)); boolean flag=false;//默认不 是图片 //获取文件后缀,与传过来的参数file_name重新组装文件名 if(originalName.indexOf(".")>0){//有后缀 XX.jpg XX.RAR this_suffix=originalName.substring(originalName.lastIndexOf(".")); String[] format = null; if("image".equals(formatType)){//判断上传文件的类型:image 图片,text 文档 format = Const.imgArray; params.put(Const.ISIMG, 1); }else if("text".equals(formatType)){ format = Const.otherArray; } for(String suffix:format){ if(suffix.equalsIgnoreCase(this_suffix)){ flag=true; break; } } } if(!flag){ json.put("message", "不是指定格式"); return json.toString() ; }else{ /* * 4.进行信息的处理 */ Date date = new Date(); SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String mongodbId=fileService.SaveFile(file,params); pfRegisterAttched.setMogodb_id(mongodbId);//把存储mongoDb的文件序号存到数据库中 pfRegisterAttched.setCreate_dt(date); pfRegisterAttched.setFile_format(this_suffix); Integer id = registerAttchedService.addAppRegisterAttched(pfRegisterAttched);//获取存入信息的id json.put("fileId",id); json.put("mongodbId", mongodbId); json.put("creatDate", sf.format(date)); json.put("message", "上传成功"); } }else{ json.put("message", "文件不存在"); } return json.toString(); } }
复制代码

 

 

4.解决方案

  在问题描述中已有问题解决方案。

  此方法主要利用了Spring框架中已有的工具类。

  参考资料:http://www.itdadao.com/articles/c15a279110p0.html

 

 

5.总结

  5.1 不同的content-type请求获取参数值的方法不同。

  5.2 在multipart/form-data请求方式中,需要利用SpringMVC框架中的CommonsMultipartResolver类包装转化为MultipartHttpServletRequest获取参数值;

 

 

6. 参考学习

  1. servlet3.0 Tomcat7.0 简洁方案

    如果使用的是servlet3.0及以上版本,multipart/form-data请求方式取值可以使用 HttpServletRequest.getPart(key)方法获取指定值;

    参考资料:http://stackoverflow.com/questions/2422468/how-to-upload-files-to-server-using-jsp-servlet/2424824#2424824

 

 

 

  2.通常的三种请求方式获取值方法

    * application/x-www-form-urlencoded

    *application/json

    * text/xml 

 

  可以使用将请求转化为流,再转为字符串获取相应的值,通过如下方法获取的字符串获取指定的参数值;

  转化方法如下:

复制代码
    /** 
     * 获取请求Body 
     * 
     * @param request 
     * @return 
     */  
    public static String getBodyString(ServletRequest request) {  
        StringBuilder sb = new StringBuilder();  
        InputStream inputStream = null;  
        BufferedReader reader = null;  
        try {  
            inputStream = request.getInputStream();  
            reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));  
            String line = "";  
            while ((line = reader.readLine()) != null) {  
                sb.append(line);  
            }  
        } catch (IOException e) {  
            e.printStackTrace();  
        } finally {  
            if (inputStream != null) {  
                try {  
                    inputStream.close();  
                } catch (IOException e) {  
                    e.printStackTrace();  
                }  
            }  
            if (reader != null) {  
                try {  
                    reader.close();  
                } catch (IOException e) {  
                    e.printStackTrace();  
                }  
            }  
        }  
        return sb.toString();  
    }  
复制代码
查看评论

web过滤器中获取请求的参数(content-type:multipart/form-data)

http://www.cnblogs.com/springlight/p/6208908.html web过滤器中获取请求的参数(content-type:multipart/form-...
  • strawbingo
  • strawbingo
  • 2017-04-05 15:43:07
  • 1816

springMVC 过滤器获取bean 和 multipart/form-data请求参数

springMVC 过滤器获取bean 和 multipart/form-data请求参数
  • LegendOfxiaoli
  • LegendOfxiaoli
  • 2017-09-26 16:16:30
  • 378

关于multipart/form-data类型请求,filter失效的处理办法

这个是在java官网翻到的,需要特殊处理下: Example 7.9 MultipartEncodeFilter - Standard Filter Strategy publ...
  • skey007
  • skey007
  • 2015-05-17 22:42:09
  • 487

springMvc 完美解决 multipart/form-data 方式提交请求 在 Filter 中 ServletRequest.getParameter方法 获取不到参数的问题

springMvc 解决 multipart/form-data 方式提交请求 ,在Filter 中 ServletRequest.getParameter方法 获取不到参数的问题...
  • qq_22956867
  • qq_22956867
  • 2016-05-17 17:34:22
  • 14365

python post content-type:multipart/form-data 类型的表单数据

表单数据post默认为application/x-www-form-urlencode,可以为标签加入属性enctype=multipart/form-data,使得post的数据按multipart...
  • strwolf
  • strwolf
  • 2016-01-10 11:21:07
  • 5511

解析Content-Type: multipart/form-data格式数据

经常要处理php端发过来的Content-Type: multipart/form-data数据,写两个函数处理下这个数据好。 #include #include #include #inclu...
  • smallmelon
  • smallmelon
  • 2014-01-23 18:05:24
  • 2683

C#中HttpWebRequest用Post提交Content-Type: multipart/form-data; boundary=

在C#中有HttpWebRequest类,可以很方便用来获取http请求,但是这个类对Post方式没有提供一个很方便的方法来获取数据。网上有很多人提供了解决方法,但都参差不齐,这里我把我使用的方法总结...
  • flymorn
  • flymorn
  • 2011-09-12 21:31:18
  • 16667

java 防止xss攻击 通过filter的方法

http://breezylee.iteye.com/blog/2063615 关于xss的概念和解决方案网上很多,可以参考这个: http://www.cnblogs.com/TankXiao...
  • kouwoo
  • kouwoo
  • 2014-12-15 18:45:03
  • 7335

使用java的过滤器filter备忘(post multipart/form-data)

测试中间件是weblogic8.1.4,jdk 是weblogic 自带的1.42
  • lin49940
  • lin49940
  • 2017-08-12 16:34:25
  • 303

四种post 提交数据方式(content-type)

四种常见的 POST 提交数据方式  我们知道,HTTP 协议是以 ASCII 码传输,建立在 TCP/IP 协议之上的应用层规范。规范把 HTTP 请求分为三个部分:状态行、请求头、消息主体。...
  • pzqingchong
  • pzqingchong
  • 2016-08-05 20:23:06
  • 1082
    个人资料
    持之以恒
    等级:
    访问量: 22万+
    积分: 2851
    排名: 1万+
    个人说明

    开发菜鸟一名
    专注于JAVA开发技术,
    搞过Javascript,Android/iOS,
    Oracle/MySQL,Linux/Unix等

    希望结识更多志同道合的朋友

    联系方式
    新浪微博: 
    http://weibo.com/luis0223
    QQ: 
    1157387617
    E-Mail: 
    zhanglu19910223@hotmail.com

    最新评论