赞
踩
参数验证是一个常见的问题,无论是前端还是后台,都需要对用户输入进行验证,以此来保证系统数据的正确性。对于web来说,有些人可能理所当然的想在前端验证就行了,但是这样是非常错误的想法,前端代码对于用户来说是透明的,稍微有点技术的人就可以绕过这个验证,直接提交数据到后台。无论是前端网页接口的提交还是提供给外部的接口,参数验证随处可见,也是必不可少的。前端做验证只是为了用户体验,比如控制按钮的显示隐藏,单页应用的路由跳转等等,后端才是最重的保障。总之一切用户的输入都是不可信的,需要我们去进行验证辨别。
前端的校验是必须的,后台的校验更是必须的,下面介绍几种验证的方式:
1.表现层验证:SpringMVC提供对JSR-303的表现层验证;
2.业务逻辑层验证:Spring3.1提供对业务逻辑层的方法验证(当然方法验证也可以出现在其他层,但个人认为方法验证应该属于业务逻辑层);
3.DAO层验证:Hibernate提供DAO层的模型数据的验证;
4.数据库端的验证:通过数据库约束来进行;
5.客户端验证支持:JSR-303页提供编程式验证支持
- if(string.IsNullOrEmpty(info.UserName))
- {
- return FailJson("用户名不能为空");
- }
这种方式简单,但是,参数多了的话就会很繁琐了;
切面拦截controller方法,然后捕获带@ChechParam注解方法参数实例,最后反射实例校验。
controller:
- @RequestMapping(value = "update" )
- @ResponseBody
- public ResultBean update(@CheckParam User user){
- return ResultBean.ok();
- }
model:
- public class User implements Serializable{
- @CheckParam(notNull = true)
- private String username;
- }
annotation:
- @Target(value={ElementType.PARAMETER,ElementType.FIELD,ElementType.TYPE})
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- public @interface CheckParam {
- boolean notNull() default false;
- }
aspect:
- import org.aspectj.lang.ProceedingJoinPoint;
- import org.aspectj.lang.annotation.Around;
- import org.aspectj.lang.annotation.Aspect;
- import org.aspectj.lang.annotation.Pointcut;
- import org.aspectj.lang.reflect.MethodSignature;
- import org.springframework.core.GenericTypeResolver;
- import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
- import org.springframework.core.MethodParameter;
- import org.springframework.stereotype.Component;
- import org.springframework.util.StringUtils;
- import java.lang.reflect.Field;
- import java.lang.reflect.Method;
-
-
- @Component
- @Aspect
- public class CheckParamAspect {
- @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
- public void methodPointCut() {}
-
- /**
- * 环绕切入方法
- **/
- @Around("methodPointCut()")
- public Object around(ProceedingJoinPoint point) throws Throwable {
- MethodSignature msig = (MethodSignature) point.getSignature();
- Method method = msig.getMethod();
- LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
- Object[] args = point.getArgs();
- for (int i = 0; i < args.length; i++) {
- Object obj = args[i];
- MethodParameter mp = new MethodParameter(method,i);
- mp.initParameterNameDiscovery(u);
- GenericTypeResolver.resolveParameterType(mp, method.getClass());//Spring处理参数
- //String paramName = mp.getParameterName();//参数名
- CheckParam anno = mp.getParameterAnnotation(CheckParam.class);//参数注解
- if(anno != null){
- check(obj);
- }
- }
- return point.proceed();
-
- }
-
- /**
- * 校验成员变量
- **/
- private void check(Object obj) throws IllegalAccessException {
- Class clazz = obj.getClass();
- for(Field field : clazz.getDeclaredFields()){
- CheckParam cp = field.getAnnotation(CheckParam.class);
- if(cp != null){
- check(obj,clazz, field,cp);
- }
- }
- }
-
- /**
- * 取出注解,校验变量
- **/
- private void check(Object obj, Class clazz, Field field, CheckParam cp) throws IllegalAccessException {
- if(cp.notNull()){
- field.setAccessible(true);
- Object f = field.get(obj);
- if(StringUtils.isEmpty(f)){
- throw new IllegalArgumentException("类" + clazz.getName() + "成员" + field.getName() + "检测到非法参数");
- }
- }
- }
- }

