spring 19-Spring框架使用拦截器进行服务器验证

  • 服务器端的数据接收必须要保证整个数据的合法性,该接收什么类型的数据就应该接收什么类型的
  • 常用的验证规则基本上有四种:
    • int数据类型:d+
    • double数据类型:d+(.d+)?
    • string数据类型:验证该数据是否为空
    • date数据:验证是否为一个日期,d{4}-d{2}-d{2}

经典案例:使用拦截器进行服务器数据类型验证

1、编写/pages/emp_add.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<%
	String addUrl = request.getContextPath() + "/pages/emp/addall.action" ;
%>
<form action="<%=addUrl%>" method="post" enctype="multipart/form-data">
	雇员编号:<input type="text" name="empno" id="empno" value="7369"><br>
	雇员姓名:<input type="text" name="ename" id="ename" value="liang"><br>
	雇员工资:<input type="text" name="sal" id="sal" value="1.1"><br>
	雇佣日期:<input type="text" name="hiredate" id="hiredate" value="1911-11-11"><br>
	雇员照片:<input type="file" name="photo" id="photo"><br>
	<input type="submit" value="提交">
</form>
</body>
</html>

2、编写/pages/emp/show.jsp

<h1>${empno}</h1>
<h1>${ename}</h1>
<h1>${sal}</h1>
<h1>${hiredate}</h1>
<h1>${photo}</h1>

3、编写/pages/errors.jsp

<%@ page pageEncoding="UTF-8"%>
<h1>对不起,出现了错误!</h1>
<h1>${errors}</h1>

4、编写properties文件

error.page=/pages/errors.jsp
EmpTAction.addall.error.page=/pages/errors.jsp
validation.string.msg=该数据不允许为空!
validation.int.msg=该数据不允许为空!
validation.double.msg=该数据必须是数字!
validation.rand.msg=验证码输入错误!
validation.date.msg=该数据必须是日期(yyyy-mm-dd)!
validation.datetime.msg=该数据必须是日期时间(yyyy-mm-dd hh:mm:ss)!
validation.mime.msg=上传了非法文件,请确认后重新上传!
mime.rules=image/bmp|image/jpg|image/jpeg|image/png|image/gif
EmpTAction.addall.mime.rules=image/bmp|image/jpg|image/jpeg|image/png|image/gif
EmpTAction.addall.rules=empno:int|ename:string|sal:double|hiredate:date

5、编写AbstractAction.java

package cn.liang.util.action;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Locale;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.context.MessageSource;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.multipart.MultipartFile;
import cn.liang.util.UploadFileUtil;

public abstract class AbstractAction {
	@Resource
	private MessageSource msgSource ;	// 表示此对象直接引用配置好的类对象(根据类型匹配)
	
	/**
	 * 根据指定的key的信息进行资源数据的读取控制
	 * @param msgKey 表示要读取的资源文件的key的内容
	 * @return 表示资源对应的内容
	 */
	public String getValue(String msgKey,Object ...args) {
		return this.msgSource.getMessage(msgKey, args, Locale.getDefault()) ;
	} 
	
	@InitBinder	
	public void initBinder(WebDataBinder binder) {	// 方法名称自己随便写
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") ;
		// 本方法的处理指的是追加有一个自定义的转换编辑器,如果遇见的操作目标类型为java.util.Date类
		// 则使用定义好的SimpleDateFormat类来进行格式化处理,并且允许此参数的内容为空
		binder.registerCustomEditor(java.util.Date.class, new CustomDateEditor(sdf, true));
	} 
	
	/**
	 * 生成图片名称
	 * @param photoFile	上传上来的图片文件对象
	 * @return
	 */
	public String createFileName(MultipartFile photoFile){
		if (photoFile.isEmpty()) {
			return "nophoto.png";
		}else {
			return UUID.randomUUID() + "." + photoFile.getContentType().split("/")[1];
		}
	}
	
	public abstract String getFileUploadDir();
}

6、编写EmpTAction.java

package cn.liang.action;
import java.util.HashMap;
import java.util.Map;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import cn.liang.util.action.AbstractAction;

@Controller
@RequestMapping("/pages/emp/*") 
public class EmpTAction  extends AbstractAction{
	private Logger log = Logger.getLogger(EmpTAction.class) ;
	
