基于身份验证票据的权限系统的实现之源代码篇

前天的承诺,放出一个源代码。是现写的,时间比较仓促,取其精华,弃其糟粕吧。
还是一样,希望大家提出宝贵意见。

 

上一篇文章中提到了基于用户凭据的权限控制方法,今天向大家介绍如何基于这种方法开法应用程序。在这些源代码中,我们基于每个字节代表一种权限位的算法。

1、  建立数据库。

CREATE TABLE [dbo].[用户表](

    [ID] [int] IDENTITY(1,1) NOT NULL,

    [用户名] [varchar](50) COLLATE Chinese_PRC_CI_AS NULL,

    [用户密码] [char](32) COLLATE Chinese_PRC_CI_AS NULL,

    [用户权限] [binary](50) NULL

) ON [PRIMARY]

 

CREATE TABLE [dbo].[权限对照表](

    [ID] [int] IDENTITY(1,1) NOT NULL,

    [权限名称] [varchar](50) COLLATE Chinese_PRC_CI_AS NULL

) ON [PRIMARY]

 

为了方便起见。今天只向大家介绍用户表和权限对照表。角色表暂不涉及

 

2、  创建DataAccess类。大家可能习惯于使用SqlHelper。我比较喜欢自己写一个小类。萝卜青菜,各有所爱吧。

using System;

using System.Data;

using System.Data.SqlClient;

 

namespace localhost

{

     /// <summary>

     /// 数据库处理组件

     /// </summary>

     public class DataAccess

     {

         public SqlConnection Connection

         {

              get{return _conn;}

              set{_conn = value;}

         }

         private SqlConnection _conn;

         public DataAccess()

         {

              string connectString = System.Configuration.ConfigurationSettings.AppSettings["connectstring"];

              this._conn = new SqlConnection(connectString);

         }

         public DataAccess(string connectString)

         {

              this._conn = new SqlConnection(connectString);

         }

 

         public void OpenConnection()

         {

              if(this._conn != null && this._conn.State == ConnectionState.Closed)

                   this._conn.Open();

              else if(this._conn == null)

                   throw new Exception("Connection 还没有初始化");

         }

         public void CloseConnection()

         {

              if(this._conn != null && this._conn.State == ConnectionState.Open)

                   this._conn.Close();

              else if(this._conn == null)

                   throw new Exception("Connection 还没有初始化");

         }

 

         public SqlDataReader ExecuteReader(string sql,params SqlParameter[] parameters)

         {

              SqlCommand command = new SqlCommand(sql);

              return ExecuteReader(command,parameters);

         }

         public SqlDataReader ExecuteReader(SqlCommand command,params SqlParameter[] parameters)

         {

              foreach(SqlParameter parameter in parameters)

              {

                   command.Parameters.Add(parameter);

              }

              return ExecuteReader(command);

         }

         public SqlDataReader ExecuteReader(SqlCommand command)

         {

              command.Connection = this._conn;

              this.OpenConnection();

 

              return command.ExecuteReader();

         }

         public int ExecuteNonQuery(string sql,params SqlParameter[] parameters)

         {

              SqlCommand command = new SqlCommand(sql);

              return ExecuteNonQuery(command,parameters);

         }

         public int ExecuteNonQuery(SqlCommand command,params SqlParameter[] parameters)

         {

              for(int i = 0; i < parameters.Length ; i++)

                   command.Parameters.Add(parameters[i]);

              return ExecuteNonQuery(command);

         }

         public int ExecuteNonQuery(SqlCommand command)

         {

              command.Connection = this._conn;

              this.OpenConnection();

              try

              {

                   return command.ExecuteNonQuery();

              }

              finally

              {

                   this.CloseConnection();

              }

         }

         public int FillDataSet(DataSet dataset,SqlCommand command,string tablename)

         {

              command.Connection = this._conn;

              try

              {

                   this.OpenConnection();

                   SqlDataAdapter adapter = new SqlDataAdapter(command);

                   return adapter.Fill(dataset,tablename);

              }

              finally

              {

                   this.CloseConnection();

              }

         }

         public int FillDataSet(DataSet dataset,string sql,string tablename,params SqlParameter[] parameters)

         {

              SqlCommand command = new SqlCommand(sql);

              return this.FillDataSet(dataset,command,tablename,parameters);

         }

         public int FillDataSet(DataSet dataset,SqlCommand command,string tablename,params SqlParameter[] parameters)

