String *I am String* (自由大过天) 路漫漫其修远兮,吾以上下而求索

(道,可道,非常道)-----天之道,利而不害。圣人之道,为而不争。信言不美,美言不信。善者不辩,辩者不善。知者不博,博者不知。

导航

浅谈基于角色的安全

Posted on 2004-11-25 07:02  goodbaby  阅读(538)  评论(0)    收藏  举报
 

     基于角色的安全在web应用有广泛的应用,在.net中对角色安全的支持分为三中

      1:基于Windows的身份验证

      2:基于表单的身份验证

      3:基于Passport的身份验证

 简单说一下第一种和第三种,基于Windows的身份验证是把安全交给Windows的安全

子系统,依靠域服务器和IIS,当一个请求到达时,IIS首先截获,请求域服务器,进行身份验证,生成访问钥匙,然后严格按照ACL(Access Control List),执行相应的权限。基于Windows的角色安全是用在局域网的(Intranet)。基于Web(Internet)应用拥有大量用户的程序来说,维护一个庞大的ACL是不现实的,所以要使用基于表单的身份验证和基于Passport的身份验证,Passport的身份验证是MS提供的一个用来赚钱的服务,你要去订阅,他提供了很高的安全性和可跨站点的访问。基于表单的身份验证是最多的吧,我简单研究了一下,也对他进行了简单的实现。

 

      有的解决方案是Context.User=new GenericPrinciple(“”,角色数组);但是这不能够解决权限问题(我想了很久),所以需要实现一个IPrinciple接口扩充负责者的功能。好了,先看一下数据库的设计:

一个RoleAuthentication_Account表,包括UserID,UserFriendName,UserName,Password字段用来存储用户信息,不用多说。

一个RoleAuthentication_Role表,包括RoleID,Description

一个RoleAuthentication_Permission,包括PermissionId,Description,

一个RoleAuthentication_AccountRole, RoleAuthentication_RolePermission,很简单完成多对多的应射。

好了接下来数据服务的程序:

 

RoleAuthenticationDAL.cs

using System;

using System.Data;

using System.Data.SqlClient;

using System.Collections;

using System.Text;

using System.Security;

using System.Security.Cryptography;

 

namespace RoleAuthenticationDataAccessLayer

{

     /// <summary>

     /// Class1 的摘要说明。

     /// </summary>

     public class Account

     {

         public Account()

         {

              //

              // TODO: 在此处添加构造函数逻辑

              //

         }

 

         public DataRow getUserInfo(int UserID)

         {

              SqlParameter[] parameters = { new SqlParameter("@UserID", SqlDbType.Int) };

              parameters[0].Value = UserID;

            DataSet ds=DataAccessHelper.EexcuteDataSet(DataAccessHelper.ConStr,CommandType.StoredProcedure,"getUserInfo",parameters);

              return ds.Tables[0].Rows[0];

         }

 

         public ArrayList getUserRole(int UserID)

         {

              ArrayList role=new ArrayList();

              SqlParameter[] parameters = { new SqlParameter("@UserID", SqlDbType.Int) };

              parameters[0].Value = UserID;

              SqlDataReader dr = DataAccessHelper.ExecuteReader(DataAccessHelper.ConStr,CommandType.StoredProcedure,"getUserRole",parameters);

              while(dr.Read())

              {

                   role.Add(dr.GetInt32(0));

              }

              dr.Close();

              return role;

         }

 

         public ArrayList getUserPermission(int UserID)

         {

              ArrayList role=new ArrayList();

              SqlParameter[] parameters = { new SqlParameter("@UserID", SqlDbType.Int) };

              parameters[0].Value = UserID;

              SqlDataReader dr = DataAccessHelper.ExecuteReader(DataAccessHelper.ConStr,CommandType.StoredProcedure,"getUserPermission",parameters);

              while(dr.Read())

              {

                   role.Add(dr.GetInt32(0));

              }

              dr.Close();

              return role;

         }

 

         public int Login(string UserName,string Password)

         {

              for(int i=0;i<15-UserName.Length;i++)

              {

                   UserName+=" ";

              }

              byte[] pass=EncryptPassword(Password);

              SqlParameter[] parameters = { new SqlParameter("@UserID", SqlDbType.Int),new SqlParameter("@UserName", SqlDbType.Char),new SqlParameter("@UserPassword", SqlDbType.VarBinary) };

              parameters[0].Direction=ParameterDirection.Output;

              parameters[1].Value = UserName;

            parameters[2].Value=pass;

              int result=DataAccessHelper.RExecutNonQuery(DataAccessHelper.ConStr,CommandType.StoredProcedure,"Login",parameters);

              if(result>0)

              {

                   return result;

              }

              else

              {

                   return -1;

              }

         }

 