表单验证工具类ValidationUtils,依赖包commons-lang
- import org.apache.commons.lang3.StringUtils;
- import org.apache.commons.lang3.math.NumberUtils;
- import java.util.HashMap;
- import java.util.Map;
- import java.util.regex.Pattern;
-
- public class ValidateUtils {
- /**
- * @param fields
- * @param params
- * @return
- * 不存在的校验规则:返回true
- * 关键字不按要求写:返回true
- */
- public static SKResult validate(ValidField[] fields, Map<String, String> params){
-
- try {
- for(ValidField field : fields){
- String name = field.getName();
- String desc = field.getDes();
- boolean isValid = field.isValid();
- String[] rules = field.getRules();
- String value = params.get(name); // 对应请求参数值
- if(!isValid){
- return new SKResult(true, "");
- }
- for(String rule : rules){
- String[] arr = rule.replaceAll(" ", "").split(":");
- String arr1 = arr[0]; // required
- String arr2 = arr[1]; // true
- switch (arr1) {
- case "required": // 必须项 required:true|false
- if(Boolean.parseBoolean(arr2)){
- if(value==null || value.trim().length()==0){
- return new SKResult(false, desc+"不能为空");
- }
- }
- break;
- case "number": // 必须输入合法的数字(负数,小数) number:true|false
- if(Boolean.parseBoolean(arr2)){
- try{
- Double.valueOf(value);
- }catch(Exception e){
- return new SKResult(false, desc+"数值类型不合法");
- }
- }
- break;
- default:
- break;
- }
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- System.out.println("===ValidField格式不合法,请注意检查!");
- return new SKResult(true, "ValidField格式不合法");
- }
- return new SKResult(true, "校验通过");
- }
-
- public static void main(String[] args) {
- Map<String, String> params = new HashMap<String, String>();
- params.put("username", "18702764599");
- params.put("password", "123");
- ValidField[] fields = {
- new ValidField("username", "手机号", true, new String[]{
- "required:true",
- "isTel:true"
- "min:5"
- "max:5"
- }),
- new ValidField("password", "密码", true, new String[]{
- "required:true",
- "isPassword:true",
- "equalTo:#username"
- "max:2"
- })
- };
-
- SKResult sk = ValidateUtils.validate(fields, params);
- System.out.println(sk);
- //SKResult [result=true, respMsg=校验通过, obj=null, type=null]
- }
- }

SKResult :
- public class SKResult {
- // 返回代码
- private boolean result;
- // 错误信息
- private String respMsg;
- private Object obj;
-
- //set.get方法
- @Override
- public String toString() {
- return "SKResult [result=" + result + ", respMsg=" + respMsg + ", obj="
- + obj + ", type=" + type + "]";
- }
- }
ValidField :
- public class ValidField {
- /**
- * 字段名
- */
- private String name;
- /**
- * 字段描述
- */
- private String des;
- /**
- * 为true必须校验
- */
- private boolean isValid = false;
- /**
- * 校验规则
- */
- private String[] rules;
-
- public String[] getRules() {
- return rules;
- }
- public void setRules(String[] rules) {
- this.rules = rules;
- }
-
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public String getDes() {
- return des;
- }
- public void setDes(String des) {
- this.des = des;
- }
- public boolean isValid() {
- return isValid;
- }
- public void setValid(boolean isValid) {
- this.isValid = isValid;
- }
- public ValidField(String name, String des, boolean isValid, String[] rules) {
- super();
- this.name = name;
- this.des = des;
- this.isValid = isValid;
- this.rules = rules;
- }
- }

JSR 303(Java Specification Requests 规范提案)是JAVA EE 6中的一项子规范,一套JavaBean参数校验的标准,叫做Bean Validation。JSR 303用于对Java Bean中的字段的值进行验证,Spring MVC 3.x之中也大力支持 JSR-303,可以在控制器中对表单提交的数据方便地验证。
- <!--jsr 303-->
- <dependency>
- <groupId>javax.validation</groupId>
- <artifactId>validation-api</artifactId>
- <version>1.1.0.Final</version>
- </dependency>
- <!-- hibernate validator-->
- <dependency>
- <groupId>org.hibernate</groupId>
- <artifactId>hibernate-validator</artifactId>
- <version>5.2.0.Final</version>
- </dependency>
- 在想要校验的类的属性上面加入@NotNull等注解
-
-
- package com.example.demo;
-
- import javax.validation.ConstraintViolation;
- import javax.validation.Validation;
- import javax.validation.ValidationException;
- import javax.validation.Validator;
- import javax.validation.constraints.NotNull;
- import javax.validation.constraints.Pattern;
- import java.util.Iterator;
- import java.util.Set;
-
- /**
- * @author lanxinghua
- * @date 2018/08/05 15:51
- * @description
- */
- public class ValidateTestClass {
- @NotNull(message = "reason信息不可以为空")
- @Pattern(regexp = "[1-7]{1}", message = "reason的类型值为1-7中的一个类型")
- private String reason;
-
- public void setReason(String reason) {
- this.reason = reason;
- }
-
- public void validateParams() {
- //调用JSR303验证工具,校验参数
- Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
- Set<ConstraintViolation<ValidateTestClass>> violations = validator.validate(this);
- Iterator<ConstraintViolation<ValidateTestClass>> iter = violations.iterator();
- if (iter.hasNext()) {
- String errMessage = iter.next().getMessage();
- throw new ValidationException(errMessage);
- }
- }
- }

- 在controller里面的方法入参前面加上@Valid,参数后面加上BindingResult
- 在方法里面通过bindingResult.hasErrors()判断是否有错误,如果有错误返
- 回当前页面
-
- @RequestMapping("/register")
- public ModelAndView register(@Valid User user,BindingResult bindingResult) {
- ModelAndView modelAndView = new ModelAndView();
- if(bindingResult.hasErrors()){
- String message=bindingResult.getFieldError().getDefaultMessage();
- System.out.println(message);
- }
- modelAndView.setViewName("/index");
- return modelAndView;
- }
以上方法是在controller层对参数进行校验,然而当我们需要在server层对参数字段等进行校验的时候则需要手动进行校验,栗子如下:
首先在需要检验的类上添加相关的校验注解,此处不做赘述;
第二步创建校验的工具类:
-
- import javax.validation.ConstraintViolation;
- import javax.validation.Validation;
- import javax.validation.Validator;
- import java.util.Set;
-
- public class CommonTool {
-
- public static void validate(Object o) throws Exception {
- String error="";
- Validator validator= Validation.buildDefaultValidatorFactory().getValidator();
- Set<ConstraintViolation<Object>> constraintViolations =validator.validate(o);
- if (constraintViolations.size()>0){
- for (ConstraintViolation<Object> constraintViolation:constraintViolations){
- error=error+constraintViolation.getMessage()+";";
- }
- throw new Exception(error);
- }
-
- }
- }

第三步,手动校验
- public Department addDepartment(Department dt) throws Exception {
- //手动进行校验
- CommonTool.validate(dt);
- return departmentDao.save(dt);
- }
JSR和Hiberbate validator的校验只能对Object的属性进行校验。
1.Model中添加校验注解
- public class Book {
- private long id;
- @NotEmpty(message = "书名不能为空")
- private String bookName;
- @NotNull(message = "ISBN号不能为空")
- private String bookIsbn;
- @DecimalMin(value = "0.1",message = "单价最低为0.1")
- private doubleprice; // getter setter ....... }
2.在controller中使用此校验
- @RequestMapping(value = "/book",method = RequestMethod.POST)
- public void addBook(@RequestBody @Valid Book book) {
- System.out.println(book.toString());
- }
3.分组验证
对同一个Model,我们在增加和修改时对参数的校验也是不一样的,这个时候我们就需要定义分组验证,步骤如下:
定义两个空接口,分别代表Person对象的增加校验规则和修改校验规则
- //可以在一个Model上面添加多套参数验证规则,此接口定义添加Person模型修改时的参数校验规则
-
- public interface PersonAddView {}
-
- public interface PersonModifyView {}
Model上添加注解时使用指明所述的分组
- public class Person {
- private long id;
- /**
- * 添加groups 属性,说明只在特定的验证规则里面起作用,不加则表示在使用Deafault规则时起作用
- */
-
- @NotNull(groups = {PersonAddView.class, PersonModifyView.class}, message= "添加、修改用户时名字不能为空",payload = ValidateErrorLevel.Info.class)
-
- @ListNotHasNull.List({
- @ListNotHasNull(groups = {PersonAddView.class}, message = "添加上Name不能为空"),
- @ListNotHasNull(groups = {PersonModifyView.class}, message = "修改时Name不能为空")})
- private String name;
-
- @NotNull(groups = {PersonAddView.class}, message = "添加用户时地址不能为空")
- private String address;
-
- @Min(value = 18, groups = {PersonAddView.class}, message = "姓名不能低于18岁")
- @Max(value = 30, groups = {PersonModifyView.class}, message = "姓名不能超过30岁")
- private int age;
- //getter setter 方法......
-
- }

此时启用校验和之前的不同,需要指明启用哪一组规则
- /**
- * 备注:此处@Validated(PersonAddView.class)表示使用PersonAndView这套校验规则,若使用@Valid 则表示使用默认校验规则,若两个规则同时加上去,则只有第一套起作用
- * 修改Person对象
- * 此处启用PersonModifyView这个验证规则
- */
- @RequestMapping(value = "/person", method = RequestMethod.PUT)
- public void modifyPerson(@RequestBody @Validated(value ={PersonModifyView.class}) Person person) {
- System.out.println(person.toString());
- }
JSR和Hibernate validator的校验只能对Object的属性进行校验,不能对单个的参数进行校验,Spring在此基础上进行了扩展,添加了MethodValidationPostProcessor拦截器,可以实现对方法参数的校验
- public @NotNull UserModel get2(@NotNull @Size(min = 1) Integer uuid) {
- //获取 User Model
- UserModel user = new UserModel(); //此处应该从数据库获取
- return user;
- }
- @Null 被注释的元素必须为 null
- @NotNull 被注释的元素必须不为 null
- @AssertTrue 被注释的元素必须为 true
- @AssertFalse 被注释的元素必须为 false
- @Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
- @Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
- @DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
- @DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
- @Size(max=, min=) 被注释的元素的大小必须在指定的范围内
- @Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
- @Past 被注释的元素必须是一个过去的日期
- @Future 被注释的元素必须是一个将来的日期
- @Pattern(regex=,flag=) 被注释的元素必须符合指定的正则表达式
- @NotBlank(message =) 验证字符串非null,且长度必须大于0
- @Email 被注释的元素必须是电子邮箱地址
- @Length(min=,max=) 被注释的字符串的大小必须在指定的范围内
- @NotEmpty 被注释的字符串的必须非空
- @Range(min=,max=,message=) 被注释的元素必须在合适的范围内
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。