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


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'); //转成0与1组成的字符串
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; //根据选择的授权还是取消授权。将字节数组中对应的位设置为1或0
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},即只需要权限表中ID为1的参数。由于这个页面的权限验证在其基类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 文件
<authenticationmode="Forms">
<formsname=".ASPXFORMSDEMO"loginUrl="Logn.aspx"protection="All"path="/"timeout="30"/>Õ
</authentication>

长按二维码向我转账

受苹果公司新规定影响,微信 iOS 版的赞赏功能被关闭,可通过二维码转账支持公众号。

    阅读
    好看
    已推荐到看一看
    你的朋友可以在“发现”-“看一看”看到你认为好看的文章。
    已取消,“好看”想法已同步删除
    已推荐到看一看 和朋友分享想法
    最多200字,当前共 发送

    已发送

    朋友将在看一看看到

    确定
    分享你的想法...
    取消

    分享想法到看一看

    确定
    最多200字,当前共

    发送中

    网络异常,请稍后重试

    微信扫一扫
    关注该公众号





    联系我们

    欢迎来到TinyMind。

    关于TinyMind的内容或商务合作、网站建议,举报不良信息等均可联系我们。

    TinyMind客服邮箱:support@tinymind.net.cn