• Java 数据校验


    数据校验

    1. 为什么需要数据校验

    虽然我们在前台js进行了拦截,比如submit总体校验一遍,或者每个form控件blur失去焦点的时候进行了校验,但是

    我们服务器接口可能被服务器通过代码(http-client)访问,或者其他的方式跳过浏览器js的校验逻辑,如果后台不进行

    校验,那么可能会带来严重的安全问题:比如sql注入,XXS攻击等等安全漏洞。

    2. 数据校验的解决

    使用Hibernate-validator进行校验
    1. 依赖
     <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-validation</artifactId>
     </dependency>
    
    2. 常用的注解
    注解 注解的元素类型 描述
    @AssertFalse Boolean、boolean 被注解的元素必须为False
    @AssertTrue Boolean、boolean 被注解的元素必须为True
    @DecimalMax BigDecimal、BigInteger、CharSequence、byte、short、int、long以及它们各自的包装类 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
    @DecimalMin BigDecimal、BigInteger、CharSequence、byte、short、int、long以及它们各自的包装类 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
    @Digits BigDecimal、BigInteger、CharSequence、byte、short、int、long以及它们各自的包装类 被注释的元素必须是一个数字,其值必须在可接受的范围内
    @Email CharSequence 被注释的元素必须是电子邮箱地址
    @Future Java.util.Date、Java.util.Calender以及java.time包下的时间类 被注释的元素必须是一个将来的日期
    @FutureOrPresent Java.util.Date、Java.util.Calender以及java.time包下的时间类 被注释的元素必须是当前时间或之后一个时间
    @Max BigDecimal、BigInteger、byte、short、int、long以及它们各自的包装类 被注释的元素必须是一个数字,其值必须小于等于指定的最大值,注意如果@Max所注解的元素是null,则@Max注解会返回true,所以应该把@Max注解和@NotNull注解结合使用
    @Min BigDecimal、BigInteger、byte、short、int、long以及它们各自的包装类 被注释的元素必须是数字,并且值要大于或等于给定的值。注意如果@Min所注解的元素是null,则@Min注解会返回true,即也会通过校验,所以应该把@Min注解和@NotNull注解结合使用
    @Negative BigDecimal、BigInteger、byte、short、int、long以及它们各自的包装类 被注解的元素必须是负数
    @NegativeOrZero BigDecimal、BigInteger、byte、short、int、long以及它们各自的包装类 被注解的元素必须是负数或0
    @NotBlank CharSequence 被注解的元素必须不为null并且至少有一个非空白字符
    @NotEmpty CharSequence、Collection、Map、Array 被注解的字符串不为null或空字符串,被注解的集合或数组不为空。和@NotBlank相比,一个空字符串在@NotBlank中验证不通过,但是在@NotEmpty中验证可以通过
    @NotNull 任意类型 被注解的元素不能为null
    @Past Java.util.Date、Java.util.Calender以及java.time包下的时间类 被注解的元素必须是一个过去的日期
    @PastOrPresent Java.util.Date、Java.util.Calender以及java.time包下的时间类 被注解的元素必须是一个过去的日期或者当前日期
    @Pattern CharSequence 被注解的元素必须符合指定正则表达式
    @Positive BigDecimal、BigInteger、byte、short、int、long以及它们各自的包装类 被注解的元素必须是正数
    @PositiveOrZero BigDecimal、BigInteger、byte、short、int、long以及它们各自的包装类 被注解的元素必须是正数或0
    @Size CharSequence、Collection、Map、Array 被注解的字符串长度,集合或者数组的大小必须在指定的范围内
    3.@Valid和@Validated的区别
    1. 作用范围:
      @Valid:可以用在方法、构造函数、方法参数和成员属性(字段)上
      @Validated:可以用在类型、方法和方法参数上。但是不能用在成员属性(字段)上,不支持嵌套检测

    2. 来源:

      @Valid:使用Hibernate validation的时候使用

      @Validated:只用Spring Validator校验机制使用

    3. 功能:

      @Valid:支持嵌套检测,不支持分组检测
      @Validated:不支持嵌套检测,支持分组检测

    DEMO

    package com.asher.springboot.service.vo;
    
    import javax.validation.Valid;
    import javax.validation.constraints.*;
    
    /**
     * \* @author: AsherWu
     * \* Date: 2022/4/21
     * \* Description: 学生信息VO 新增或者修改学生信息的时候接收参数
     * \
     */
    public class StudentVO {
    
        @NotBlank(message = "学生姓名不能为空!")
        private String stuName;
    
        @Max(value = 30, message = "学生年龄不能超过30!")
        @Min(value = 10, message = "学生年龄不能超过10!")
        private Integer age;
    
        @NotNull(message = "学生性别不能为空!")
        private Integer sex;
    
        @NotBlank(message = "学生地址不能为空!")
        private String address;
    
        @Valid
        private ParentVO parentVO;
    
        public String getStuName() {
            return stuName;
        }
    
        public void setStuName(String stuName) {
            this.stuName = stuName;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        public Integer getSex() {
            return sex;
        }
    
        public void setSex(Integer sex) {
            this.sex = sex;
        }
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    
        public ParentVO getParentVO() {
            return parentVO;
        }
    
        public void setParentVO(ParentVO parentVO) {
            this.parentVO = parentVO;
        }
    
        @Override
        public String toString() {
            return "StudentVO{" +
                    "stuName='" + stuName + '\'' +
                    ", age=" + age +
                    ", sex=" + sex +
                    ", address='" + address + '\'' +
                    ", parentVO=" + parentVO +
                    '}';
        }
    }
    
    package com.asher.springboot.service.vo;
    
    import javax.validation.constraints.Max;
    import javax.validation.constraints.Min;
    import javax.validation.constraints.NotBlank;
    
    /**
     * \* @author: AsherWu
     * \* Date: 2022/4/21
     * \* Description: 家长信息VO 与学生信息关联
     * \
     */
    public class ParentVO {
    
        @NotBlank(message = "家长姓名不能为空!")
        private String parName;
    
        @Max(value = 80, message = "家长年龄不能超过80!")
        @Min(value = 30, message = "家长年龄不能低于30!")
        private Integer parAge;
    
        @NotBlank(message = "家长联系方式不能为空!")
        private String phone;
    
        public String getParName() {
            return parName;
        }
    
        public void setParName(String parName) {
            this.parName = parName;
        }
    
        public Integer getParAge() {
            return parAge;
        }
    
        public void setParAge(Integer parAge) {
            this.parAge = parAge;
        }
    
        public String getPhone() {
            return phone;
        }
    
        public void setPhone(String phone) {
            this.phone = phone;
        }
    
        @Override
        public String toString() {
            return "ParentVO{" +
                    "parName='" + parName + '\'' +
                    ", parAge=" + parAge +
                    ", phone='" + phone + '\'' +
                    '}';
        }
    }
    
    
    
    package com.asher.springboot.controller;
    
    import com.asher.springboot.core.domain.AjaxResult;
    import com.asher.springboot.service.vo.StudentVO;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.boot.context.properties.bind.BindResult;
    import org.springframework.util.CollectionUtils;
    import org.springframework.validation.BindingResult;
    import org.springframework.validation.ObjectError;
    import org.springframework.validation.annotation.Validated;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.validation.Valid;
    import java.util.List;
    
    /**
     * \* @author: AsherWu
     * \* Date: 2022/4/21
     * \* Description: 测试valid
     * \
     */
    @RestController
    @RequestMapping("valid")
    public class ValidController {
        private static final Logger logger = LoggerFactory.getLogger(ValidController.class);
    
        @PostMapping("addStudent")
        public AjaxResult testValid(@RequestBody @Valid StudentVO studentVO, BindingResult bindResult) {
          
            logger.info("学生信息,{}",studentVO);
            final List<ObjectError> allErrors = bindResult.getAllErrors();
            if(!CollectionUtils.isEmpty(allErrors)){
                logger.error(allErrors.get(0).getDefaultMessage());
            }
            return AjaxResult.success();
        }
    }
    
    
    

    分组校验

    在实际的开发中,新增表单和修改表单可能使用同一个类接收参数,但是我们在新增的时候需要对部分字段进行校验,在修改的时候对另外部分的字段进行校验,如果说新增和修改使用不同的类接收参数则不会有问题,但是更简便的做法就是进行分组校验

    1. 定义分组:首先就是定义分组,创建接口,例如Addgroup接口和UpdateGroup接口分别表示新增的时候需要校验和更新的时候需要校验
    2. 添加分组:在参数封装类的元素上添加分组例如,表示在新增的时候对学生年龄的最小值和最大值,非空进行校验
    @Max(value = 30, message = "学生年龄不能超过30!",groups = {AddGroup.class})
    @Min(value = 10, message = "学生年龄不能超过10!",groups = {AddGroup.class})
    @NotNull(message = "学生年龄不能为空!",groups = {AddGroup.class})
    private Integer age;
    
    1. 最重要的一点是在接口接收参数的校验注解中添加
    @PostMapping("addStudent")
    public AjaxResult testValid(@RequestBody @Validated(value = {AddGroup.class}) StudentVO studentVO, BindingResult bindResult) {
      // 如果接口层接收BindingResult校验的结果,则校验出错后不会抛出异常,由开发者自已决定相应的操作
      logger.info("学生信息,{}",studentVO);
      final List<ObjectError> allErrors = bindResult.getAllErrors();
      if(!CollectionUtils.isEmpty(allErrors)){
        logger.error(allErrors.get(0).getDefaultMessage());
      }
      return AjaxResult.success();
    }
    

    因为@Valid不支持分组校验,所以我们使用@Validated进行校验,这样我们就完成了一个简单的校验,如果接口参数指定的校验的分组,则不会对没有添加分组的元素进行校验,例如

    @Max(value = 30, message = "学生年龄不能超过30!",groups = {AddGroup.class})
    @Min(value = 10, message = "学生年龄不能超过10!",groups = {AddGroup.class})
    @NotNull(message = "学生年龄不能为空!",groups = {AddGroup.class})
    private Integer age;
    
    @NotNull(message = "学生性别不能为空!")
    private Integer sex;
    

    如果接口指定了@Validated(value = {AddGroup.class}),则不会对sex进行校验

    如果接口没有添加分组@Validated,则只校验sex,不会对age进行校验

  • 相关阅读:
    DLL导出类避免地狱问题的完美解决方案
    WorldWind源码剖析系列:影像图层类ImageLayer
    WorldWind源码剖析系列:插件类Plugin、插件信息类PluginInfo和插件编译器类PluginCompiler
    多线程中的锁系统(一)-基础用法
    [转]分布式计算框架综述
    C#自定义控件开发
    GDI+编程小结
    C#自定义控件
    WorldWind源码剖析系列:窗口定制控件类WorldWindow
    WorldWind源码剖析系列:四叉树瓦片集合类QuadTileSet
  • 原文地址:https://www.cnblogs.com/wuliqqq/p/16181909.html
Copyright © 2020-2023  润新知