Spring Security技术栈开发企业级认证与授权(三)表单校验以及自定义校验注解开发
2018-03-26 12:30
701 查看
Hibernate不仅仅为操作数据库提供了解决方案,还为数据校验提供了解决方案——
Hibernate Validator。本篇博客将介绍常用的
Validator注解的使用以及在
Validator不满足实际需求的情况下如何使用自定义
Validator来实现数据校验。
一、常见的数据校验注解
首先我们需要在项目的POM文件中添加
Hibernate Validator的依赖才可以使用它的数据校验器进行数据校验。由于
Spring Boot已经将
Hibernate Validator集成到了
spring-boot-starter-web包里,所以这里不需要额外引用
Hibernate Validator依赖。常用的校验注解下表所示:
注解 | 说明 |
---|---|
@NotNull | 值不能为空 |
@Null | 值必须为空 |
@Pattern(regex=) | 字符串必须匹配正则表达式 |
@Size(min=, max=) | 集合元素数量必须在min和 max之间 |
@CreditCardNumber(ignoreNonDigitCharaters=) | 字符串必须是信用卡号(美国标准信用卡) |
字符串必须是Email地址 | |
@Length(min=, max=) | 字符串长度必须在min和 max之间 |
@NotBlank | 字符串必须有字符 |
@NotEmpty | 字符串不为null,集合必须有元素 |
@Range(min=, max=) | 数字必须在min和 max之间 |
@SafeHtml | 字符串是安全的HTML |
@URL | 字符串是合法的URL |
@AssertFalse | 值必须是false |
@AssertTrue | 值必须是true |
@DecimalMax(value=, inclusive=) | 如果inclusive=true,那么值必须大于等于 value,如果 inclusive=false,那么值必须大于value |
@DecimalMin(value=, inclusive=) | 如果inclusive=true,那么值必须小于等于 value,如果 inclusive=false,那么值必须小于 value |
@Digits(integer=, fraction=) | 数字格式检查,integer是指整数部分最大长度, fraction是指小数部分最大长度 |
@Future | 值必须是未来的日期 |
@Past | 值必须是过去的日期 |
@Max(value=) | 值必须小于等于value指定的值,不能注释在字符串类型属性上 |
@Min(value=) | 值必须大于等于value指定的值,不能注释在字符串类型属性上 |
@NotNull、
@NotEmpty、
@NotBlank3个注解的区别:
@NotNull任何对象的
value不能为
null
@NotEmpty集合对象的元素不为0,即集合不为空,也可以用于字符串不为
null
@NotBlank只能用于字符串不为
null,并且字符串
trim()以后
length要大于0
其实以上的每个注解都有三个共同的属性,因为他们都遵循
JSR 303规范:
String message() default "{org.hibernate.validator.constraints.xxx.message}"; Class<?>[] groups() default { }; Class<? extends Payload>[] payload() default { };
message提供校验失败后的错误消息;
groups分组验证
payload承载元数据
为了测试注解的作用,我在
User类的属性上加了部分注解,如下所示:
@NotEmpty(message = "用户名不能为空") private String username; @NotEmpty(message = "密码不能为空") private String password; @Past(message = "生日必须是过去的日期") private Date birthday;
这里我写一个创建用户的测试用例和Controller,并人为设置不符合要求的数据,测试用例代码如下:
@Test public void create3() throws Exception { Date date = new Date(LocalDateTime.now().plusYears(1).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); String content = "{\"username\":null,\"password\":null,\"birthday\":" + date.getTime() + "}"; mockMvc.perform(MockMvcRequestBuilders.post("/user3") .contentType(MediaType.APPLICATION_JSON_UTF8) .content(content)) .andExpect(MockMvcResultMatchers.status().isOk()) .andExpect(MockMvcResultMatchers.jsonPath("$.id").value(3)); }
为了测试效果,我设置的时间不是过去的时间,而是未来一年的时间,
LocalDateTime.now()获取当前时间,
plusYears(1)加上一年,
atZone(ZoneId.systemDefault())设置当前时区为系统默认时区,最后再毫秒化。
Controlle 4000 r方法为:
@PostMapping("/user3") public User create3(@RequestBody @Valid User user, BindingResult bindingResult) { if (bindingResult.hasErrors()) { bindingResult.getAllErrors().forEach(error -> System.out.println(error.getDefaultMessage())); } user.setId(3); return user; }
这里对错误消息进行了循环打印,打印结果是:
用户名不能为空 生日必须是过去的日期 密码不能为空
@Valid注解在数据封装之间会对数据的合法性进行校验,并将校验的错误结果存储在
BindingResult对象中。
二、自定义校验注解
以上的所有注解都是Java给我们提供的,其实他们往往只能校验一些简单的值,在实际开发中,我们面临的校验可能会很复杂,所以校验逻辑往往需要我们自己来写,这时候就需要我们自定义校验注解了。接下来我以校验身份证号码的案例来说明如何实现自定义的校验注解。
一般来说,自定义校验注解的开发步骤有以下几步:
第一步: 编写校验注解,但是需要注意的是,自定义的校验注解也得和其他
Java提供的校验注解一样,必须拥有
message、
groups、
payload三个属性。
第二步: 编写自定义校验的逻辑实体类,这个类必须实现
ConstraintValidator这个接口,这样才可以被注解用来校验。
第三步: 编写具体的校验逻辑。
编写注解:
package com.lemon.security.web.validator; import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 自定义身份证号码校验注解 * * @author lemon * @date 2018/3/31 下午7:43 */ @Target({ElementType.METHOD, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = IdCardValidator.class) public @interface IsIdCard { String message(); Class<?>[] groups() default { }; Class<? extends Payload>[] payload() default { }; }
对上面的代码进行如下解释:
message、
groups、
payload三个属性是必须的,可以参考@NotNull等注解;
@Target注解是指定当前自定义注解可以使用在哪些地方,这里仅仅让他可以使用在方法上和属性上;
@Retention指定当前注解保留到运行时;
@Constraint指定了当前注解使用哪个类来进行校验。
编写注解校验逻辑类:
package com.lemon.security.web.validator; import com.lemon.security.web.service.IdCardValidatorService; import org.springframework.beans.factory.annotation.Autowired; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; /** * 校验注解的校验逻辑 * * @author lemon * @date 2018/3/31 下午7:57 */ public class IdCardValidator implements ConstraintValidator<IsIdCard, String> { @Autowired private IdCardValidatorService idCardValidatorService; /** * 校验前的初始化工作 * * @param constraintAnnotation 自定义的校验注解 */ @Override public void initialize(IsIdCard constraintAnnotation) { String message = constraintAnnotation.message(); System.out.println("用户自定义的message信息是:".concat(message)); } /** * 具体的校验逻辑方法 * * @param value 需要校验的值,从前端传递过来 * @param context 校验器的校验环境 * @return 通过校验返回true,否则返回false */ @Override public boolean isValid(String value, ConstraintValidatorContext context) { return idCardValidatorService.valid(value); } }
对以上代码进行如下解释:
校验身份证合法性的实体类
IdCardValidator实现了接口
ConstraintValidator,其后面的第一个泛型是指为哪个注解提供校验服务,第二个泛型是指需要校验的值的类型;
它需要实现两个方法,第一个是初始化方法,第二个是校验的逻辑方法,在启动校验方法之前,都会进行初始化,可以在初始化方法中初始一些数据,比如获取用户自定义
message内容;第二个方法的第一个参数是需要被校验的值,第二个参数是校验的上下文环境;
一般的开发过程中,往往将校验逻辑抽取成为一个
Service服务,并通过
Spring的
DI注入到这个校验类中,需要注意的是,这个校验类上并不需要添加
Spring的
Component等注解,
Spring可以自动将校验逻辑服务类实例对象注入到这个类中。在这里就注入了
IdCardValidatorService实现类对象。
编写注解校验逻辑接口和实现类:
接口:
package com.lemon.security.web.service; /** * @author lemon * @date 2018/3/31 下午8:11 */ public interface IdCardValidatorService { /** * 身份证号校验,支持18位、15位和港澳台的10位 * * @param value 需要被校验的值 * @return 校验通过返回true,否则返回false */ boolean valid(String value); }
实现类:
package com.lemon.security.web.service.impl; import cn.hutool.core.util.IdcardUtil; import com.lemon.security.web.service.IdCardValidatorService; import org.springframework.stereotype.Service; /** * @author lemon * @date 2018/3/31 下午8:14 */ @Service public class IdCardValidatorServiceImpl implements IdCardValidatorService { @Override public boolean valid(String value) { return IdcardUtil.isValidCard(value); } }
这里的校验逻辑采用的是
Hutool提供的工具包进行校验的,具体可以参考它的文档。
这就完成了自定义校验注解的完整案例编写,接下来进行提供
RESTful风格的
API进行测试。在测试之前,请在原来的
User类上加上
idCard属性,并加上
@IsIdCard注解。
@IsIdCard(message = "身份证号码必须是大陆的18位或者15位,或者是港澳台的10位") private String idCard;
测试方法:
@Test public void create4() throws Exception { Date date = new Date(LocalDateTime.now().plusYears(1).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); String content = "{\"username\":null,\"password\":null,\"birthday\":" + date.getTime() + ",\"idCard\":\"12345678\"}"; mockMvc.perform(MockMvcRequestBuilders.post("/user4") .contentType(MediaType.APPLICATION_JSON_UTF8) .content(content)) .andExpect(MockMvcResultMatchers.status().isOk()) .andExpect(MockMvcResultMatchers.jsonPath("$.id").value(4)); }
Controller方法:
@PostMapping("/user4") public User create4(@RequestBody @Valid User user, BindingResult bindingResult) { if (bindingResult.hasErrors()) { bindingResult.getAllErrors().forEach(error -> System.out.println(error.getDefaultMessage())); } user.setId(4); return user; }
在上面的测试方法中,随便设置了一个不合法的身份证号码,显然是会校验失败的,最后的打印结果是:
用户自定义的`message`信息是:身份证号码必须是大陆的18位或者15位,或者是港澳台的10位
身份证号码必须是大陆的18位或者15位,或者是港澳台的10位
用户名不能为空 生日必须是过去的日期 密码不能为空
请认真思考上面的一个自定义校验注解的流程,可以轻松掌握在后期的开发中,使用注解来实现校验,而不是写许多重复的校验逻辑代码。
Spring Security技术栈开发企业级认证与授权系列文章列表:
Spring Security技术栈开发企业级认证与授权(一)环境搭建
Spring Security技术栈开发企业级认证与授权(二)使用Spring MVC开发RESTful API
Spring Security技术栈开发企业级认证与授权(三)表单校验以及自定义校验注解开发
Spring Security技术栈开发企业级认证与授权(四)RESTful API服务异常处理
Spring Security技术栈开发企业级认证与授权(五)使用Filter、Interceptor和AOP拦截REST服务
Spring Security技术栈开发企业级认证与授权(六)使用REST方式处理文件服务
Spring Security技术栈开发企业级认证与授权(七)使用Swagger自动生成API文档
示例代码下载地址:
项目已经上传到码云,欢迎下载,内容所在文件夹为
chapter003。
相关文章推荐
- Spring Security技术栈开发企业级认证与授权
- Spring Security技术栈开发企业级认证与授权
- JAVAWEB开发之Servlet3.0新特性的使用以及注解的详细使用和自定义注解的方法、动态代理的使用、利用动态代理实现细粒度的权限控制以及类加载和泛型反射
- 利用jquery.validate以及bootstrap的tooltip开发气泡式的表单校验组件
- Spring Security技术栈开发企业级认证与授权(一)环境搭建
- 【Android应用开发详解】第01期:第三方授权认证(一)实现第三方授权登录、分享以及获取用户资料
- Spring Security技术栈开发企业级认证与授权(四)RESTful API服务异常处理
- 【Android应用开发详解】第01期:第三方授权认证(一)实现第三方授权登录、分享以及获取用户资料
- 【Android应用开发详解】第01期:第三方授权认证(一)实现第三方授权登录、分享以及获取用户资料
- 【Android应用开发详解】第01期:第三方授权认证(一)实现第三方授权登录、分享以及获取用户资料
- 【Android应用开发详解】第01期:第三方授权认证(一)实现第三方授权登录、分享以及获取用户资料
- hibernate validator自定义校验注解以及基于服务(服务组)的校验
- web安全 - - 授权的配置以及实现基于表单的认证
- 【Android应用开发详解】第01期:第三方授权认证(一)实现第三方授权登录、分享以及获取用户资料
- angular2+内置表单校验以及自定义表单校验
- 【Android应用开发详解】第01期:第三方授权认证(一)实现第三方授权登录、分享以及获取用户资料
- 【Android应用开发详解】第01期:第三方授权认证(一)实现第三方授权登录、分享以及获取用户资料
- 【Android应用开发详解】第01期:第三方授权认证(一)实现第三方授权登录、分享以及获取用户资料
- Spring MVC代码实例系列-06:Spring MVC配置Hibernate-Validator以及自定义校验注解
- shiro- session,自定义FormAuthenticationFilter(表单认证器)-->实现验证码校验