         {

              foreach(SqlParameter parameter in parameters)

                   command.Parameters.Add(parameter);

              return this.FillDataSet(dataset,command,tablename);

         }

     }

}

 

3、   创建登录类LoginClass。这个类也可以直接写到登录页面中。为了方便,我们把权限的长度写为50位长。并且每个字节表示一种权限。做为一个常量保存在类中,名字是Length。该类提供了一个静态方法Login,输入用户名和密码,返回一个Ticket。如果失败则抛出异常。我们并没有处理此异常。在实际的应用中大家可以根据自己的需要处理此异常。

using System;

using System.Data;

using System.Data.SqlClient;

using System.Web.Security;

 

namespace localhost

{

     /// <summary>

     ///登录组件

     /// </summary>

     public class LoginClass

     {

         //定义权限列的长度

         public const int Length = 50;

         public LoginClass()

         {

             

         }

 

         public static FormsAuthenticationTicket Login(string username,string password)

          {

              string sql = "SELECT TOP 1 * FROM 用户表 WHERE 用户名 = @username";//取出用户输入用户名的用户信息

 

              SqlParameter userParameter = new SqlParameter("@username",username); //将用户名作为参数传入,避免SQL注入的发生

 

              DataAccess da = new DataAccess();//实例化数据访问组件

 

              try

              {

                   SqlDataReader reader = da.ExecuteReader(sql,userParameter); //执行SQL语句,返回一个SqlDataReader

                   if(reader.Read())//如果读取到了数据

                   {

                       if(reader["用户密码"] == null || (string)reader["用户密码"] != HashPassWord(password)) //如果用户密码为null或与数据库中保存的不一致

                            throw new Exception("用户密码不正确");//密码错误,抛出异常

 

                       byte[] buffer = new byte[Length]; //定义一个与权限字节长度相等的字节数组

                       int index = reader.GetOrdinal("用户权限");//取出用户权限列对应的序号

                       if(!reader.IsDBNull(index)) //如果用户权限这一列不为null。则取出来保存到字节数组中

                            reader.GetBytes(index,0,buffer,0,buffer.Length);

 

                       string userdata = string.Empty;

                       foreach(byte b in buffer)

                            userdata += Convert.ToString(b,2).PadLeft(1,'0'); //转成01组成的字符串

 

                       FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1,username,DateTime.Now,DateTime.MaxValue,false,userdata); //把这些信息保存到一个Ticket

                       return ticket;

                   }

                   else

                       throw new Exception("用户不存在");//抛出用户名不存在的异常

              }

              finally

              {

                   da.CloseConnection();//断开数据库连接,实际的应用中。可以在读取完字节数组后就断开,我们使用数据库的原则是晚连早断。

              }

 

         }

         public static string HashPassWord(string password) //计算密码的散列值

         {

              return FormsAuthentication.HashPasswordForStoringInConfigFile(password,"md5");

         }

     }

}

 

4、   创建一个继承自Page的类PageBase。需要验证的类都要继承自这个类,这样当页面初始化时。会首先执行此OnInit事件。即首先通过验证才能进行此页面的访问,在此操作中。我们对没有通过验证的用户,也直接抛出异常,实际情况可以自定义自己的操作。

using System;

using System.Web.Security;

 

namespace localhost

{

     /// <summary>

     /// 页面基类

     /// </summary>

     public class PageBase:System.Web.UI.Page

     {

         protected int[] _needPermissions; //需要的权限列表。数组的每一个值表示权限表中的ID,大家可以重写它。直接写入权限名称。然后取出ID放在此数组中

         protected override void OnInit(EventArgs e)

         {

             

              //验证权限

              if(this._needPermissions != null && this._needPermissions.Length > 0) //当权限数组不是null并且长度大于0时,表示需要验证

              {

                   string userdata = string.Empty;

                   if(User.Identity.IsAuthenticated)

                   {

                       FormsIdentity identity = User.Identity as FormsIdentity;

                       userdata = identity.Ticket.UserData;

                   }

                   if(userdata == string.Empty) //没有登录,抛出异常

                       throw new Exception("非法请求");

                   foreach(int index in this._needPermissions)

                   {

                       if(userdata.Length - 1 < index || userdata.Substring(index,1) == "0")

                            throw new Exception("非法请求");//没有权限,抛出异常

                   }

              }

              base.OnInit (e);

         }

 

     }

}

