第24章 用户验证系统
本章视频教学录像:22分钟
注册会员是网站聚集人气的一个重要功能,很多服务和信息只有会员才能享用。本章介绍如何在网站中添加用户验证系统来保证网站的信息安全。
本章要点(已掌握的在方框中打钩)
□ 了解用户验证的原理
□ 创建数据库和数据表
□ 设计系统前台页面
□ 设计用户验证代码
□ 将用户验证系统添加至已有网站中
24.1 系统分析
本节视频教学录像:5分钟
为了区分每个用户的不同信息,许多网站都采用了身份验证。不同的用户登录网站之后可以使用自己的身份进行网络活动,就像个人的身份证一样。
本章使用ASP.NET和SQL Server 2008来创建一个用户身份验证系统。
24.1.1 系统目标
本系统需要实现的目标有以下3点。
⑴用户注册。
⑵用户登录。
⑶生成验证码。
24.1.2 系统原理
当用户浏览网站时,可要求用户成为本网站的会员之后才可以查看相关的网站资源。用户注册成为会员之后,凭用户名和密码登录网站。同时为了防止利用程序重复登录破解密码,还要生成随机的验证码,以增加程序识别的难度。
24.1.3 技术要点
提示
如何生成验证码?
先利用Random类生成随机数,然后将产生的随机数绘制成图片并以二进制的形式保存到输出流中即可。
为了完成以上功能,需要使用一般处理程序(HTTP 处理程序 .ashx)。.ashx 文件是用于写Webhandler的, 当然也可以用.aspx 文件。但是使用.ashx可以让你专注于编程,而不用管相关的Web技术。
右键单击项目,在弹出的快捷菜单中选择【添加新项】菜单项,弹出【添加新项】窗体,选择“一般处理程序”模板,在【名称】文本框中输入“ValidCode.ashx”,单击【添加】按钮。
首先需要生成验证码显示的随机数。打开ValidCode.ashx,添加GetValidCode方法,此方法从给定的序列中随机抽取若干个字符并返回给调用者(代码24-1.txt)。
01 private string GetValidCode(int num)
02 {
03 string strRandomCode = "ABCD1EF2GH3IJ4KL5MN6P7QR8ST9UVWXYZ"; //定义要随机抽取的字符串
04 char[] chastr= strRandomCode.ToCharArray(); //将定义的字符串转换成字符数组
05 StringBuilder sbValidCode= new StringBuilder(); //定义StringBuilder对象用于存放验证码
06 Random rd= new Random(); //随机函数,随机抽取字符
07 for (int i= 0; i< num; i++)
08 {
09 sbValidCode.Append(strRandomCode.Substring(rd.Next(0, strRandomCode.Length), 1)); //以 strRandomCode的长度产生随机位置,并截取该位置的字符添加到StringBuilder对象中
10 }
11 return sbValidCode.ToString();
12 }
有了随机数之后,还要将此随机数生成图片信息保存到输出流中。找到系统自动生成的ProcessRequest方法,输入以下代码。当使用一般处理程序的时候,此方法将被自动调用(代码24-2.txt)。
01 public void ProcessRequest(HttpContext context)
02 {
03 string strValidCode=GetValidCode(5); //产生5位随机字符
04 context.Session["ValidCode"]= strValidCode; //将字符串保存到Session中,以便需要时进行验证
05 Bitmap image= new Bitmap(120, 30); //定义宽120像素、高30像素的数据定义的图像对象
06 Graphics g=Graphics.FromImage(image); //绘制图片
07 try
08 {
09 Random random= new Random(); //生成随机对象
10 g.Clear(Color.White); //清除图片背景色
11 for (int i= 0; i< 25; i++) //随机产生图片的背景噪线
12 {
13 int x1= random.Next(image.Width);
14 int x2= random.Next(image.Width);
15 int y1= random.Next(image.Height);
16 int y2= random.Next(image.Height);
17 g.DrawLine(new Pen(Color.Silver), x1, y1, x2, y2);
18 }
19 Font font= newSystem.Drawing.Font("Arial", 20, (System.Drawing.FontStyle.Bold));
//设置图片字体风格
20 LinearGradientBrush brush = new LinearGradientBrush(new Rectangle(0, 0, image.Width,image.Height),Color.Blue,Color.DarkRed, 3, true); //设置画笔类型
21 g.DrawString(strValidCode, font,brush, 5, 2); //绘制随机字符
22 g.DrawRectangle(new Pen(Color.Silver), 0, 0, image.Width - 1, image.Height - 1); //绘制图片的前景噪点
23 System.IO.MemoryStream ms= new System.IO.MemoryStream(); //建立存储区为内存的流
24 image.Save(ms, ImageFormat.Gif); //将图像对象储存为内存流
25 context.Response.ClearContent(); //清除当前缓冲区流中的所有内容
26 context.Response.ContentType= "image/Gif"; //设置输出流的MIME类型
27 context.Response.BinaryWrite(ms.ToArray()); //将内存流写入到输出流
28 }
29 finally
30 {
31 g.Dispose();
32 image.Dispose();
33 }
34 }
24.2 数据库分析及设计
本节视频教学录像:1分钟
根据分析,本系统需要用到数据库来保存用户的注册信息数据,所以在系统开发之前,首先要进行数据库的设计。
24.2.1 数据库分析
在用户身份验证系统中,需要保存用户的名称、密码、性别、年龄等信息。更详细的信息用户资料,读者可以自己继续添加。
24.2.2 创建数据库
在SQL Server 2008中创建数据库的具体步骤如下。
⑴选择【开始】【所有程序】【Microsoft SQL Server 2008】【SQL Server Management Studio】,以【Windows身份验证】模式登录。
⑵在【对象资源管理器】窗口中的【数据库】节点上右击,在弹出的快捷菜单中选择【新建数据库】菜单项,弹出【新建数据库】对话框。
⑶在【数据库名称】文本框中输入“ch24DataBase”,在【数据库文件】列表框中使用默认设置“ch24DataBase”和“ch24DataBase_log”文件的路径,然后单击【确定】按钮,即可完成ch24DataBase数据库的创建。
24.2.3 创建数据表
本实例中需要一张记录上传文件相关信息的表,下面来创建数据表。
⑴在【对象资源管理器】中展开ch24DataBase节点,右击【表】节点,在弹出的快捷菜单中选择【新建表】菜单项。
⑵在打开的表编辑窗口中,按照下表进行输入。
⑶在【dbo.Table_1*】选项卡上右击,在弹出的快捷菜单中选择【保存】菜单项,弹出【选择名称】对话框,在【输入表名称】文本框中输入“tabUsersInfo”,然后单击【确定】按钮,即可完成表的创建。
24.3 实现步骤
本节视频教学录像:10分钟
在24.1.1小节中,已经提出了用户身份验证系统需要实现的3个目标,所以在系统设计时,需要设计3个模块来实现各个目标。
24.3.1 登录页面设计
首先设计系统的登录页面,页面中需要输入用户名、密码、显示的验证码。输入用户名后可以检查该用户名是否已注册来减少不必要的操作步骤。所有信息输入后单击【提交】按钮验证用户信息;如果用户未注册则单击【注册】按钮,转到注册页面进行新用户注册。
⑴在Visual Studio 2010中,新建【语言】为【Visual C#】的ASP.NET网站,删除原有的Default. aspx页面,重新添加一个Default.aspx页面。
⑵在Default.aspx页面的设计视图中,如图所示设计系统界面,所用控件及属性设置如表所示。
24.3.2 用户注册页面设计
如果用户第1次进入本网站则需要注册新的用户身份,系统需要添加一个用户注册页面。
⑴在网站中添加一个Web页面,命名为“UserRegist.aspx”。
⑵在UserRegist.aspx页面的设计视图中,如图所示设计系统界面,所用控件及属性设置如表所示。
提示
提验证控件的ValidationGroup属性可以将验证的内容分组,单击按钮的时候只有属于同一个验证组的控件才能被执行验证。Ajax控件可以在检查用户是否注册的时候局部刷新页面,保持用户输入的内容不至于丢失。
24.3.3 配置网站的Web.config
数据库和系统页面都设计好了,如何将它们连接起来呢?这就需要通过配置系统的Web.config文件来连接数据库。
在【解决方案资源管理器】中双击Web.config文件,打开Web.config的代码窗口,然后将<connectionStrings>和< /connectionStrings>之间的代码更换为以下代码。
<add name="ch24DataBase" connectionString="Data Source=.\sqlexpress;Initial Catalog=ch24DataBase; User ID=sa;Password=123"/>
【代码详解】
此段代码的作用是添加一个数据库的连接字符串,“.\sqlexpress”表示要连接当前本机的sqlexpress数据库服务器,读者也可设定为数据库所在服务器的IP地址。数据库的名称为“ch24DataBase”。Password为登录数据库服务器的密码。
24.3.4 数据库连接代码设计
在用户登录网站或注册新用户的时候需要连接数据库操作数据,在使用结束时要关闭此连接,所有相关数据操作使用一个公共数据类来实现。
新建一个类文件,命名为“DataClass.cs”,添加GetSqlServerConn方法,用于获得数据库连接(代码24-3.txt)。
01 private SqlConnection GetSqlServerConn()
02 {
03 SqlConnection sqlConn; //定义SQlServer连接对象
04 string strConn = WebConfigurationManager.ConnectionStrings["ch24DataBase "]ConnectionString; //读取Web.config配置文件的ConnectionString节点获取连接字符串
05 sqlConn= new SqlConnection(strConn); //生成数据库连接对象
06 sqlConn.Open(); //打开数据库连接
07 return sqlConn; //返回数据库连接对象以供调用
08 }
继续添加CloseSqlServerConn方法,用于关闭数据库连接(代码24-4.txt)。
01 private void CloseSqlServerConn(SqlConnection sqlConn)
02 {
03 if (sqlConn.State==ConnectionState.Open) //如果数据库连接处于关闭状态,则打开此连接
04 {
05 sqlConn.Close();
06 }
07 }
24.3.5 判断用户是否注册代码设计
由于用户名不能重复,所以新用户注册的时候要检查输入的用户名是否已经注册。在公共类中继续添加IsUserRegist方法(代码24-5.txt)。
01 public bool IsUserRegist(string strUserName)
02 {
03 string strComm= "select count(*) from tabUsersInfo where UserName=@UserName";
04 SqlConnection sqlConn= this.GetSqlServerConn(); //调用GetSqlServerConn()方法获得数据库连接
05 SqlCommand sqlComm= new SqlCommand(); //生成数据库命令操作对象
06 try
07 {
08 sqlComm.CommandText= strComm; //指定要执行的SQL命令
09 sqlComm.Connection= sqlConn; //指定要使用的SQL连接
10 sqlComm.Parameters.AddWithValue("@UserName", strUserName); //为SQL命令的参数赋值
11 object obj= sqlComm.ExecuteScalar(); //执行SQL命令返回第1行第1列的值
12 if (int.Parse(obj.ToString())> 0) //判断是否存在数据
13 {
14 return true;
15 }
16 else
17 {
18 return false;
19 }
20 }
21 catch (Exception ex)
22 {
23 return false;
24 }
25 finally
26 {
27 this.CloseSqlServerConn(sqlConn); //调用方法关闭数据库
28 }
29 }
24.3.6 保存用户注册信息代码设计
用户可以保存输入的注册信息,注册后的用户可以凭用户名和密码登录网站。由于密码信息在数据库中是以明文的方式保存的,可能会导致用户的密码被盗取,所以需要将密码加密后再保存到数据库中。这里使用MD5常用的加密方式,在公共类中添加GetMD5方法(代码24-6.txt)。
01 private string GetMD5(string strPwd)
02 {
03 MD5 md5= new MD5CryptoServiceProvider(); //加密服务提供类
04 byte[]bPwd=Encoding.Default.GetBytes(strPwd); //将输入的密码转换成字节数组
05 byte[]bMD5=md5.ComputeHash(bPwd); //计算指定字节数组的哈希值
06 md5.Clear(); //释放加密服务提供类的所有资源
07 StringBuilder sbMD5Pwd=new StringBuilder();
08 for(int i=0;i<bMD5.Length;i++) //将加密后的字节转换成字符串
09 {
10 sbMD5Pwd.Append(bMD5[i].ToString());
11 }
12 return sbMD5Pwd.ToString();
13 }
密码加密后就可以保存到数据库中,在公共类中添加SaveUserInfo方法来保存用户注册的信息(代码24-7.txt)。
01 public bool SaveUserInfo(string strUserName, string strPwd, int iSex, int iAge)
02 {
03 string strComm =@"insert into tabUsersInfo(UserName,Pwd,Sex,Age) values(@UserName,@Pwd,@Sex,@Age)";
04 SqlConnection sqlConn= this.GetSqlServerConn(); //调用GetSqlServerConn()方法获得数据库连接
05 SqlCommand sqlComm= new SqlCommand(); //生成数据库命令操作对象
06 try
07 {
08 sqlComm.CommandText= strComm; //指定要执行的SQL命令
09 sqlComm.Connection= sqlConn; //指定要使用的SQL连接
10 sqlComm.Parameters.AddWithValue("@UserName", strUserName); //为SQL命令的参数赋值
11 sqlComm.Parameters.AddWithValue("@Pwd",GetMD5(strPwd));
12 sqlComm.Parameters.AddWithValue("@Sex", iSex);
13 sqlComm.Parameters.AddWithValue("@Age", iAge);
14 sqlComm.ExecuteNonQuery(); //执行SQL命令
15 return true;
16 }
17 catch (Exception ex)
18 {
19 return false;
20 }
21 finally
22 {
23 this.CloseSqlServerConn(sqlConn); //调用方法关闭数据库
24 }
25 }
24.3.7 用户登录检查代码设计
用户登录网站时要输入注册的用户名和密码来验证是否为注册用户,只有输入的信息验证通过才能浏览网站信息。可在公共类中添加IsUserExist方法来检查用户输入的信息是否正确(代码24-8.txt)。
01 public bool IsUserExist(string strUserName, string strPwd)
02 {
03 string strComm = "select count(*) from tabUsersInfo where UserName=@UserName and Pwd=@Pwd";
04 SqlConnection sqlConn= this.GetSqlServerConn();
//调用GetSqlServerConn()方法获得数据库连接
05 SqlCommand sqlComm= new SqlCommand(); //生成数据库命令操作对象
06 try
07 {
08 sqlComm.CommandText= strComm; //指定要执行的SQL命令
09 sqlComm.Connection= sqlConn; //指定要使用的SQL连接
10 sqlComm.Parameters.AddWithValue("@UserName", strUserName); //为SQL命令的参数赋值
11 sqlComm.Parameters.AddWithValue("@Pwd",GetMD5(strPwd));
12 object obj= sqlComm.ExecuteScalar(); //执行SQL命令返回第1行第1列的值
13 if (int.Parse(obj.ToString())> 0) //判断是否存在数据
14 {
15 return true;
16 }
17 else
18 {
19 return false;
20 }
21 }
22 catch (Exception ex)
23 {
24 return false;
25 }
26 finally
27 {
28 this.CloseSqlServerConn(sqlConn); //调用方法关闭数据库
29 }
30 }
24.3.8 判断用户是否注册事件代码
由于用户名不能重复,所以新用户注册的时候要检查输入的用户名是否已经注册。这里我们借助JQuery实现异步检测,当光标从用户名文本框离开后,自动检测用户名是否已被注册。步骤如下:
⑴在UserRegist.aspx页面添加对JQuery的引用,拖曳Scripts文件夹下的jquery-1.4.1.js到页面源视图<head>区域,代码如下:
<script src="Scripts/jquery-1.4.1.js" type="text/javascript">< /script>
⑵在 UserRegist.aspx 源视图的 <head> 区域加上如下代码,用于异步调用 CheckUserName.aspx以检测用户名,并将检测的结果返回并显示。
01<script type="text/javascript">
02 function checkUserName() {
03 //请求的地址
04 //将用户名发送给服务器,查看该用户名是否被使用,返回一个字符串
05 var username= $("#txtUserName").val();
06 $.get("CheckUserName.aspx?username="+ username, null, callback);
07 }
08 function callback(data) {
09 $("#message").html(data);
10 }
11 < /script>
⑶为txtUserName文本框加上onblur="checkUserName();"事件。
⑷添加一个名为CheckUserName.aspx的页面,在Page_Load()事件输入以下代码。
01 protected void Page_Load(object sender,EventArgs e)
02 {
03 string txtUserName=Request["username"];
04 //调用 IsUserRegist判断用户是否已经注册
05 bool bFlag= new DataClass().IsUserRegist(txtUserName);
06 if (bFlag) //根据判断结果显示对应信息
07 {
08 Response.Write("该用户名已注册 ");
09 }
10 else
11 {
12 Response.Write("您可以使用该用户名注册 ");
13 }
14 }
24.3.9 保存用户信息事件代码
用户单击注册页面的【提交】按钮,可以触发该事件保存用户信息。在UserRegist.aspx页面双击“提交”按钮控件,生成btnSubmit_Click方法,输入以下代码(代码24-10.txt)。
01 protected void btnSubmit_Click(object sender,EventArgs e)
02 {
03 DataClass dc= new DataClass();
04 if (String.IsNullOrEmpty(txtAge.Text.Trim()))
05 {
06 txtAge.Text= "0";
07 }
08 bool bResult = dc.SaveUserInfo(txtUserName.Text.Trim(), txtPwd.Text.Trim(), int.Parse(rdlSex.SelectedItem.Value.Trim()), int.Parse(txtAge.Text)); //调用方法保存用户注册信息
09 if (bResult)
10 {
11 Response.Write("<script>alert('保存成功! ') ;window.location.href = 'Default.aspx'; < /script>");
12 }
13 else
14 {
15 Response.Write("<script>alert('保存失败! ')< /script>");
16 }
17 }
24.3.10 用户登录事件代码
用户进入登录页面,输入用户名、密码、验证码后单击【提交】按钮,如果是已注册的网站用户,则显示欢迎信息。在Default.aspx页面双击“提交”按钮控件,生成btnSubmit_Click方法,输入以下代码(代码24-11.txt)。
01 protected void btnSubmit_Click(object sender,EventArgs e)
02 {
03 if (txtValidCode.Text.ToUpper().Equals(Session["ValidCode"].ToString().ToUpper()))
04 {
05 bool bFlag= new DataClass().IsUserExist(txtUserName.Text, txtPwd.Text); // 调用DataClass的 IsUserExist方法判断输入的信息是否正确
06 if (bFlag)
07 {
08 Response.Write("<script>alert('欢迎 "+ txtUserName.Text+ "登录 ')< /script>");
09 }
10 else
11 {
12 Response.Write("<script>alert('用户名或密码错误,请重新输入! ')< /script>");
13 }
14 }
15 else
16 {
17 Response.Write("<script>alert('验证码输入错误,请重新输入! ')< /script>");
18 }
19 }
24.3.11 用户注册事件代码
用户进入登录页面,如果是第1次进入网站,则需要注册。单击【注册】按钮,转到相应的注册页面。在Default.aspx页面双击“注册”按钮控件,生成btnRegist_Click方法,输入以下代码(代码24-12.txt)。
01 protected void btnRegist_Click(object sender,EventArgs e)
02 {
03 Response.Redirect("UserRegist.aspx");
04 }
24.4 运行系统
本节视频教学录像:4分钟
系统设计好了,下面来看系统运行的效果。
⑴按【F5】或【Ctrl+F5】快捷键,在浏览器中运行该程序,页面中将显示用户登录的界面。
⑵单击【注册】按钮,将跳转到用户注册页面。
⑶输入注册用户名后,光标离开,系统自动检测该用户名是否能够注册。
⑷用户输入的信息不正确,系统会提示错误信息。
⑸用户输入正确的信息后单击【提交】按钮,保存该注册信息并显示保存结果。
⑹注册用户可以凭用户名和密码登录网站。输入用户名和密码,单击【提交】按钮,系统会提示登录成功信息或失败信息。
24.5 在我的网站中应用本系统
本节视频教学录像:1分钟
读者可将Default.aspx页面用作网站的首页作为网站的身份验证,其余相关页面只需复制到网站中对应的位置即可使用。具体页面的细节可自行调整,这里只是给出一个主要的功能模块供读者参考。
24.6 开发过程中的常见问题及解决方式
本节视频教学录像:1分钟
开发过程中需要注意以下几个问题。
⑴用户登录成功后,此处给出的是登录成功的提示信息,读者可修改为跳转到指定的页面。
⑵当使用验证控件的时候,如果没有设置ValidationGroup属性为同一个值,在单击所有的按钮控件时都会触发验证事件,所以要把验证的控件都设置为同一个验证组才能避免出现这种情况。
⑶注册用户时,用户名的检测借助JQuery采用异步传输方式,实现自动检测用户名是否可用。