在web的開發中,我們經常需要對參數進行校驗,使用最多的即爲在前端頁面通過js進行校驗,但是對於安全性要求較高的我們通常會在服務端進行校驗。
在服務端進行校驗主要包括如下內容:
控制層-Controller:主要校驗頁面請求參數的合法性(比如長度、是否爲空等),在Controller進行校驗,是不區分客戶端類型的(瀏覽器、手機客戶端等)。
業務層-Service:在業務層主要校驗的是關鍵業務的參數,僅限於Service層中使用的參數校驗。
持久層-Dao層:在這一層一般是不進行校驗的(使用Mybatis時可在mapper.xml中通過Sql片段以及sql的if判斷進行一些校驗)。
在SpringMVC中,我們使用Hibernate的validation框架進行校驗,這裏需要注意,Validation與Hibernate並沒有半毛錢的關係。
接下來說一下校驗思路:
首先頁面提交請求的參數,請求發送到Controller後,Controller使用Validation進行相關的校驗,如果校驗出錯,則將錯誤信息展現到頁面,反之系統正常運行。
在本次講解中,同樣的通過一個用戶登錄案例進行講解(用戶名長度必須爲1-3,密碼不能爲空),當校驗出錯後,顯示出錯信息。
開發如下:
1、加入相關jar包
百度雲下載:點擊下載
2、編寫出錯的資源文件LoginValidationMessages.properties
#添加校驗錯誤提交信息
user.username.length.error=請輸入1-3長度的用戶名...
user.password.isNULL=您輸入的密碼爲空...
3、接下來我們需要在pojo文件內對變量進行校驗
package com.sw.po;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotEmpty;
/*
*@Author swxctx
*@time 2017年5月15日
*@Explain:用戶表po對象
*id:編號
*username:用戶名
*password:密碼
*/
public class User {
private int id;
//長度在1-3之間
@Length(min=1,max=3,message="{user.username.length.error}")
private String username;
//非空校驗
@NotEmpty(message="{user.password.isNULL}")
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
如上代碼所示,將username的長度限定爲1-3,同時密碼不能爲空,如果出錯,則在登錄界面顯示出錯信息,若正常則進行正常的登錄校驗。
在hibernate的校驗框架中,常用的註解如下:
@Email 被註釋的元素必須是電子郵箱地址
@Length 被註釋的字符串的大小必須在指定的範圍內
@NotEmpty被註釋的字符串的必須非空
@Range 被註釋的元素必須在合適的範圍內
4、接下來我們需要在springmvc.xml文件中進行相關的配置,首先需要配置校驗信息的錯誤配置文件:
<!-- 校驗錯誤信息配置文件 -->
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<!-- 資源文件名 -->
<property name="basenames">
<list>
<value>classpath:LoginValidationMessages</value>
</list>
</property>
<!-- 資源文件編碼格式 -->
<property name="fileEncodings" value="utf-8"></property>
<!-- 資源文件內容緩存時間 -->
<property name="cacheSeconds" value="120"></property>
</bean>
接下來需要配置校驗器:
<!-- 數據校驗-對用戶輸入的用戶名進行長度驗證,對密碼進行非空校驗 -->
<bean id="validator"
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<!-- 使用Hibernate校驗器 -->
<property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>
<!-- 指定使用的資源文件 -->
<property name="validationMessageSource" ref="messageSource"></property>
</bean>
配置好以後需要將其引用到適配器與映射器中:
<!-- 配置適配器與映射器-通過drivern進行綜合 -->
<mvc:annotation-driven validator="validator"></mvc:annotation-driven>
5、校驗器相關的配置都已經做好,接下來我們需要在Controller文件中進行使用校驗:
package com.sw.controller;
import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import com.sw.container.SwServiceProvider;
import com.sw.po.User;
import com.sw.service.UserService;
/*
*@Author swxctx
*@time 2017年5月16日
*@Explain:完成登錄相關工作
*/
@Controller
public class LoginController{
//vo對象
//UserForm userForm = new UserForm();
//service
UserService userService = (UserService) SwServiceProvider.getService(UserService.SERVICE_NAME);
//登錄驗證
@RequestMapping("/logincheck")
public ModelAndView loginCheck(@Validated User user,BindingResult bindingResult)throws Exception{
String pass = userService.findLoginCheck(user.getUsername());
ModelAndView modelAndView = new ModelAndView();
//獲取校驗信息
if(bindingResult.hasErrors()){
//輸出
List<ObjectError> errors = bindingResult.getAllErrors();
for(ObjectError objectError:errors){
//輸出錯誤信息
System.out.println(objectError.getDefaultMessage());
//將錯誤信息傳到頁面
}
modelAndView.addObject("allErrors", errors);
modelAndView.setViewName("forward:/login.jsp");
}else{
//判斷
if(user.getPassword().equals(pass)){
//modelAndView.setViewName("dataAll.action");
//modelAndView.setView(new RedirectView("dataAll.action",false));
modelAndView.setViewName("redirect:/data/dataAll.action");
}else{
modelAndView.setViewName("/login/err");
}
}
return modelAndView;
}
}
如上代碼所示,通過對用戶輸入的用戶名與密碼進行校驗,當校驗出錯後在登錄界面顯示錯誤信息,反之進一步的進行登錄驗證。
注意:
在Controller的開發中,我們在需要校驗的參數前面加入@Validated,在之後加入BindingResult用於獲取錯誤信息,註解與BindResult是配套使用的,並且我們需要注意其順序也是固定的。
6、最後我們需要在jsp登錄界面將錯誤信息展示出來,通過jsp標籤<c:if>判斷是否存在錯誤信息,若存在錯誤則通過<c:forech>將錯誤信息輸出顯示到頁面,如下所示:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>系統登陸</title>
</head>
<body>
<!-- 顯示錯誤信息 -->
<div style="width:100%;text-align:center">
<div style="width:100%;text-align:center">
<c:if test="${allErrors!=null}">
<c:forEach items="${allErrors}" var="error">
${error.defaultMessage}<br/>
</c:forEach>
</c:if>
</div>
<br/>
<div style="width:100%;text-align:center">
<form action="${pageContext.request.contextPath }/logincheck.action" method="post">
<input type="text" name="username" value="${user.username}" placeholder="用戶名"/><br/>
<input type="password" name="password" value="${user.password}" placeholder="密碼"/><br/>
<input type="submit" value="登陸"/>
</form>
</div>
</div>
</body>
</html>
至此,關於校驗器的開發已經完成了,我們測試一下:
如上圖所示,輸入的用戶名已經遠遠超出了3位,同時密碼爲空,點擊登錄,若頁面顯示錯誤信息則表示我們開發的校驗器校驗成功:
如上,在頁面輸出了我們配置的錯誤信息,則表示校驗器配置成功。
到這裏我們的基本校驗器已經完成,但是這在開發中是並不完美的,我們在pojo中已經完全限定了校驗(即對用戶名與密碼都進行校驗),但是在開發時,我們的很多Controller在使用pojo時,有的Controller並不需要對其進行校驗。
面對上面的問題,我們可以使用分組校驗的方法,即對pojo的成員進行分組,在Controller中校驗時使用分組,則可以對指定的成員進行校驗,而不是全部進行校驗。
下面我們通過上述案例進行改進。
1、首先我們需要定義一個接口,此接口用於分組,但是此接口不需要實現任何東西,如下:
package com.sw.validator.group;
/*
*@Author swxctx
*@time 2017年5月24日
*@Explain:分組校驗
*/
public interface ValidGroupOne {
/*注意:此接口並不需要編寫任何代碼
此分組用於校驗用戶名的長度
*/
}
2、下一步進行pojo的開發,引入分組的概念:
//長度在1-3之間
@Length(min=1,max=3,message="{user.username.length.error}",groups={ValidGroupOne.class})
private String username;
//非空校驗
@NotEmpty(message="{user.password.isNULL}")
private String password;
如上代碼所示,將username放到了ValidGroupOne的分組中,密碼沒有放置到分組中,Controller在使用是若沒有指定分組只會對密碼進行校驗,若指定了分組,則只會對用戶名進行校驗。
3、Controller中,只需要在@Validated中加入分組屬性:
//登錄驗證
@RequestMapping("/logincheck")
public ModelAndView loginCheck(@Validated(value={ValidGroupOne.class}) User user,BindingResult bindingResult)throws Exception{}
如上,分組校驗已經完成,接下來進行測試:
如上所示,用戶名的長度已經超過了3位,密碼依然爲空,如果校驗成功,則只會顯示用戶名長度錯誤,並不會顯示密碼爲空的錯誤,在分組內並沒有對密碼進行校驗,點擊登錄,結果如下:
如上所示,登錄後提示用戶名長度錯誤,密碼並沒有校驗,則說明分組校驗已經成功。
結語:在SpringMVC的開發中,很多時候是需要用到校驗的,我們也經常在服務端進行校驗,除了我們本章講解的hibernate的校驗框架外,我們還可以使用JSR框架進行校驗,原理都是差不多的。