TinyMind
.net.cn
首页
资讯
文章
注册
登录
[转载]基于身份验证票据的权限系统的实现之源代码篇
白鸟云平
关注文章
TinyMind专栏频道
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;