	@RequestMapping("addall")
	public ModelAndView addall(int empno,String ename , Double sal, String hiredate,MultipartFile photo) {
		log.info("*** empno = " + empno);
		log.info("*** ename = " + ename);
		log.info("*** sal = " + sal);
		log.info("*** hiredate = " + hiredate);
		log.info("*** photo = " + super.createFileName(photo));
		Map<String,Object> map = new HashMap<String,Object>() ;
		map.put("empno", empno);
		map.put("ename", ename);
		map.put("sal", sal);
		map.put("hiredate", hiredate);
		map.put("photo", super.createFileName(photo));
		ModelAndView mav = new ModelAndView("show.jsp") ;
		mav.addAllObjects(map) ;
		return mav ; 
	}

	@Override
	public String getFileUploadDir() {
		return "/upload/images/";
	}
}

7、编写ResourceReadUtil.java

package cn.liang.util;
import java.lang.reflect.Method;
import org.springframework.web.method.HandlerMethod;

public class ResourceReadUtil {
	/**
	 * 读取错误页的配置消息
	 * @param handlerMethod
	 * @return
	 */
	public static String getErrorPageValue(HandlerMethod handlerMethod) {
		String pageKey = handlerMethod.getBean().getClass().getSimpleName() + "."
				+ handlerMethod.getMethod().getName() + ".error.page";
		String pageUrl = getValue(handlerMethod,pageKey) ;
		if (pageUrl == null) {
			pageUrl = getValue(handlerMethod,"error.page") ;
		}  
		return pageUrl ; 
	}
	/**
	 * 实现消息的手工配置读取
	 * @param handlerMethod
	 * @param msgKey
	 * @return
	 */
	public static String getValue(HandlerMethod handlerMethod, String msgKey) {
		try {
			Method getValueMethod = handlerMethod.getBean().getClass().getMethod("getValue", String.class,
					Object[].class);
			return getValueMethod.invoke(handlerMethod.getBean(), msgKey, null).toString();
		} catch (Exception e) {
			return null ; 
		} 
	}
}

8、编写拦截器ValidationInterveptor.java

package cn.liang.util.validate;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import cn.liang.util.ResourceReadUtil;

public class ValidationInterveptor implements HandlerInterceptor {
	Logger log = Logger.getLogger(ValidationInterveptor.class);
	
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		log.info("**** ValidationInterveptor--preHandle ****");
		boolean flag = true ;	// 默认放行
		// 需要取得HandlerMethod对象,这样可以取得相关的Action信息
		HandlerMethod handlerMethod = (HandlerMethod) handler ;
		System.out.println(handlerMethod.getBean().getClass().getSimpleName() + "."
				+ handlerMethod.getMethod().getName() + ".rules");
		System.out.println(handlerMethod.getBean().getClass().getMethod("getValue", String.class,
				Object[].class).invoke(handlerMethod.getBean(), handlerMethod.getBean().getClass().getSimpleName() + "."
						+ handlerMethod.getMethod().getName() + ".rules", null));
		// 表示具体的验证处理操作,所有的错误信息通过Map返回
		Map<String,String> errors = ValidatorUtils.validate(request, handlerMethod) ;
		log.info(errors.toString());
		if (errors.size() > 0) {	// 有错
			request.setAttribute("errors", errors);	// 保存在Request属性范围之中
			flag = false ;	// 表示现在有错误,无法向下执行
			request.getRequestDispatcher(ResourceReadUtil.getErrorPageValue(handlerMethod)).forward(request, response);
			log.info(ResourceReadUtil.getErrorPageValue(handlerMethod));
		} else {	// 没有错
			return true ;
		}
		return flag;		
	}
	
	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		log.info("**** ValidationInterveptor--postHandle ****");
	}
	
	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		log.info("**** ValidationInterveptor--afterCompletion ****");
	}
}

9、编写验证类ValidatorUtils.java

package cn.liang.util.validate;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.log4j.Logger;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartRequest;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import cn.liang.util.ResourceReadUtil;