         public static byte[] EncryptPassword(string password)

         {

              UnicodeEncoding encoding = new UnicodeEncoding();

              byte[] hashBytes = encoding.GetBytes( password );

 

              // compute SHA-1 hash.

              SHA1 sha1 = new SHA1CryptoServiceProvider();

              byte[] cryptPassword = sha1.ComputeHash ( hashBytes );

 

              return cryptPassword;

         }

 

         public bool AddUser(string UserName,string UserFriendName,string Password)

         {

              for(int i=0;i<15-UserName.Length;i++)

              {

                   UserName+=" ";

              }

              byte[] pass=EncryptPassword(Password);

              SqlParameter[] parameters = { new SqlParameter("@UserName", SqlDbType.Char),new SqlParameter("@UserPassword", SqlDbType.VarBinary,20),new SqlParameter("@UserFriendName", SqlDbType.VarChar,50) };

              parameters[0].Value = UserName;

              parameters[1].Value=pass;

              parameters[2].Value=UserFriendName;

              if(DataAccessHelper.ExecutNonQuery(DataAccessHelper.ConStr,CommandType.StoredProcedure,"AddUser",parameters)>0)

              {

                   return true;

              }

              else

              {

                   return false;

              }

         }

 

         public bool ContributeRoleForUser(int UserID,int RoleID)

         {

              SqlParameter[] parameters = { new SqlParameter("@UserID", SqlDbType.Int),new SqlParameter("@RoleID", SqlDbType.Int) };

              parameters[0].Value = UserID;

              parameters[1].Value=RoleID;

              if(DataAccessHelper.ExecutNonQuery(DataAccessHelper.ConStr,CommandType.StoredProcedure,"ContributeRoleForUser",parameters)>0)

              {

                   return true;

              }

              else

              {

                   return false;

              }

         }

     }

}

 

DataAccessHelper.cs这个简单的类是自己实现的在不知道有MSDataAccess之前

using System;

using System.Data;

using System.Data.SqlClient;

using System.Configuration;

 

namespace RoleAuthenticationDataAccessLayer

{

     /// <summary>

     /// DataAccessHelper 的摘要说明。

     /// </summary>

     public abstract class DataAccessHelper

     {

         public static readonly string ConStr="server=GOODBABY;database=PRRolesAuthentication;uid=sa;password=goodbaby";//ConfigurationSettings.AppSettings["ConnectionString"];

        

        

         public static int ExecutNonQuery(string connString, CommandType cmdType, string cmdText, params SqlParameter[] cmdParms)

         {

              SqlCommand cmd=new SqlCommand();

              SqlConnection con=new SqlConnection(connString);

              PrepareCmd(cmd,con,cmdType,cmdText,cmdParms);

              try

              {

                   int result=cmd.ExecuteNonQuery();

                   cmd.Parameters.Clear();

                   return result;

              }

              catch(Exception ex)

              {

                   throw ex;

              }

              finally

              {

                   con.Close();

              }

         }

 

         public static int RExecutNonQuery(string connString, CommandType cmdType, string cmdText, params SqlParameter[] cmdParms)

         {

              SqlCommand cmd=new SqlCommand();

              SqlConnection con=new SqlConnection(connString);

              PrepareCmd(cmd,con,cmdType,cmdText,cmdParms);

              try

              {

                   int result=-1;

                   cmd.ExecuteNonQuery();

                   if(cmd.Parameters[0].Value is DBNull)

                   {

                       return result;

                   }

                result=Convert.ToInt32(cmd.Parameters[0].Value);

                   cmd.Parameters.Clear();

                   return result;

              }

              catch(Exception ex)

              {

                   throw ex;

              }

              finally

              {

                   con.Close();

              }

         }

         public static SqlDataReader ExecuteReader(string connString, CommandType cmdType, string cmdText, params SqlParameter[] cmdParms)

         {

              SqlCommand cmd=new SqlCommand();

              SqlConnection con=new SqlConnection(connString);

              PrepareCmd(cmd,con,cmdType,cmdText,cmdParms);

              try

              {

                   return cmd.ExecuteReader(CommandBehavior.CloseConnection);

              }

              catch(SqlException ex1)

              {

                   throw ex1;

              }

         }

 

 

         public static DataSet EexcuteDataSet(string connString, CommandType cmdType, string cmdText, params SqlParameter[] cmdParms)

         {

              SqlCommand cmd=new SqlCommand();

              SqlConnection con=new SqlConnection(connString);

              PrepareCmd(cmd,con,cmdType,cmdText,cmdParms);

              SqlDataAdapter dap=new SqlDataAdapter(cmd);

              try

              {

                   DataSet myDS=new DataSet();

                   dap.Fill(myDS);

                   return myDS;

              }

              catch(SqlException ex2)

              {

                   throw ex2;

              }

              finally

              {

                   con.Close();

              }

         }

 

 

 

