Spring Framework 验证有两种方式,一是 Spring 传统的,对需要验证的 POJO 对象编写一个对应的验证类(实现Validator接口);二是标准的 JSR-303/349 Bean Validation API,需要第三方的实现(Hibernate-validator);三是使用 JSR-349 中的 ScriptAssert。
下面以注册页面验证为例,用 JSR Bean Validation 实现基本字段验证,应用不同手段实现密码校验。
要实现验证的注册页面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="sf" uri="http://www.springframework.org/tags/form"%>
<html>
<head>
<meta charset="utf-8">
<title>Register</title>
<style type="text/css">
.error { color: red; font-size: 0.9em; font-weight: bold; }
</style>
</head>
<body>
<h1>Register</h1>
<sf:form method="POST" action="register.do" modelAttribute="registerCommand">
<fieldset>
<table cellspacing="0">
<tr>
<th><label for="username">Username</label></th>
<td><sf:input path="username" id="username" size="15" maxlength="15"/>
<sf:errors path="username" cssClass="error" />
</td>
</tr>
<tr>
<th><label for="fullname">Nickname</label></th>
<td><sf:input path="fullname" id="fullname" size="15"/>
<sf:errors path="fullname" cssClass="error" />
</td>
</tr>
<tr>
<th><label for="password">Password</label></th>
<td><sf:password path="password" id="password" size="30"/>
<sf:errors path="password" cssClass="error" />
</td>
</tr>
<tr>
<th><label for="confirmation">Password</label></th>
<td><sf:password path="confirmation" id="confirmation" size="30"/>
<sf:errors path="confirmation" cssClass="error" />
</td>
</tr>
<tr>
<th><label for="email">Email</label></th>
<td><sf:input path="email" id="email" size="30"/>
<sf:errors path="email" cssClass="error" />
</td>
</tr>
<tr>
<th></th>
<td><input name="commit" type="submit" value="I accept. Create my account."></td>
</tr>
</table>
</fieldset>
</sf:form>
</body>
</html>
|
基本配置
pom.xml
1
2
3
4
5
|
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.1.0.Final</version>
</dependency>
|
servlet.xml
1
2
3
4
5
|
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="errormess"/>
</bean>
|
resources 目录下 errormess.properties
1
2
3
4
5
6
7
8
|
Size=the {0} field must be between {2} and {1} characters long
Size.loginCommand.name=Name must be between {2} and {1} characters
password.confirmation.error=Password don't confirm
password.null=Field cannot be left blank
password.confirmation.null=Field cannot be left blank
NotEmpty=Field cannot be left blank
NotNull=Field cannot be left blank
|
验证对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public class RegisterCommand {
// omit getter and setter
@Size(min=5, max=20)
@Pattern(regexp="^[a-zA-Z0-9]+$", message="Username must be alphanumeric with no spaces")
private String username;
private String fullname;
@Size(min=6, max=20)
private String password;
@Size(min=6, max=20)
private String confirmation;
@Email
private String email;
}
|
方法一,编写自定义 Validator
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public class RegistrationValidator implements Validator{
@Override
public boolean supports(Class<?> aClass) {
return RegistrationValidator.class.isAssignableFrom(aClass);
}
@Override
public void validate(Object o, Errors errors) {
RegisterCommand registerCommand = (RegisterCommand) o;
if (!registerCommand.getConfirmation().equals(registerCommand.getPassword()))
errors.rejectValue("confirmation", "password.confirmation.error");
}
}
|
控制器注入自定义的Validator,HomeController.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
@Controller
public class HomeController {
private static String salt = "SECRET";
//private static final int DEFAULT_SPITTERS_PER_PAGE = 25;
@Autowired
private UserService userService;
@Autowired
private RegistrationValidator registrationValidator;
@RequestMapping(value="/", method = RequestMethod.GET)
public String loginCommand(Model model){
model.addAttribute("loginCommand", new LoginCommand());
return "home";
}
@RequestMapping(value="/", method = RequestMethod.POST)
public ModelAndView loginCheck(@ModelAttribute("loginCommand") @Valid LoginCommand loginCommand,
BindingResult result,
HttpServletRequest request, Model model) {
String name = loginCommand.getUsername();
String pass = loginCommand.getPassword();
if (result.hasErrors()) {
return new ModelAndView("home");
}
String secretPass = DigestUtils.md5Hex(salt + pass);
boolean valid = userService.hasMatchUser(name, secretPass);
if (!valid) {
return new ModelAndView("redirect:/", "fatal", "Username or password error!");
} else {
User user = userService.findUserByName(name);
user.setLastIp(request.getRemoteAddr());
user.setLastVisit(new Date());
userService.loginSuccess(user);
request.getSession().setAttribute("user", user);
String userPage = "redirect:user/" + name;
return new ModelAndView(userPage);
}
}
@RequestMapping(value="/user/{name}")
public ModelAndView userPage(@PathVariable String name, HttpServletRequest request, Model model) {
model.addAttribute("name", name);
return new ModelAndView("sucess");
}
@RequestMapping(value="register.html", method=RequestMethod.GET)
public String register(Model model) {
model.addAttribute("registerCommand", new RegisterCommand());
return "register";
}
@RequestMapping(value="register.do", method=RequestMethod.POST)
public String register(@ModelAttribute("registerCommand") @Valid RegisterCommand registerCommand,
BindingResult result,
HttpServletRequest request, Model model) {
registrationValidator.validate(registerCommand, result);
if (result.hasErrors()) {
return "register";
}
User user = new User();
user.setUserName(registerCommand.getUsername());
user.setPassword(DigestUtils.md5Hex(salt + registerCommand.getPassword()));
user.setLastIp(request.getRemoteAddr());
user.setLastVisit(new Date());
userService.insertUser(user);
model.addAttribute("loginCommand", new LoginCommand());
return "home";
}
}```
## 方法二,编写注解类
CheckPassword
```java
@Target({ TYPE, ANNOTATION_TYPE})
@Retention(RUNTIME)
@Constraint(validatedBy = CheckPasswordValidator.class)
@Documented
public @interface CheckPassword {
//默认错误消息
String message() default "";
//分组
Class<?>[] groups() default { };
//负载
Class<? extends Payload>[] payload() default { };
//指定多个时使用
@Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Documented
@interface List {
CheckPassword[] value();
}
}
public class CheckPasswordValidator implements ConstraintValidator<CheckPassword, RegisterCommand> {
@Override
public void initialize(CheckPassword constraintAnnotation) {
}
@Override
public boolean isValid(RegisterCommand user, ConstraintValidatorContext context) {
if(user == null) {
return true;
}
//两次密码不一样
if (!user.getPassword().trim().equals(user.getConfirmation().trim())) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate("{password.confirmation.error}")
.addPropertyNode("confirmation")
.addConstraintViolation();
return false;
}
return true;
}
}
|
现在只需要在验证对象 RegisterCommand 上加上注解 @CheckPassword 即可
这里 https://github.com/jirutka/validator-spring ,有人写了一个更加通用的解决方案,可以在注解中自定义验证方法
1
2
3
4
5
6
7
8
|
@SpELAssert(value = "password.equals(passwordVerify)",
applyIf = "password || passwordVerify",
message = "{validator.passwords_not_same}")
public class User {
private String password;
private String passwordVerify;
}
|
方法三,使用SpELAssert
JSR-349 Bean Validation API 1.1
1
2
|
@ScriptAssert(script = "_this.confirmation==_this.password", lang = "javascript", alias = "_this",
message="Password don't confirm")
|
参考
http://codetutr.com/2013/05/28/spring-mvc-form-validation/
http://haohaoxuexi.iteye.com/blog/1812584
http://www.javacodegeeks.com/2013/06/spring-mvc-validator-and-initbinder.html
https://github.com/jirutka/validator-spring