public class ValidatorUtils {
	private static Logger log = Logger.getLogger(ValidatorUtils.class);
	/**
	 * 实现提交参数的验证,使用指定Action的指定验证规则处理
	 * @param request
	 * @param handlerMethod
	 * @return 所有的验证错误信息保存在Map集合中返回,如果没有错误,则Map集合的长度为0
	 */
	public static Map<String, String> validate(HttpServletRequest request, HandlerMethod handlerMethod) {
		// 通过给定的Action名称以及要调用的业务方法“rules”一起拼凑出要取出的验证规则,在Validations.properties中定义
		String validationKey = handlerMethod.getBean().getClass().getSimpleName() + "."
				+ handlerMethod.getMethod().getName() + ".rules";
		Map<String,String> errors = new HashMap<String,String>() ;	// 保存所有的验证信息
		// log.info("【*** preHandle ***】validationValue = " + validationKey);
		try {
			// 现在取得了验证规则的key的信息之后实际上并无法知道该key对应的具体的内容是什么,而内容需要依靠AbstractAction.getValue()取得
			Method getValueMethod = handlerMethod.getBean().getClass().getMethod("getValue", String.class,
					Object[].class);
			try { // 如果现在没有指定的key有可能产生异常,就认为现在没有具体的验证规则出现
					// 通过getValue()方法的Method对象取得对应的验证信息
				String validationValue = (String) getValueMethod.invoke(handlerMethod.getBean(), validationKey, null);
				if (validationValue != null) { // 表示规则现在存在
					// log.info("【*** preHandle ***】validationValue = " + validationValue);
					// 取得全部的提交参数, 需要针对于给定的规则进行拆分控制
					String result[] = validationValue.split("\|"); // 按照竖线拆分
					for (int x = 0; x < result.length; x++) { // 每一个规则的组成“参数名称:规则类型”
						String temp[] = result[x].split(":");
						String paramName = temp [0];
						String paramRule = temp [1] ;	// 验证规则
						String paramValue = request.getParameter(paramName) ;
						log.info("【提交参数】paramName = " + paramName + "、paramValue = " + request.getParameter(paramName));
						switch (paramRule) {
							case "string" : {
								if (!ValidateRuleUtil.isString(paramValue)) {	// 该验证没有通过
									String msg = (String) getValueMethod.invoke(handlerMethod.getBean(), "validation.string.msg", null) ;
									errors.put(paramName, msg) ;
								}
								break ;
							} 
							case "int" : {
								if (!ValidateRuleUtil.isInt(paramValue)) {	// 该验证没有通过
									String msg = (String) getValueMethod.invoke(handlerMethod.getBean(), "validation.int.msg", null) ;
									errors.put(paramName, msg) ;
								}
								break ;
							} 
							case "double" : {
								if (!ValidateRuleUtil.isDouble(paramValue)) {	// 该验证没有通过
									String msg = (String) getValueMethod.invoke(handlerMethod.getBean(), "validation.double.msg", null) ;
									errors.put(paramName, msg) ;
								}
								break ;
							} 
							case "date" : {
								if (!ValidateRuleUtil.isDate(paramValue)) {	// 该验证没有通过
									String msg = (String) getValueMethod.invoke(handlerMethod.getBean(), "validation.date.msg", null) ;
									errors.put(paramName, msg) ;
								}
								break ;
							} 
							case "datetime" : {
								if (!ValidateRuleUtil.isDatetime(paramValue)) {	// 该验证没有通过
									String msg = (String) getValueMethod.invoke(handlerMethod.getBean(), "validation.datetime.msg", null) ;
									errors.put(paramName, msg) ;
								}
								break ;
							} 
							case "rand" : {
								if (!ValidateRuleUtil.isRand(request,paramValue)) {	// 该验证没有通过
									String msg = (String) getValueMethod.invoke(handlerMethod.getBean(), "validation.rand.msg", null) ;
									errors.put(paramName, msg) ;
								} 
								break ;
							}
						}
					}
				}
			} catch (Exception e) {
			}
		} catch (Exception e) {
		}
		if (errors.size() == 0) {	// 之前没有错误信息,现在表示我可以对上传文件类型进行验证
			// 需要判断是否当前有上传文件
			MultipartResolver mr = new CommonsMultipartResolver() ;		// 通过它来判断对于上传文件的接收操作
			if (mr.isMultipart(request)) {	// 表示的是当前有上传文件
				// 需要拼凑验证规则使用的key的信息
				String mimeKey = handlerMethod.getBean().getClass().getSimpleName() + "."
						+ handlerMethod.getMethod().getName() + ".mime.rules" ;
				// 取得具体的验证规则的消息
				String mimeValue = ResourceReadUtil.getValue(handlerMethod, mimeKey) ;
				if (mimeValue == null) {	// 没有消息读到,没有设置单独的验证规则
					mimeValue = ResourceReadUtil.getValue(handlerMethod, "mime.rules") ;
				}
				// 进行每一个上传文件的具体验证操作
				String mimeResult [] = mimeValue.split("\|") ;	// 因为是一组规则,所以需要拆分
				MultipartRequest mreq = (MultipartRequest) request ;	// 处理上传时的request
				Map<String,MultipartFile> fileMap = mreq.getFileMap() ;	// 取得全部的上传文件
				if (fileMap.size() > 0) {	// 现在有上传文件
					// 需要判断每一个文件的类型
					Iterator<Map.Entry<String,MultipartFile>> iter = fileMap.entrySet().iterator() ;
					while (iter.hasNext()) {	// 判断每一个文件的类型
						Map.Entry<String,MultipartFile> me = iter.next() ;
						if (me.getValue().getSize() > 0) {	// 当前的这个上传文件的长度大于0,有上传
							if (!ValidateRuleUtil.isMime(mimeResult, me.getValue().getContentType())) {	// 没有验证通过
								errors.put("file", ResourceReadUtil.getValue(handlerMethod, "validation.mime.msg")) ;
							}
						}
					}
				}
			}
		}
		return errors ;
	}
}

