1. Struts2的输入校验
1.1 输入校验概述
输入校验分为客户端校验和服务器端校验,客户端校验主要是过滤正常用户的误操作,主要通过JavaScript代码完成;服务器端校验是整个应用阻止非法数据的最后防线,主要通过在应用中编程实现。
1.1.1 客户端校验
大多数情况下,使用JavaScript进行客户端校验的步骤如下:
- 编写校验函数;
- 在提交表单的事件中调用校验函数;
- 根据校验函数来判断是否进行表单提交;
下面通过一个简单的示例讲解使用JavaScript进行客户端校验的方法,具体代码如下所示:
<%-- Created by IntelliJ IDEA. User: mairr Date: 17-12-5 Time: 下午10:07 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page language = "java" import = "java.util.*" pageEncoding="utf-8"%> <%@ taglib prefix="s" uri="/struts-tags"%> <html> <head> <title>注册界面</title> <script type="text/javascript" > // 去掉前后的空格 function deltrim(x) { while (x.length > 0 && x.charAt(0) == ' ') x = x.substring(1, x.length); while (x.length > 0 && x.charAt(x.length - 1) == ' ') x = x.substring(0, x.length - 1); return x; } // 非空验证 function isNULL(elem,message){ var va = deltrim(elem.value); if(va == " ") { alert(message); elem.focus(); return false; } return true; } // 验证帐号,帐号只能是小写字母数字,并且只能以字母开头 function validateId(){ var first = document.forms[0].id.value.charAt(0); var exp = /^[a-z0-9]+$/; if(isNULL(document.forms[0].id,"请输入帐号")){ // 验证非空 // 验证首字符 if((first >= 'a' && first <= 'z') || (first >= 'A' && first <= 'Z')){} else{ alert("帐号首字符必须是字母!"); document.forms[0].id.focus(); return false; } if(!exp.test(document.forms[0].id.value)){ alert("帐号必须是字母或者数字!"); document.forms[0].id.focus(); return false; } return true; } else{ return false; } } // 验证密码,密码要在8位以上,且需要有字母或者数字之外的字符 function validatepwd(){ var exp = /^[a-z0-9]+$/; if(isNULL(document.forms[0].pwd," 请输入密码")){ //验证非空 if(document.forms[0].pwd.value.length <= 8){ alert("密码大于8位"); document.forms[0].pwd.focus(); return false; }else{ if(exp.test(document.forms[0].pwd.value)){ alert("密码要有字母和数字之外的字符!"); document.forms[0].pwd.focus(); return false; } } }else{ return false; } if(document.forms[0].pwd.value != document.forms[0].repwd.value) { alert("两次密码不一样!"); document.forms[0].pwd.focus(); return false; } return true; } // 验证邮箱,右边为六位数子 function checkcode() { var exp = /^[0-9]+$/; if(isNULL(document.forms[0].ecode,"请输入邮编")){ // 验证非空 if(document.forms[0].ecode.value.length != 6){ alert("邮编为6位"); document.forms[0].ecode.focus(); return false; }else{ if(!exp.test(document.forms[0].ecode.value)){ alert("邮编为数字"); document.forms[0].ecode.focus(); return false; } } return true; }else{ return false; } } // 验证E-mail的基本格式 function checkEmail(){ var exp = /^w+((-w+)|(.w+))*@[A-Za-z0-9]+((.|-)[A-Za-z0-9]+)*.[A-Za-z0-9]+$/; if(isNULL(document.forms[0].email,"请输入Email")){ // 验证非空 if(!exp.test(document.forms[0].email.value)){ alert("Email格式错误"); document.forms[0].email.focus(); return false; } return true; }else{ return false; } } // 提交按钮 function gogo(){ if(validateId() && validatepwd() && checkcode() && checkEmail()){ document.forms[0].submit(); return true; } return false; } </script> </head>
<body> <s:form action ="" theme="simple"> <table> <tr> <td>登录帐号</td> <td><s:textfield name="id"/></td> </tr> <tr> <td>密码</td> <td><s:password name="pwd"/></td> </tr> <tr> <td>确认密码</td> <td><s:password name="repwd"/></td> </tr> <tr> <td>邮编</td> <td><s:textfield name = "ecode"/></td> </tr> <tr> <td>Mail</td> <td><s:textfield name = "email"/></td> </tr> <tr> <td><input type="button" value="提交" onclick="return gogo()" /></td> <td><s:reset value = "重置"/></td> </tr> </table> </s:form> </body>
</html>
上述的校验过程中,建立了一个Struts2的项目(前面几篇博文有介绍),JSP校验页面中运用了部分struts2的标签。由上述JSP程序,执行结果有如下几种情况:
(1) 页面:
(2) 客户端校验结果:
客户端校验可以过滤用户的错误操作,是第一道防线,一般使用JavaScript代码实现。仅有客户端校验是不够的。攻击者可以绕过客户端校验直接进行非法输入,这样会引起系统的异常,为了确保数据的合法性,防止用户通过非正常手段提交错误信息,所以必须加上服务器验证。
1.1.2 服务器端校验
服务器对于系统的安全性、完整性、健壮性起到至关重要的作用。Struts2框架提供了一套验证框架,通过验证框架能够非常简单和快速地完成输入校验。
在服务器端,对于输入校验Struts2提供了两种实现方法:一是采用手工编写代码实现;另外一种是,给予XML配置方式的实现(校验框架校验)。接下来详细介绍这两种方法。
1.2 手动编程校验
手动编程校验主要是通过在类中编写校验逻辑代码,有两种方式i:一是在Action类中重写validate()方法;二是在Action类中重写validateXxx()方法。
1.2.1 重写validate方法
validate()方法会校验Action中所有与execute()方法签名相同的方法。当某个数据校验失败时,在validate()方法中应该调用addFiledError()方法向系统fieldErrors添加校验失败信息。为了使用addFileError方法,Action类需要继承ActionSupport。
如果系统的fieldErrors包含失败信息,Struts2会将请求转发到名为input的result。在input视图中可以通过<s:fielderror/>标签失败信息。
下面通过一个简单示例讲解如何重写validate()方法进行输入校验。
(1) 创建一个JSP页面login.jsp,要求验证所有输入项不能为空、密码长度6~12位以及两次密码一样(不运用javascript的客户端校验),具体代码如下所示:
<%-- Created by IntelliJ IDEA. User: mairr Date: 17-12-6 Time: 下午9:39 To change this template use File | Settings | File Templates. --%> <%@ page language="java" import="java.util.*" pageEncoding="utf-8" %> <%@ taglib prefix="s" uri="/struts-tags" %> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <html> <head> <base href="<%=basePath%>"> </head> <body> <s:form action="login" theme="simple"> <table> <tr> <td>登陆帐号</td> <td><s:textfield name = "id"/></td> </tr> <tr> <td>密码</td> <td><s:password name = "pwd"/></td> </tr> <tr> <td>确认密码</td> <td><s:password name = "repwd"/></td> </tr> <tr> <td><s:submit value= "提交"/></td> <td><s:reset value = "重置"/></td> </tr> </table> <!--显示this.addFieldError("id","id不能为空")的信息--> <s:fielderror fieldName="id"/> <!--显示所有校验失败信息--> <s:fielderror/> </s:form> </body> </html>
(2) 创建Action类LoginAction.java,在该类中重写validate()方法,具体代码如下“
package action; import com.opensymphony.xwork2.ActionSupport; public class LoginAction extends ActionSupport { private String id; private String pwd; private String repwd; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } public String getRepwd() { return repwd; } public void setRepwd(String repwd) { this.repwd = repwd; } // 重写validate方法,进行输入校验,该方法在execute方法之前执行 @Override public void validate() { if (id == null || id.trim().equals(" ")) { this.addFieldError("id", "id不能为空"); } if (pwd == null || pwd.trim().equals(" ")) { this.addFieldError("pwd", "密码不能为空"); } else { if (pwd.length() < 6 || pwd.length() > 12) { this.addFieldError("pwdlength", "密码的长度在6~12位之间"); } } if (!pwd.equals(repwd)) { this.addFieldError("pwdsame", "两次密码不一致"); } } // 实现登陆业务处理 @Override public String execute() { return SUCCESS; } }
(3) 配置Struts2的action,具体代码如下:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN" "http://struts.apache.org/dtds/struts-2.5.dtd"> <struts> <package name="default" namespace="/" extends="struts-default"> <action name="login" class="action.LoginAction"> <result name="input">/login.jsp</result> </action> </package> </struts>
(4) web.xml配置
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
(5) 运行login.jsp页面,如果输入非法信息则输出校验结果如下所示:
1.2.2 重写validateXxx()方法
validateXxx()只会校验Action中方法名为Xxx()的方法,其中Xxx的第一个字母要大写。重写validateXxx()方法进行输入校验与重写validate()方法基本一样,唯一不同的就是校验的方法名不同。
将上面一个例子重写为validateXxx()方法进行输入校验,则需要修改Action类和配置文件。
- 修改后的Action类的代码如下:
package action; import com.opensymphony.xwork2.ActionSupport; public class LoginAction extends ActionSupport { private String id; private String pwd; private String repwd; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } public String getRepwd() { return repwd; } public void setRepwd(String repwd) { this.repwd = repwd; } // 重写validateLogin方法,进行输入校验 public void validateLogin() { if (id == null || id.trim().equals(" ")) { this.addFieldError("id", "id不能为空"); } if (pwd == null || pwd.trim().equals(" ")) { this.addFieldError("pwd", "密码不能为空"); } else { if (pwd.length() < 6 || pwd.length() > 12) { this.addFieldError("pwdlength", "密码的长度在6~12位之间"); } } if (!pwd.equals(repwd)) { this.addFieldError("pwdsame", "两次密码不一致"); } } // 实现登陆业务处理 public String login() { return SUCCESS; } }
- 修改后的配置文件struts.xml如下:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN" "http://struts.apache.org/dtds/struts-2.5.dtd"> <struts> <package name="default" namespace="/" extends="struts-default"> <action name="login" class="action.LoginAction" method="login"> <result name="input">/login.jsp</result> </action> </package> </struts>
- 结果是一致的:
1.2.3 输入校验的流程
经过前面讲解,可以知道,Struts2输入校验需要经过一下几个步骤:
- 类型转器对请求参数执行类型转换,并且把转换后的值赋给Action中的属性。
- 如果在执行转换过程中出现了异常,系统会把异常信息保存到ActionContext , conversionError拦截器将异常信息添加到fieldError里。不管类型转换是否出现异常,都会进入步骤三;
- 系统通过反射技术先调用Action中的validateXxx()方法,Xxx为方法名字。
- 再调用Action中的validate()方法;
- 经过上面的4个步骤,如果系统中的fieldError存在错误信息(即存放错误信息的集合的size大于0),系统自动将请求转发至名称为input的视图。如果系统中的fileError没有任何错误信息,系统将执行Action中的处理方法。
1.3 校验框架校验
使用Struts2校验框架的好处是将校验逻辑放到配置文件中,实现校验逻辑代码与业务逻辑代码的分离。使用基于校验框架校验方式实现输入校验时,Action也需要继承ActionSupport,并且提供校验文件。同样框架校验的方式也有2种:一是校验Action中所有与execute方法签名相同的方法;二是校验Action中某个与execute方法签名相同的方法。
1.3.1 Struts2内置的校验器
Struts2框架提供的内置校验器如下:(多看几遍,还蛮重要的)
- required:必填校验器,要求field的值不能为null;
- requiredstring:必填字符串校验器,要求field的值不能为null,并且长度大于0,默认情况下会对字符串去掉前后空格;
- stringlength:字符串长度校验器,要求field的值必须在指定的范围内,否则校验失败;miniLength参数指定最小长度,maxLength参数指定最大长度,trim参数指定校验field之前是否去掉字符串前后空格;
- regex:正则表达式校验器,检查被校验的field是否匹配一个正则表达式。expression参数指定正则表达式, caseSensitive参数指定进行正则表达式匹配时,是否区分大小写,默认值为true;
- int:整数校验器,要求field的整数值必须在指定范围内,mini指定最小值,max最大值;
- double:双精度浮点数校验器,要求field的双进度浮点数必须在指定范围内,mini指定最小值,max最大值;
- fieldexpression:字段OGNL表达式校验器,要求field满足一个OGNL表达式,expression参数指定一个OGNL表达式,该逻辑表达式基于ValueStack进行求值,返回true时校验通过,否则不通过。该校验器只能用于<filed-validator>;
- email:邮件地址校验器,要求如果field的值非空,则必须是合法的邮件地址;
- url:网址校验器,要求如果field的值非空,则必须是合法的URL地址i;
- date:日期校验器,要求field的日期值必须在指定的范围内,mini指定最小值,max指定最大值;
- conversion:转换校验器,指定在类型转换失败时,提示错误信息;
- visitor:用于校验Action中的复合属性,指定一个校验文件用于校验复合中的属性;
- expression:OGNL表达式表达式校验,expression参数指定OGNL表达式,该逻辑表达式基于ValueStack进行求值,返回true时校验通过,否则不通过,该校验器不用在字段校验器的配置中,只能用于<validator>.
1.3.2 常用内置校验器的配置
对于Struts2校验框架来说,一般有两种方式来配置校验器:
- 使用<validator>
- 使用<field-validator>
当<validator>的子节点中配置了<param name="fieldName">用于指定某个属性进行校验时,则达到的效果与<field-validator>是一样的。如下所示:(两种配置方式是等效的)
<!--校验user.id属性时,用<validator>来配置--> <validator type="required"> <s:param name="fieldName">user.id</s:param> <message>用户的ID不能为空!</message> </validator> <!--------方式二 -----> <field name="user.id"> <field-validator type ="required"> <message>用户的ID不能为空!</message> </field-validator>
</field>
下面简单介绍几种常用的内置校验器的配置示例:
(1) required(必填校验器)
<field-validator type ="required"> <message>用户的ID不能为空!</message> </field-validator>
(2) requiredstring(必填字符串校验器)
<field-validator type ="requiredstring"> <param name ="trim">true</param> <message>用户的ID不能为空!</message> </field-validator>
(3) stringlength(字符串长度校验器)
<field-validator type ="stringlength"> <param name = "maxlength">12</param> <param name = "minilength">6</param> <message>密码必须在6~12位之间</message> </field-validator>
(4) email(邮件地址校验器)
<field-validator type ="email"> <message>邮箱格式不正确</message> </field-validator>
(5) regex(正则表达式校验器)
<field-validator type ="regex"> <param name="expression"><![CDATA[^1[3578]d{9}$]]></param> <message>手机号格式不正确</message> </field-validator>
(6) int(整数校验)
<param type ="int"> <param name="max">100</param> <param name="mini">0</param> <message>年龄必须在0~100之间</message> </field-validator>
(7) 字段OGNL表达式校验器
<param type ="int"> <param name="expression"><![CDATA[imagefile.length() <=0 ]]></param> <message>文件不能为空</message> </field-validator>
基于Struts2框架的校验主要是以上内容,这是Struts2框架应用比较多的一个版块,比较重要的内容!