目录
Spring服务器端数据校验(JSR-303)
数据校验(服务端数据校验)
数据校验:①客户端校验、②服务器端校验
在Spring MVC 框架中有两种方法可以验证输入数据,一种是利用Spring自带的验证框架,另一种是利用JSR 303实现验证,推荐使用JSR 303验证。
1.前端校验要做,目的是为了提高用户体验
2.后端校验也要做,目的是为了数据安全
(1)客户端校验
主要是通过过滤正常用户的误操作,是第一道防线,一般使用JavaScript代码实现。但是只有客户端校验是不够的,攻击者可以绕过客户端验证直接进行非法输入,这样可能会引起系统异常,为了确保数据的合法性,防止用户通过非正常手段提交错误信息,必须加上服务器端验证
(2)服务器端校验
整个应用阻止非法数据的最后一道防线,通过应用中的编程实现。服务器端验证对于系统的安全性、完整性、健壮性起到了至关重要的作用。在Spring MVC 框架中可以利用Spring自带的验证框架验证数据,也可以利用JSR 303实现数据验证
服务端校验
(1)控制层Conroller:校验页面请求的参数的合法性。在服务端控制层Controller校验,不区分客户端类型(浏览器、手机客户端、远程调用)
(2)业务层service(使用较多):主要校验关键业务参数,仅限于service接口中使用的参数
(3)持久层dao:一般是不校验的
Spring MVC服务器端校验方式
在Spring MVC中有两种方式可以验证输入
(1)利用Spring自带的验证框架
(2)利用JSR 303实现
JSR 303介绍
JSR 303(Java Specification Requests)是Java为Bean数据合法性校验所提供的标准框架。JSR 303通过在Bean属性上标注类似于@NotNull、@Max等标准的注解指定校验规则,并通过标准的验证接口对Bean进行验证。
Bean Validation为JavaBean验证定义了相应的元数据模型和API。缺省的元数据是Java Annotations,通过使用XML可以对原有的元数据信息进行覆盖和扩展。在应用程序中,通过使用 Bean Validation 或是你自己定义的 constraint(约束),例如 @NotNull, @Max, @ZipCode, 就可以确保数据模型(JavaBean)的正确性。constraint 可以附加到字段,getter 方法,类或者接口上面。对于一些特定的需求,用户可以很容易的开发定制化的 constraint。Bean Validation 是一个运行时的数据验证框架,在验证之后验证的错误信息会被马上返回。
JSR 303 的发布使得在数据自动绑定和验证变得简单,使开发人员在定义数据模型时不必考虑实现框架的限制。当然 Bean Validation 还只是提供了一些最基本的 constraint,在实际的开发过程中,用户可以根据自己的需要组合或开发出更加复杂的constraint。
下载:http://jcp.org/en/jsr/detail?id=303
官网下载:http://hibernate.org/validator
注意:Spring本身没有提供对JSR 303的实现,Hibernate Validator实现了JSR 303,所以必须在加入来自Hibernate Validator库的jar文件
导入其中三个即可,Spring将会自动加载并装配
hibernate-validator-版本.Final
jboss-logging-版本.CR
validator-api版本.GA
JSR 303内置约束
JSR 303不需要编写验证器,它定义一套可标注在成员变量、属性方法上的校验注解。
约束 | 说明 |
---|---|
@Null | 被注释的元素必须为null |
@NotNull | 被注释的元素必须不为null |
@AssertTrue | 被注释的元素必须为true |
@AssertFalse | 被注释的元素必须为false |
@Min(value) | 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 |
@Max(value) | 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 |
@DecimaMin(value) | 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 |
@DecimaMax(value) | 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 |
@Size(max,min) | 被注释的元素大小必须在指定的范围内 |
@Digits(integer,fraction) | 被注释的元素必须是一个数字,其值必须在课街火速的范围内 |
@Past | 被注释的元素必须是一个过去的日期 |
@Futrue | 被注释的元素必须是一个将来的日期 |
@Pattern(value) | 被注释的元素必须符合指定的正则表达式 |
Spring MVC支持JSR 303标准的校验框架,Spring的DataBinder在进行数据绑定时,可同时调用数据校验框架来完成数据校验工作,非常简单方便。在Spring MVC中,可以直接通过注解驱动的方式来进行数据校验。
Hibernate Validator附加的constraint
约束(constraint) | 说明 |
---|---|
被注释的元素必须是电子邮箱地址 | |
@Length | 被注释的字符串的大小必须在指定的范围内 |
@NotEmpty | 被注释的字符串的必须非空 |
@Range | 被注释的元素必须在合适的范围内 |
一个constraint通常由annotation和相应的constraint validator组成,它们是一对多的关系。也就是说可以有多个constraint validator对应一个annotation。在运行时,Bean Validation框架本身会根据被注释元素的类型来选择合适的constraint validator对数据进行验证。
有些时候,在用户的应用中需要一些更复杂的constraint。Bean Validation提供扩展constraint的机制。可以通过两种方法去实现。
(1)一种是组合现有的constraint来生成一个更复杂的constraint
(2)另外一种是开发一个全新的constraint
使用步骤
(1)导包
<!-- JSR 303 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.4.3.Final</version>
</dependency>
(2)配置校验器到处理器适配器
<!-- 校验器注入到处理器适配器中 -->
<mvc:annotation-driven validator="validator"/>
<!-- 校验器 -->
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<!-- Hibernate校验器 -->
<property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>
<!-- 指定校验使用的资源文件,在文件中配置校验错误信息,如果不指定则默认使用classpath下的ValidationMessages.properties -->
<property name="validationMessageSource" ref="messageSource"></property>
</bean>
<!-- 校验外置配置文件 -->
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<!-- 资源文件名 -->
<property name="basenames">
<list>
<value>classpath:ValidationMessages.properties</value>
</list>
</property>
<!-- 资源文件编码格式 -->
<property name="fileEncodings" value="utf-8"></property>
<!-- 对资源文件内容缓冲时间,单位秒 -->
<property name="cacheSeconds" value="120"></property>
</bean>
(3)增加校验规则
public class User {
@Length(min = 2, max = 5,message = "长度大于等于2小于等于5!")
private Integer id;
@NotNull(message = "{user.error.name.NotNull}")
private String name;
private String address;
private String email;
}
(4)ValidationMessages(文件编码格式跟JSP页面一致)
user.error.name.NotNull=姓名不能为空
(5)Handler控制器测试
@Controller
public class UserController {
@RequestMapping("/user")
public String UserTest(Model model, @Valid User user, BindingResult bindingResult) {
if (bindingResult.hasErrors()) { //有错误信息
List<ObjectError> allErrors = bindingResult.getAllErrors();
for (ObjectError objError : allErrors) {
// 输出错误信息
System.out.println(objError.getDefaultMessage());
}
model.addAttribute("allErrors", allErrors); //发送至页面
return "message";
}
// 没有错误信息,省略……
return "逻辑视图名";
}
}
Controller相关设置,model前添加@Valid注解;Controller的方法中@valid对应的@ModelAttribute参数与bindingResult之间不能有参数,它们必须紧挨在一起,否则报错;
需要在Handler(Controller)中加入BindingResult对象,接收校验出错信息;否则会直接报错。
@Valid是javax.validation里的。@Validated是@Valid的一次封装,是Spring提供的校验机制使用。
相比@Valid;@Validated提供了几个新功能
(1)可以通过groups对验证进行分组
(2)按照序列组来验证
(3)验证多个实体
分组校验
1.校验规则(增加组标识)
public class User {
private int id;
@NotEmpty(message = "{user.error.name.NotNull}", groups = UserVov1.class)
private String name;
@NotEmpty(message = "{user.error.address.NotNull}")
private String address;
@Email(message = "必须是邮箱!")
private String email;
// 省略getter/setter方法...
public interface UserVov1{};
public interface UserVov2{};
public interface UserVov3{};
}
2.定义一个接口类(增加分组)
@GroupSequence(value = {User.UserVov1.class,User.UserVov2.class})
public interface GroupbyName {
// 不需要定义任何方法,仅对校验进行分组
//目的:在pojo类中定义了校验规则;只校验部分内容
}
3.Handler(Controller)中使用
@RequestMapping("/groups")
public String groups(@Validated(value = GroupbyName.class) User user) {
return "groups_ok";
}
自定义验证类
(1)自定义注解
至少需要包含message()、groups()、payload()
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = { IsRightValidator.class })
public @interface IsRight {
boolean right() default true;
String message() default "这是验证失败的提示信息";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
(2)自定义注解校验器类
继承ConstraintValidator类<注解类,注解参数类型> ,重写两个方法(initialize:初始化操作、isValid:逻辑处理)
IsRight注解校验类
public class IsRightValidator implements ConstraintValidator<IsRight, String> {
private boolean right= false;
@Override
public void initialize(IsRight constraintAnnotation) {
right = constraintAnnotation.right();
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (right) {
return true;
} else {
return false;
}
}
}