5、   创建一个登录页面Login.aspx。此页面主要有两个TextBox,分别用于输入用户名和密码。一个按钮,用户点击此按钮时开始登录

using System;

using System.Collections;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Web;

using System.Web.SessionState;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.Web.UI.HtmlControls;

using System.Web.Security;

 

namespace localhost

{

     /// <summary>

     /// 登录页面

     /// </summary>

     public class Login : System.Web.UI.Page

     {

         protected System.Web.UI.WebControls.Label Label1;

         protected System.Web.UI.WebControls.TextBox TextBox1;

         protected System.Web.UI.WebControls.Label Label2;

         protected System.Web.UI.WebControls.TextBox TextBox2;

         protected System.Web.UI.WebControls.Button Button1;

    

         private void Page_Load(object sender, System.EventArgs e)

         {

              // 在此处放置用户代码以初始化页面

         }

 

         #region Web 窗体设计器生成的代码

         override protected void OnInit(EventArgs e)

         {

              //

              // CODEGEN: 该调用是 ASP.NET Web 窗体设计器所必需的。

              //

              InitializeComponent();

              base.OnInit(e);

         }

        

         /// <summary>

         /// 设计器支持所需的方法 - 不要使用代码编辑器修改

         /// 此方法的内容。

         /// </summary>

         private void InitializeComponent()

         {   

              this.Button1.Click += new System.EventHandler(this.Button1_Click);

              this.Load += new System.EventHandler(this.Page_Load);

 

         }

         #endregion

 

         private void Button1_Click(object sender, System.EventArgs e)

         {

              string username = TextBox1.Text; //用户名

              string password = TextBox2.Text; //密码

 

              try

              {

                   FormsAuthenticationTicket ticket = LoginClass.Login(username,password); //登录。如果成功的话会抛出异常。否则返回登录后的ticket

                   string data = FormsAuthentication.Encrypt(ticket); //ticket登录

 

                   HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName,data); //将加密后的ticket保存在Cookie

                   Response.Cookies.Add(cookie);

                   string strRedirect = Request["ReturnUrl"];//取出登录后返回的页面

                   if (strRedirect == null)

                       strRedirect = "Default.aspx";

                   Response.Redirect(strRedirect, true); //返回

              }

              catch(Exception ex) //捕获异常。弹出对话话提示用户登录失败

              {

                   Page.RegisterClientScriptBlock("alertloginerror","<script>alert('您输入的用户名或密码不正确');</script>");

              }

          }

     }

}

 

6、  创建一个授权页面WebForm1.aspx。这个页面用于给用户授权。有两个DropDownListBox。分别显示所有用户和所有权限。一个RadioButtonList。用于确定是授权还是取消此权限。为了使用方面,建议使用DataGrid来操作。

using System;

using System.Collections;

using System.ComponentModel;

using System.Data;

using System.Data.SqlClient;

using System.Drawing;

using System.Web;

using System.Web.SessionState;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.Web.UI.HtmlControls;

 

namespace localhost

{

     /// <summary>

     /// WebForm2 的摘要说明。

     /// </summary>

     public class WebForm2 : PageBase

     {

         protected System.Web.UI.WebControls.DropDownList DropDownList2;

         protected System.Web.UI.WebControls.RadioButtonList RadioButtonList1;

         protected System.Web.UI.WebControls.Button Button1;

         protected System.Web.UI.WebControls.DropDownList DropDownList1;

    

         private void Page_Load(object sender, System.EventArgs e)

         {

              if(!IsPostBack)

              {

                   this.Bind_Users();

                   this.Bind_Permissions();

              }

         }

         private void Bind_Users()//取出所有用户并绑定到DropDownList1

         {

              string sql = "SELECT 用户名 FROM 用户表";

              DataSet dataset = new DataSet();

              dataset.Tables.Add("users");

 

              DataAccess da = new DataAccess();

              da.FillDataSet(dataset,sql,"users");

 

              DropDownList1.DataSource = dataset.Tables[0].DefaultView;

              DropDownList1.DataTextField = "用户名";

              DropDownList1.DataValueField = "用户名";

 

              DropDownList1.DataBind();

 

         }

         private void Bind_Permissions()//绑定所有权限

