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)方法獲取指定值;
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(); }
參考資料:http://blog.csdn.net/pyxly1314/article/details/51802652