10、编写数据类型验证类ValidateRuleUtil.java

package cn.liang.util.validate;

import javax.servlet.http.HttpServletRequest;

/**
 * 完成的是一个个具体的验证规则的判断操作
 * @author 
 */
public class ValidateRuleUtil {
	/**
	 * 验证传入的mime类型是否复合于当前的开发要求
	 * @param mimeRules 整体的验证规则
	 * @param mime 每一个上传文件的类型
	 * @return
	 */
	public static boolean isMime(String mimeRules[], String mime) {
		if (isString(mime)) {
			for (int x = 0; x < mimeRules.length; x++) {
				if (mime.equals(mimeRules[x])) {
					return true;
				}
			}
		}
		return false; 
	}
	/**
	 * 进行验证码的检测,验证码的属性名称固定为rand
	 * @param request
	 * @param param
	 * @return
	 */
	public static boolean isRand(HttpServletRequest request,String str) {
		if (isString(str)) {
			String rand = (String) request.getSession().getAttribute("rand") ;
			if (isString(rand)) {
				return rand.equalsIgnoreCase(str) ;
			}
		}
		return false ;
	} 
	/**
	 * 判断是否是整数
	 * @param str
	 * @return
	 */
	public static boolean isInt(String str) {
		if (isString(str)) {	// 验证数据是否为空
			return str.matches("\d+") ;
		}
		return false ;	// 数据为空返回false
	}

	/**
	 * 验证是否是日期,格式为“yyyy-MM-dd HH:mm:ss”
	 * @return
	 */
	public static boolean isDatetime(String str) {
		if (isString(str)) {
			return str.matches("\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}") ;
		}  
		return false ;
	}
	/**
	 * 验证是否是日期,格式为“yyyy-MM-dd”
	 * @return
	 */
	public static boolean isDate(String str) {
		if (isString(str)) {
			return str.matches("\d{4}-\d{2}-\d{2}") ;
		} 
		return false ;
	}
	/**
	 * 验证该数据是否是小数
	 * @param str
	 * @return
	 */
	public static boolean isDouble(String str) {
		if (isString(str)) {
			return str.matches("\d+(\.\d+)?") ;
		}
		return false ;
	}
	/**
	 * 如果传入的内容为null或者是空字符串,则表示错误,返回false
	 * @param str
	 * @return
	 */
	public static boolean isString(String str) {
		if (str == null || "".equals(str)) {
			return false ;
		}
		return true ;
	}
}
原文地址:https://www.cnblogs.com/liangjingfu/p/10113904.html