         {

              string sql = "SELECT ID,权限名称 FROM 权限对照表";

              DataSet dataset = new DataSet();

              dataset.Tables.Add("permissions");

 

              DataAccess da = new DataAccess();

              da.FillDataSet(dataset,sql,"permissions");

 

              DropDownList2.DataSource = dataset.Tables[0].DefaultView;

              DropDownList2.DataTextField = "权限名称";

              DropDownList2.DataValueField = "ID";

 

              DropDownList2.DataBind();

 

         }

 

         #region Web 窗体设计器生成的代码

         override protected void OnInit(EventArgs e)

         {

              //

              // CODEGEN: 该调用是 ASP.NET Web 窗体设计器所必需的。

              //

              InitializeComponent();

              base.OnInit(e);

         }

        

         /// <summary>

         /// 设计器支持所需的方法 - 不要使用代码编辑器修改

         /// 此方法的内容。

         /// </summary>

         private void InitializeComponent()

         {   

              this.DropDownList1.SelectedIndexChanged += new System.EventHandler(this.DropDownList1_SelectedIndexChanged);

              this.Button1.Click += new System.EventHandler(this.Button1_Click);

              this.Load += new System.EventHandler(this.Page_Load);

 

         }

 

         #endregion

 

 

         private void DropDownList1_SelectedIndexChanged(object sender, System.EventArgs e)

         {

        

         }

 

         private void Button1_Click(object sender, System.EventArgs e) //给用户授权还是取消权限

         {

              string username = DropDownList1.SelectedValue; //用户名

              DataAccess da = new DataAccess();//实例化数据操作类

              SqlParameter user = new SqlParameter("@username",DropDownList1.SelectedValue); //用户名参数

              SqlDataReader reader = da.ExecuteReader("SELECT TOP 1 用户权限 FROM 用户表 WHERE 用户名 = @username",user); //取回一个DataReader。包含用户权限

 

              byte[] buffer = new byte[LoginClass.Length]; //定义一个字节数组保用用户现在的权限

              if(reader.Read() && !reader.IsDBNull(0))

                   reader.GetBytes(0,0,buffer,0,buffer.Length); //将用户权限取出来保存到字节数组中

              da.CloseConnection();//关闭数据连接

 

              int index = int.Parse(DropDownList2.SelectedValue); //要处理的权限ID

 

            buffer[index] = RadioButtonList1.Items[0].Selected ? (byte)1:(byte)0; //根据选择的授权还是取消授权。将字节数组中对应的位设置为10

 

              string sql = "UPDATE 用户表 SET 用户权限 = @permission WHERE 用户名=@user";//更新用户权限列

 

              SqlParameter parameter = new SqlParameter("@permission",SqlDbType.Binary,buffer.Length);

              parameter.Value = buffer; //将字节数组作为参数传入SQL语句执行

 

              da.ExecuteNonQuery(sql,parameter,user);

 

 

         }

     }

}

 

7、  创建一个测试页面WebForm1.aspx。这个页面将_needPermissions数组赋为{1},即只需要权限表中ID1的参数。由于这个页面的权限验证在其基类PageBase 中实现,所以不需要任何验证即拥有了权限验证功能。

using System;

using System.Collections;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Web;

using System.Web.SessionState;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.Web.UI.HtmlControls;

 

namespace localhost

{

     /// <summary>

     /// WebForm1 的摘要说明。

     /// </summary>

     public class WebForm1 : PageBase

     {

         public WebForm1()

         {

              base._needPermissions = new int[] {1};

         }

         private void Page_Load(object sender, System.EventArgs e)

         {

             

         }

 

         #region Web 窗体设计器生成的代码

         override protected void OnInit(EventArgs e)

         {

              //

              // CODEGEN: 该调用是 ASP.NET Web 窗体设计器所必需的。

              //

              InitializeComponent();

              base.OnInit(e);

         }

        

         /// <summary>

         /// 设计器支持所需的方法 - 不要使用代码编辑器修改

         /// 此方法的内容。

         /// </summary>

         private void InitializeComponent()

         {   

              this.Load += new System.EventHandler(this.Page_Load);

         }

         #endregion

     }

}

8、  修改Web.config 文件

<authentication mode="Forms">

    <forms name=".ASPXFORMSDEMO" loginUrl="Logn.aspx"  protection="All" path="/" timeout="30" />Õ

 

</authentication>

使用Forms认证。

 

posted on 2005-09-02 23:13  rainlake  阅读(4523)  评论(6编辑  收藏  举报

导航