構建基於角色、由應用程序管理的授權框架

        安全性是一個非常大的課題,本篇文章旨在探討授權部分。
如果你不理解安全性相關概念,可以參考如下幾篇文章:
        1、Web 應用程序安全框架:http://erpcrm.cnblogs.com/articles/234776.html
        2、設計由應用程序管理的授權:http://erpcrm.cnblogs.com/articles/234793.html
        3 、.net 基於角色的安全性介紹 http://msdn.microsoft.com/library/chs/?url=/library/CHS/cpguide/html/cpconIntroductionToRole-BasedSecurity.asp

本文假設你對.net 安全性相關概念如Principal 和Identity有所瞭解。
一、分析
先看一個基於角色授權的需求:
在一個CRM系統中,用戶 “zhangsan”屬於銷售部的一名普通員工,“lisi”是該銷售部的部門經理,“zhangsan”作爲一名銷售員有創建新客戶的權利(customer.add),刪除客戶的權利(customer.delete)只有部門經理纔有。
這種需求,我們一般情況下,建立兩個角色(employees,managers),zhangsan屬於employees,  lisi屬於managers,
然後我們再定義employees有customer.add的權利,managers兩個權利都有,這樣就基本滿足了這種需求。

現在有一個更深層次的需求:zhangsan作爲銷售員需要報銷一些行銷費用,在報銷流程中,1000元以下的費用由部門經理審批,超過1000元的由總經理審批,類似這樣的需求,我們認爲屬於業務邏輯範疇,本授權框架不解決類似問題。

本框架的數據持久部分如下圖:
本持久部分是該授權框架的一個持久實現,本框架數據持久部分採用porvider模型,允許你定義自己的持久實現。


二 利用.net實現該框架
在.net中基於角色安全性的兩個重要概念就是 Principal(主體),Identity(標示)。
在該框架中 ECubePrincipal 實現了IPrincipal接口,ECubeIdentity 實現了IIdentity接口。
 

using System;
using System.Runtime.Serialization;
using System.Configuration;

namespace ECube.Business.Security
{
    [SerializableAttribute]
    
public class ECubeIdentity : System.Security.Principal.IIdentity
    
{
        
private string _Name;
        
private string _AuthenticationType;
        
private bool _IsAuthenticated;
    

        
public ECubeIdentity()
        
{
            _AuthenticationType 
= "ECubeCustom";        
        }

        
internal ECubeIdentity(String name, Boolean isAuthenticated)
        
{
            _Name 
= name;
            _IsAuthenticated 
= isAuthenticated;
        }


           
public string Name
        
{
            
get
            
{
                
return _Name;
            }

        }

        
public bool IsAuthenticated
        
{
            
get
            
{
                
return _IsAuthenticated;
            }

        }

        
public string AuthenticationType
        
{
            
get
            
{
                
return _AuthenticationType;
            }

        }

    }

}

上面這個是ECubeIdentity的源碼,比較簡單,沒啥可說的。
下面看看ECubePrinicpal:

using System;
using System.Collections.Generic;
using System.Text;
using System.Security;
using System.Security.Principal;
using System.Web.Security;
using ECube.Business.SystemFramework;
using ECube.Business.Security.Data;

namespace ECube.Business.Security
{
    [SerializableAttribute]
    
public class ECubePrincipal : IPrincipal
    
{
        IIdentity _Identity;
        
private static  IUserProvider _UserProvider;
        
private static  IRoleProvider _RoleProvider;
        
private static  IPermissionProvider _PermissionProvider;
        
private static  String[] _Roles;

        
private Dictionary<String, Boolean> permissions = null;

        
public  ECubePrincipal(IIdentity identity)
        
{
            _Identity 
= identity;

            
//如果在ASP.NET 中是Forms授權
            if(identity is System.Web.Security.FormsIdentity)
            
{

                
//檢查Membership.Provider是否實現 IUserProvider;
                if (!(System.Web.Security.Membership.Provider is IUserProvider))
                
{
                    
throw new SecurityException("System.Web.Security.Membership.Provider Must is IUserProvider Type.");
                }

                
//檢查Roles.Provider是否實現 IRoleProvider;
                if (!(System.Web.Security.Roles.Provider is IRoleProvider))
                
{
                    
throw new SecurityException("System.Web.Security.Roles.Provider Must is IRoleProvide Type.");
                }


                _UserProvider 
= Membership.Provider as IUserProvider;
                _RoleProvider 
= Roles.Provider as IRoleProvider;
            }

            
else if(identity is System.Security.Principal.WindowsIdentity)
            
{

            }

            
else if(identity is System.Web.Security.PassportIdentity)
            
{

            }

            
else if(identity is System.Security.Principal.GenericIdentity)
            
{

            }

            
else if(identity is ECubeIdentity)
            
{
                
            }

            
else
            
{

            }


            
//取得該用戶的 “許可集”
            permissions = PermissionProvider.GetPermissions(identity.Name);
                
        }


        
/// <summary>
        
/// 返回角色數據提供者。
        
/// </summary>