         private static void PrepareCmd(SqlCommand cmd, SqlConnection conn, CommandType cmdType, string cmdText, SqlParameter[] cmdParms)

         {

              if (conn.State != ConnectionState.Open)

                   conn.Open();

            cmd.Connection = conn;

              cmd.CommandText = cmdText;

              cmd.CommandType=cmdType;

              if (cmdParms != null)

              {

                   foreach(SqlParameter SqlPar in cmdParms)

                   {

                       cmd.Parameters.Add(SqlPar);

                   }

              }

         }

     }

}

 

BL中实现了IPrinciple,IIdentity

using System;

using System.Security.Principal;

using System.Collections;

using RoleAuthenticationDataAccessLayer;

 

 

namespace RolsesAuthenticationBL

{

     /// <summary>

     /// Class1 的摘要说明。

     /// </summary>

     public class MyPrinciple:IPrincipal

     {

         protected IIdentity  identity;

         protected ArrayList permissionList;

         protected ArrayList roleList;

        

         public MyPrinciple()

         {

              //

              // TODO: 在此处添加构造函数逻辑

              //

         }

        //use DataAccessLayer'user class to set Attributes and use UserID to constructe a identity object

         public MyPrinciple(int UserID)

         {

            Account account=new Account();

              roleList=account.getUserRole(UserID);

             permissionList=account.getUserPermission(UserID);

              identity=new MyIdentity(UserID);

         }

 

         public IIdentity Identity

         {

              get

              {

                   return identity;

              }

              set

              {

                   identity = value;

              }

         }

 

         public bool IsInRole(string role)

         {

              return roleList.Contains( role );

         }

 

         public bool HasPermission( int permissionID )

         {

              return permissionList.Contains( permissionID );

         }

 

         public ArrayList Permissions

         {

              get

              {

                   return permissionList;

              }

         }

 

         public ArrayList Roles

         {

              get

              {

                   return roleList;

              }

         }

 

     }

}

 

MyIdentity.cs

using System;

using System.Security.Principal;

using RoleAuthenticationDataAccessLayer;

using System.Data;

namespace RolsesAuthenticationBL

{

     /// <summary>

     /// MyIdentity 的摘要说明。

     /// </summary>

     ///

    

     public class MyIdentity:IIdentity

     {

         private string _Name;

         private int    _UserID;

         private string _FriendName;

        private byte[] _Password;

         public MyIdentity()

         {

              //

              // TODO: 在此处添加构造函数逻辑

              //

         }

        //use userid for set attribute use DataAccessLayer'user class

         public MyIdentity(int UserID)

         {

              _UserID=UserID;

            Account account=new Account();

              DataRow datarow=account.getUserInfo(UserID);

              _Name=(string)datarow["UserName"];

            _FriendName=(string)datarow["UserFriendName"];

              _Password=(byte[])datarow["UserPassword"];

 

         }

         public bool IsAuthenticated

         {

              get

              {

                   return true;

              }

         }

 

         public string AuthenticationType

         {

              get

              {

                   return "MyCustom";

              }

         }

 

         public string Name

         {

              get

              {

                   return _Name;

              }

         }

 

         public int UserID

         {

              get

              {

                   return _UserID;

              }

         }

 

         public string FriendName

         {

              get

              {

                   return _FriendName;

              }

         }

 

         public byte[] Password

         {

              get

              {

                   return _Password;

              }

         }

     }

}

 

User封装验证,由于关注的是实现角色验证这个类没什么功能。

最后在SignIn.aspx中登陆后,在testpage.aspx中的Page_Load()中重新为User指定Principle

if(Context.User.Identity.IsAuthenticated)

              {

                   status.Text="I am Authenticationed";

                   if(!(Context.User is MyPrinciple))

                   {

                       status.Text=status.Text+"  I am not MyPrincipleType!";

                       Context.User=new MyPrinciple(int.Parse(User.Identity.Name));

                   }

              }

              else

              {

                   Response.Redirect("SignIn.aspx");

              }

也可以在Globle.asax中为设置User,不要在Application_BeginRequest()中,试了一下是不行的,原因很简单,请你想一下,在验证和授权中吧。好了用一个Button来测试一下,对一个需要权限ID2的操作进行验证,

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

         {

              status.Text=User.GetType().ToString();

              if(((MyPrinciple)User).HasPermission(2))

              {

                   status.Text=status.Text+" and you have the permission";

              }

              else

              {

                   status.Text="sorry you hava'not the permission";

              }

         }

OK结果符合了预期的效果。

图:

 


好了,入门学习,水平不高,希望大家指正。