servlet硬编码方式进行输入校验:
客户端的输入校验:
<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>请输入您的注册信息</title>
<meta name="website" content="http://www.crazyit.org" />
<script type="text/javascript">
//校验表单的JavaScript函数
function validate(form)
{
//定义错误字符串
var errStr = "";
//依次取出表单中的四个表单域的值
var username = trim(form.username.value);
var pass = trim(form.pass.value);
var age = trim(form.age.value);
var birth = trim(form.birth.value);
//判断用户名不能为空
if (username == "" || username == null)
{
errStr += "您的用户名必须输入";
}
//判断用户名必须是数字和字母,且长度必须为4到25之间
else if (!/^\w{4,25}$/.test(username))
{
errStr += "\n您的用户名必须是字母和数字,且长度在4到25之间";
}
//判断密码必须输入
if (pass == "" || pass == null)
{
errStr += "\n您的密码必须输入";
}
//判断密码必须是数字和字母,且长度必须为4到25之间
else if (!/^\w{4,25}$/.test(pass))
{
errStr += "\n您的密码必须是字母和数字,且长度在4到25之间";
}
//判断年龄必须输入
if (age == "" || age == null)
{
errStr += "\n您的年龄必须输入";
}
//判断年龄必须是一个有效的年龄
else if (!/^[0-1]?[0-9]?[0-9]$/.test(age))
{
errStr += "\n您的年龄必须为整数,且必须是一个有效的年龄值";
}
//判断生日必须输入
if (birth == "" || birth == null)
{
errStr += "\n您的生日必须输入";
}
//判断生日必须是一个有效的日期,且只能是19xx年,或者20xx年
else if(!/^19\d\d\-[0-1]\d\-[0-3]\d$/.test(birth)
&& !/^20[0-1]\d\-[0-1]\d\-[0-3]\d$/.test(birth))
{
errStr += "\n您的生日格式不正确,格式:yyyy-MM-DD";
}
//如果错误字符串为空,表明客户端校验通过
if (errStr == "")
{
return true;
}
//客户端校验没有通过,通过警告框输出校验失败提示
else
{
alert(errStr);
return false;
}
}
//一个简单的截去字符串前后空格的函数
function trim(s)
{
return s.replace("/^\s*/" , "")
.replace("/\s*$/" , "")
}
</script>
</head>
<body>
<h1>请输入您的注册信息</h1>
<font color="red">
${requestScope.error}
</font>
<form method="post" action="regist" οnsubmit="return validate(this);">
用户名:<input type="text" name="username" /><br />
密 码:<input type="password" name="pass"><br />
年 龄:<input type="text" name="age"><br />
生 日:<input type="text" name="birth"><br />
<input type="submit" value="注册">
</form>
</body>
</html>
上面增加的是客户端的输入校验,如果浏览器输入不符合要求的话,将无法通过校验。但是这仅仅是不能通过该页面的提交请求,攻击者可能用其他的方式提交,可能是使用底层的socket通信。也可以保存源文件,并修改其action来绕过页面校验。
就上面的代码,可以如下修改:
<form action="http://localhost:8888/ClientValidate/regist" method="post">//调用Servlet
这样简单的修改就能绕过页面校验,因此,服务器端的校验就成了最后一道防线。
下面是处理该请求的一个Servlet。
package lee;
import javax.servlet.ServletException;
import javax.servlet.ServletContext;
import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.io.IOException;
import java.util.*;
import java.text.*;
public class Regist extends HttpServlet
{
//Servlet的服务响应方法
public void service(HttpServletRequest request,
HttpServletResponse response)throws IOException
{
//设置解码格式
request.setCharacterEncoding("GBK");
//下面4行依次获取4个参数,获取的参数全部为字符串
String name = request.getParameter("username");
String pass = request.getParameter("pass");
String strAge = request.getParameter("age");
String strBirth = request.getParameter("birth");
//下面进行类型转换
//字符串类型向整型转换
int age = Integer.parseInt(strAge);
//使用SimpleDateFormat将字符串向日期转换
//指定转换格式,日期字符串,必须按yyyy-MM-DD的格式输入
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-DD");
Date birth = null;
try
{
birth = sdf.parse(strBirth);
}
catch (Exception e)
{
}
//将类型转换后值封装成UserBean值对象
UserBean user = new UserBean(name , pass , age , birth);
//用Servlet直接输出
response.setContentType("text/html;charset=GBK");
//获得页面输出流
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<head>");
//输出页面标题
out.println(" <title>类型转换页面</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>类型转换页面</h1>");
//下面输出值对象user的4个属性值
out.println("用户的用户名:" + user.getName() + "<br />");
out.println("用户的密码:" + user.getPass() + "<br />");
out.println("用户的年龄:" + user.getAge() + "<br />");
out.println("用户的生日:" + user.getBirth() + "<br />");
out.println("</body>");
out.println("</html>");
}
public void destroy()
{
super.destroy();
}
}
Servlet硬编码方式很麻烦,Struts 2提供了基于验证框架的输入校验,在这种方式下,所有的输入校验只需要通过指定简单的配置文件即可。
服务器端:
<form method="post" action="regist.action">
用户名:<input type="text" name="name"><br />
密 码:<input type="text" name="pass"><br />
年 龄:<input type="text" name="age"><br />
生 日:<input type="text" name="birth"><br />
<input type="submit" value="注册">
</form>
Action:RegistAction.java
public class RegistAction extends ActionSupport
{
//封装用户请求参数的四个属性
private String name;
private String pass;
private int age;
private Date birth;
//name属性的setter和getter方法
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return this.name;
}
//pass属性的setter和getter方法
public void setPass(String pass)
{
this.pass = pass;
}
public String getPass()
{
return this.pass;
}
//age属性的setter和getter方法
public void setAge(int age)
{
this.age = age;
}
public int getAge()
{
return this.age;
}
//birth属性的setter和getter方法
public void setBirth(Date birth)
{
this.birth = birth;
}
public Date getBirth()
{
return this.birth;
}
}
校验文件:RegistAction-validation.xml
<?xml version="1.0" encoding="GBK"?>
<!-- 指定校验配置文件的DTD信息 -->
<!DOCTYPE validators PUBLIC
"-//OpenSymphony Group//XWork Validator 1.0.3//EN"
"http://www.opensymphony.com/xwork/xwork-validator-1.0.3.dtd">
<!-- 校验文件的根元素 -->
<validators>
<!-- 校验Action的name属性 -->
<field name="name">
<!-- 指定name属性必须满足必填规则 -->
<field-validator type="requiredstring">
<param name="trim">true</param>
<message>必须输入名字</message>
</field-validator>
<!-- 指定name属性必须匹配正则表达式 -->
<field-validator type="regex">
<param name="expression"><![CDATA[(\w{4,25})]]></param>
<message>您输入的用户名只能是字母和数字
,且长度必须在4到25之间</message>
</field-validator>
</field>
<!-- 校验Action的pass属性 -->
<field name="pass">
<!-- 指定pass属性必须满足必填规则 -->
<field-validator type="requiredstring">
<param name="trim">true</param>
<message>必须输入密码</message>
</field-validator>
<!-- 指定pass属性必须满足匹配指定的正则表达式 -->
<field-validator type="regex">
<param name="expression"><![CDATA[(\w{4,25})]]></param>
<message>您输入的密码只能是字母和数字
,且长度必须在4到25之间</message>
</field-validator>
</field>
<!-- 指定age属性必须在指定范围内-->
<field name="age">
<field-validator type="int">
<param name="min">1</param>
<param name="max">150</param>
<message>年龄必须在1到150之间</message>
</field-validator>
</field>
<!-- 指定birth属性必须在指定范围内-->
<field name="birth">
<field-validator type="date">
<!-- 下面指定日期字符串时,必须使用本Locale的日期格式 -->
<param name="min">1900-01-01</param>
<param name="max">2050-02-21</param>
<message>生日必须在${min}到${max}之间</message>
</field-validator>
</field>
</validators>
Struts 2的校验文件规则和Struts 1的校验文件设计方式不同,Struts 2中每一个Action都有一个校验文件,因此该文件的文件名应该遵守如下规则:
<Action名字>-validation.xml
校验文件放在action的文件夹里。
在Struts.xml中有定义input视图,当有错误时,会将错误提示信息发给<s:fielderror/>显示
……
<message>${getText("name.regex")}</message>
……
<message>${getText("pass.requried")}</message>
……
客户端的输入校验:
增加客户端校验非常简单,将输入界面的表单元素改为使用Struts 2标签生成表单,并且为该表单增加 validate="true" 属性即可。
regist.jsp
<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<%@taglib prefix="s" uri="/struts-tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>请输入您的注册信息</title>
<meta name="website" content="http://www.crazyit.org" />
<s:head/>
</head>
<body>
<h3>请输入您的注册信息</h3>
<!-- 使用Struts 2标签库生成表单 -->
<s:form action="regist" validate="true">
<!-- 使用s:textfield标签生成文本输入框 -->
<s:textfield label="用户名" name="name"/>
<s:password label="密码" name="pass"/>
<s:textfield label="年龄" name="age"/>
<s:textfield label="生日" name="birth"/>
<s:submit/>
</s:form>
</body>
</html>
action同服务器端一样
像之前的错误提示信息都是写在配置文件中的,这样不利于国际化的提示信息。为了国际化提示信息,为message元素指定key属性,该属性指定是国际化提示信息对应的key。
服务器端的使用方法:
……
<message key=“name.requried”/>
……
<message key=“pass.requried”/>
……
客户端使用方法:
……
<message>${getText("name.requried")}</message>
……
<message>${getText("pass.requried")}</message>
……
因为上面的校验文件指定了很多国际化信息的key,所以必须在国际化资源文件中增加对应的key。
globalMessage.properties
xwork.default.invalid.fieldvalue={0}字段无效
#违反用户名必须输入的提示信息
name.requried=您必须输入用户名!
#违反用户名必须匹配正则表达式的提示信息
name.regex=您输入的用户名只能是字母和数字,且长度必须在4到25之间!
#违反密码必须输入的提示信息
pass.requried=您必须输入密码!
#违反密码必须匹配正则表达式的提示信息
pass.regex=您输入的密码只能是字母和数字,且长度必须在4到25之间!
#违反年龄必须在指定范围的提示信息
age.range=您的年龄必须在1和150之间!
#违反生日必须在指定范围的提示信息
birth.range=您的生日必须在1900-01-0和2050-02-21之间!