        public static  IRoleProvider RoleProvider
        
{
            
get
            
{
                
if (_RoleProvider == null)
                
{
                    
//首先檢查當前應用程序的配置中是否指定了 角色提供者
                    String roleProviderName = BizApplication.Current.Parameters["RoleProviderName"].Value;
                    
//如果沒有
                    if (String.IsNullOrEmpty(roleProviderName))
                    
{
                        
//取得配置文件中Role段的默認角色提供者名字
                        roleProviderName = BizSecuritySection.Role.DefaultProvider;
                    }


                    
if (!String.IsNullOrEmpty(roleProviderName))
                    
{
                        
//根據名字,取得對應的Provider 
                        _RoleProvider = BizSecuritySection.Role.GetProvider(roleProviderName);
                    }


                    
//如果還沒有取到,並且定義了提供者
                    if (_RoleProvider == null && BizSecuritySection.Role.Providers.Count > 0)
                    
{
                        
//取得第一個
                        _RoleProvider = BizSecuritySection.Role.GetProvider(BizSecuritySection.Role.Providers[0]);
                    }


                    
//如果還沒取得
                    if (_RoleProvider == null)
                    
{
                         
//使用默認的。
                        _RoleProvider = new Role(); 
                    }


                }

                
return _RoleProvider;
            }

        }

        
/// <summary>
        
/// 返回用戶提供者。
        
/// </summary>

        public static  IUserProvider UserProvider
        
{
            
get
            
{
                
if (_UserProvider == null)
                
{
                    
//首先檢查當前應用程序的配置中是否指定了 
                    String userProviderName = BizApplication.Current.Parameters["UserProviderName"].Value;

                    
//如果沒有
                    if (String.IsNullOrEmpty(userProviderName))
                    
{
                        
//取得配置文件中User段的默認提供者名字
                        userProviderName = BizSecuritySection.User.DefaultProvider;
                    }


                    
if (!String.IsNullOrEmpty(userProviderName))
                    
{
                        
//根據名字,取得對應的Provider 
                        _UserProvider = BizSecuritySection.User.GetProvider(userProviderName);
                    }

                    
//如果還沒有取到,並且定義了提供者
                    if (_UserProvider == null && BizSecuritySection.User.Providers.Count > 0)
                    
{
                        
//取得第一個
                        _UserProvider = BizSecuritySection.User.GetProvider(BizSecuritySection.User.Providers[0]);
                    }


                    
//如果還沒取得
                    if (_UserProvider == null)
                    
{
                        
//使用默認的。
                        _UserProvider = new User();
                    }

                }

                
                
return _UserProvider;
            }

        }

        
/// <summary>
        
/// 返回許可提供者.
        
/// </summary>

        public static  IPermissionProvider PermissionProvider
        
{
            
get
            
{

                
if (_PermissionProvider == null)
                
{
                    
//首先檢查當前應用程序的配置中是否指定了
                    String permissionProviderName = BizApplication.Current.Parameters["PermissionProviderName"].Value;
                    
//如果沒有
                    if (String.IsNullOrEmpty(permissionProviderName))
                    
{
                        
//取得配置文件中User段的默認提供者名字
                        permissionProviderName = BizSecuritySection.Permission.DefaultProvider;
                    }

                    
if (!String.IsNullOrEmpty(permissionProviderName))
                    
{
                        
//根據名字,取得對應的Provider
                        _PermissionProvider = BizSecuritySection.Permission.GetProvider(permissionProviderName);
                    }

                    
//如果還沒有取到,並且定義了提供者
                    if (_PermissionProvider == null && BizSecuritySection.Permission.Providers.Count > 0)
                    
{
                        
//取得第一個
                        _PermissionProvider = BizSecuritySection.Permission.GetProvider(BizSecuritySection.Permission.Providers[0]);
                    }

                    
//如果還沒取得
                    if (_PermissionProvider == null)
                    
{
                        
//使用默認的。
                        _PermissionProvider = new Permission();
                    }

                }

                
return _PermissionProvider;
            }

        }

       
        
/// <summary>
        
/// 註銷
        
/// </summary>

