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中配置的过滤器
requestFilter
util.web.RequestFilter
requestFilter
/*
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)方法转化;
packageutil.web;public class RequestFilter implementsFilter {
protectedFilterConfig filterConfig;protected booleanfilterEnabled;protected intlogLevel;protected booleanneedVCode;protected List noFilerList =null;private CommonsMultipartResolver multipartResolver = newCommonsMultipartResolver();publicRequestFilter() {this.filterConfig = null;this.filterEnabled = true;this.logLevel = -1;
}
@Overridepublic voiddestroy() {//TODO Auto-generated method stub
}
@Overridepublic voiddoFilter(ServletRequest req, ServletResponse resp,
FilterChain chain)throwsIOException, 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")
@Overridepublic void init(FilterConfig cfg) throwsServletException {//初始化操作
}privateBoolean 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对应的值;
packageplatform.common.controller;/*** mogodb文件上传下载
* 项目名称:outsideeasy
* 类名称:PfTaskFileCtrl
* 创建人:mishengliang
* 创建时间:2016-4-26 下午1:55:19
* 修改人:mishengliang
* 修改时间:2016-4-26 下午1:55:19
*@version**/@Controller
@RequestMapping("PfTaskFileCtrl")public classPfTaskFileCtrl {
@AutowiredprivatePfRegisterAttchedService registerAttchedService;
@AutowiredprivateFileOptService fileService;
@AutowiredprivatePfUpdateRegisterAttchedService updateRegisterAttchedService;
@AutowiredprivateCompanyForPlateFormService companyForPlateFormService;
@RequestMapping(value= { "/companyImageView" }, method ={ RequestMethod.GET })publicModelAndView gojsp_companyImageView(ModelAndView modelAndView ){
modelAndView.setViewName("/companyWindow/companyImageView");returnmodelAndView;
}/*** @Description:企业文件上传
* PfTaskFileCtrl
* addOrUpdateTaskImgFile1
*@paramrequest
*@paramresponse
*@return*@throwsException String
*@authoryukai
* 2016-8-4 上午10:03:00*/@DocLogger(explain="企业文件上传")
@RequestMapping(value="addOrUpdateTaskImgFile1",method=RequestMethod.POST)
@ResponseBodypublic String addOrUpdateTaskImgFile1(HttpServletRequest request,HttpServletResponse response) throwsException{
Map qryParam =WebUtil.getDefaultParamsMap(request);
LoginAccount regAccount=SessionUtil.getCurrentPlateLoginAccount();
Map params=new HashMap();
JSONObject json= newJSONObject();
json.put("success", true);/** 1.检查参数*/
if(regAccount == null){//获取任务id
json.put("message", "未登录");returnjson.toString() ;
}
// *如果在filter中未将转化的request值赋值给过滤链,则在这里无法获取fileType对应的值if(WebUtil.isEmpty(request.getParameter("fileType"))){//获取任务id
json.put("message", "没有文件类型值");returnjson.toString() ;
}/**2.赋值*/
int fileType = Integer.parseInt(request.getParameter("fileType"));
String fileName= request.getParameter("fileName");
String formatType= request.getParameter("formatType");
PfRegisterAttched pfRegisterAttched= newPfRegisterAttched();
pfRegisterAttched.setFile_type_id(fileType);//文件类型值
pfRegisterAttched.setFile_name(fileName);//文件名
if(fileName.indexOf(",") != -1){
json.put("message", "文件名中存在非法字符(英文逗号),请先去除后上传");returnjson.toString() ;
}/** 3.对文件信息的处理*/MultipartHttpServletRequest multipartRequest=(MultipartHttpServletRequest) request;if(WebUtil.isEmpty((CommonsMultipartFile) multipartRequest.getFile("file"))){
json.put("message", "没有文件");returnjson.toString() ;
}
CommonsMultipartFile file= (CommonsMultipartFile) multipartRequest.getFile("file"); //对应前台文件对象
if(file!=null && file.getSize()>0){//检查文件大小和格式
if (file.getSize() >5*1024*1024) {
json.put("message", "文件太大,超过5M");returnjson.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", "不是指定格式");returnjson.toString() ;
}else{/** 4.进行信息的处理*/Date date= newDate();
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", "文件不存在");
}returnjson.toString();
}
}
4.解决方案
在问题描述中已有问题解决方案。
此方法主要利用了Spring框架中已有的工具类。
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
*
*@paramrequest
*@return
*/
public staticString getBodyString(ServletRequest request) {
StringBuilder sb= newStringBuilder();
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();
}
}
}returnsb.toString();
}