        public void Logout()
        
{
            
this._Identity = null;
            System.Threading.Thread.CurrentPrincipal 
= null;
        }


        
/// <summary>
        
/// 登錄,這是一個靜態方法
        
/// </summary>
        
/// <param name="usrName">用於驗證的用戶名。</param>
        
/// <param name="usrPwd">用於驗證的密碼</param>
        
/// <param name="isSecurity">是否使用安全的提示,如果否:當驗證錯誤時,提示非常明確,如“沒有找到該用戶!”,“密碼錯誤!”。
        
/// 否則,給出非常模糊的提示。
        
/// </param>

        public static  void  Login(string usrName, string usrPwd, bool isSecurity)
        
{
          
            
try
            
{
                
if (_UserProvider != null)
                
{
                    
if (_UserProvider.ValidateUser(usrName, usrPwd, isSecurity))
                    
{
                      

                        DSSecurity.SECRolesDataTable roleTable 
= RoleProvider.GetRolesForUser(usrName);

                        _Roles 
= new String[roleTable.Count];
                        
for (int i = 0; i < roleTable.Count; i++)
                        
{
                            _Roles[i] 
= roleTable[i].Name;
                        }


                       
                        IIdentity identity 
= new ECubeIdentity(usrName, true);                            
                        System.Threading.Thread.CurrentPrincipal 
= new ECubePrincipal(identity );
                        
return;
                    }


                }

            }

            
catch (System.Exception ex)
            
{
                
if (ex is SecurityException)
                
{
                    
throw;
                }

                
else
                
{
                    
//做一些記錄等工作。
                }

            }

           

        }

        
   
        
/// <summary>
        
/// 檢查權限。
        
/// </summary>
        
/// <param name="permissionKey">權限的鍵:如在增加客戶的權限可以描述爲:“Customer.add”</param>
        
/// <returns></returns>

        public Boolean CheckPermission(String permissionKey)
        
{
            
if (String.IsNullOrEmpty(permissionKey))
            
{
                
return false;
            }


            
if (_Identity == null)
            
{
                
return false;
            }


            
if (!_Identity.IsAuthenticated || permissions == null || permissions.Count == 0)
            
{
                
return false;
            }


            
return permissions[permissionKey];

        }


     
        
#region IPrincipal 成員

       
public  IIdentity Identity
        
{
            
get
            
{
                
if (_Identity == null)
                
{
                    _Identity 
= new ECubeIdentity("guest",false);
                }

                
return _Identity;

            }

        }


        
public bool IsInRole(string role)
        
{
            
throw new Exception("The method or operation is not implemented.");
        }


        
#endregion

    }

}

如果你只是想使用本框架的話,你接觸到的只會是ECubePrincipal類。如果你想實現自己的持久,你可以實現
IRoleProvider、IUserProvider、IPerssionsProvider接口,當然了還需要一些配置(不過我會實現一個工具來完成這個過程)
如何使用本框架進行登錄驗證呢?
場景一:
考慮一個非ASP.NET 環境,你在登錄窗口的登錄按鈕的CLICK 事件中,可以這樣寫:
{
        if( ECubePrincipal.Login(usernmae,userpwd,true))
        {
            ....
        }
}
就這麼簡單。
場景二:
ASp.net環境,asp.net 中可能稍微複雜一些,因爲asp.net 有自己的驗證機制,但也不是沒辦法。
我們可以在Global.asax的方

法中寫如下代碼(當然你可以自己實現一個IhttpModule,不一定非得在Global.asax中)

 

void Applicaiton_AuthenticateRequest(object sender, EventArgs e)
    
{
        
if (sender == null)
        
{
            
return;
        }


        HttpContext Context 
= ((HttpApplication)sender).Context;
        
if (Context.User != null && !(Context.User is ECube.Business.Security.ECubePrincipal))
        
{
            
if (Context.User.Identity is FormsIdentity)
            
{
                
if (Membership.Provider is ECube.Business.Security.IUserProvider &&
                    Roles.Provider 
is ECube.Business.Security.IRoleProvider)
                
{
                    Context.User 
= new ECube.Business.Security.ECubePrincipal(Context.User.Identity);
                }

            }

            
else
            
{
                Context.User 
= new ECube.Business.Security.ECubePrincipal(Context.User.Identity);
            }

        }

        
    }

 


三、如何進行授權

 

 

驗證授權驗證非常簡單:
class Customer
{
   void Add()
   {
    ECubePrincipal princ = System.Threading.Thread.CurrentPrincipal as ECube.Business.Security.ECubePrincipal
   if(princ != null && princ.CheckPermission("customer.add"))
{

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章