有用过Security Application Block(以下简称SAB)的朋友都知道,它的权限规则是直接存在配置文件中,并没有提供存在数据库的实现形式,大家也都知道已经有人对它进行了扩展,提供了一个叫DBRulesAuthorizationProvider的Provider。(http://www.gotdotnet.com/Community/UserSamples/Details.aspx?SampleGuid=c0b0ce8e-1b72-44ab-b300-f369eb18664a)。对于我来说,这是一个非常不错的Provider,现在都它了。可是却存在一个问题,每次验证授权的时候都去读取数据库,特别是当我想得到某个或某个角色所具有的所有权限时(SAB使用的是逻辑表达式的形式进行授权,除了在程序中去遍历所有的权限规则,我目前是没有想到有其它别的好的办法可以获得某个人所具有的所有功能,如果有人有好的办法请告知。),去遍历权限表一个一个地去验证这个用户(或角色)是否具有这个权限,而不得不去循环访问数据库。比如有100个权限,我就要访问100次数据库,我想这对每个人来说都是不可接受的,也是我对这个Provider进行修改的主要原因。修改的目的就是要给它加上缓存权限的功能,要把数据库中规则表的所有记录都读取下来,缓存在本地。这样以后进行授权验证的时候就不用担心频繁访问数据库了。缓存?缓存在内存中合适吗?Enterprise Library 也有提供缓存程序块(Caching Application Block ),可以提供多种不同的缓存方式,可以缓存在内存,文件和本地数据库中。经过衡量,决定缓存在文件中。OK,打开DBRuleAuthorizationProvider源码......
找到DBRulesAuthorizationProviderData.cs,我不知道它叫什么名字,反正在这一整个企业库里面每一个的配置节都有这样的一个叫ProviderData的类。在里面添加上一个属性的一个构造函数。
//使用的缓存管理器的名称
[XmlAttribute(AttributeName = "cacheManager")]
public string CacheManager
{
get {return this.cacheManager;}
set {this.cacheManager = value;}
}
public DbRulesAuthorizationProviderData(string name,string database,string cacheManager) : this(name,database)
{
this.cacheManager = cacheManager;
}
修改DBRulesAuthorizationProviderNode.cs,修改这个是为了让我们使用Configuration Console的时候可以为它指定一个缓存管理器。如下图
在里面添加如下代码
private ConfigurationNodeChangedEventHandler onCacheManagerRemoved;
private ConfigurationNodeChangedEventHandler onCacheManagerRenamed;
[Editor(typeof(ReferenceEditor), typeof(UITypeEditor))]
[ReferenceType(typeof(CacheManagerNode))]
[Required]
[Description("The database instance that will be used to query for rules")]
public CacheManagerNode CacheManager
{
get { return this.cacheManager; }
set
{
ILinkNodeService service = GetService(typeof(ILinkNodeService)) as ILinkNodeService;
Debug.Assert(service != null, "Could not get the ILinkNodeService");
this.cacheManager = (CacheManagerNode)service.CreateReference(cacheManager, value, onCacheManagerRemoved, onCacheManagerRenamed);
this.DbRulesAuthorizationProviderData.CacheManager = string.Empty;
if (this.cacheManager != null)
{
this.DbRulesAuthorizationProviderData.CacheManager = this.cacheManager.Name;
}
}
}
private void OnCacheManagerRemoved(object sender,ConfigurationNodeChangedEventArgs args)
{
this.cacheManager = null;
}
private void OnCacheManagerRenamed(object sender,ConfigurationNodeChangedEventArgs args)
{
this.DbRulesAuthorizationProviderData.CacheManager = args.Node.Name;
}
//
// extended by hjf
//
private void CreateCachingSettingsNode()
{
if(!CachingSettingNodeExistes())
{
AddConfigurationSectionCommand cmd = new AddConfigurationSectionCommand(Site,typeof(CacheManagerSettingsNode),CacheManagerSettings.SectionName);
cmd.Execute(Hierarchy.RootNode);
}
}
private bool CachingSettingNodeExistes()
{
CacheManagerSettingsNode node = Hierarchy.FindNodeByType(typeof(CacheManagerSettingsNode)) as CacheManagerSettingsNode;
return (node != null);
}
this.onCacheManagerRenamed = new ConfigurationNodeChangedEventHandler(this.onCacheManagerRenamed);
Debug.Assert(cacheManagerSettingNode != null,"How is it that the cachemanager settings are not there?");
CacheManagerCollectionNode cacheManagerCollectionNode = Hierarchy.FindNodeByType(typeof(CacheManagerCollectionNode)) as CacheManagerCollectionNode;
this.cacheManager = Hierarchy.FindNodeByName(cacheManager,this.DbRulesAuthorizationProviderData.CacheManager) as CacheManagerNode;
现在我们看看DbRulesManager.cs,读数据库数据的操作都在这里了,现在CacheManager就用在这边了。用了缓存后,我是将数据库中所有的规则权限都一次性读到本地中,用CacheManager缓存起来。原来还以为自己要写存储过程,仔细看一下代码,它已经写好了一个方法,public AuthorizationRuleDataCollection GetAllRulesAsCollection(){},读出所有的Rule,存在AuthorizationRuleDataCollection 里。查看一下AuthorizationRuleDataCollection 的定义。太好了,它可以根据RuleName ,取出对应的RuleData。那我只要把生成的AuthorizationRuleDataCollection 对象保存起来不就可以了,还有一个问题AuthorizationRuleData是不可序列化的,没法缓存。改一下Security 的源码就行了,给AuthorizationRuleData加上[Serializable]属性。新增代码
private Microsoft.Practices.EnterpriseLibrary.Caching.CacheManager cacheManager = null;
//增加构造函数,初始化cacheManager
public DbRulesManager(string databaseService,string cacheManagerService,ConfigurationContext config) : this(databaseService,config)
{
CacheManagerFactory factory = new CacheManagerFactory(config);
cacheManager = factory.GetCacheManager(cacheManagerService);
}
//增加Property,外面调用这个属性就可以了
public AuthorizationRuleDataCollection AllRuleCollection
{
get
{
AuthorizationRuleDataCollection m_rulesCollection = null;
if(cacheManager[STR_CACHE_RULESCOLLECTION] != null)
m_rulesCollection = cacheManager[STR_CACHE_RULESCOLLECTION] as AuthorizationRuleDataCollection;
if(m_rulesCollection == null)
{
m_rulesCollection = GetAllRulesAsCollection();
AllRuleCollection = m_rulesCollection;
}
return m_rulesCollection;
}
set
{
if(value == null)
cacheManager.Remove(STR_CACHE_RULESCOLLECTION);
else
//
// 缓存数据一天。
//
cacheManager.Add(STR_CACHE_RULESCOLLECTION,value,CacheItemPriority.High,null,new SlidingTime(new TimeSpan(1,0,0,0)));
}
}
最后,我们来修改DBRulesAuthorizationProvider.cs,这个就是这个Provider对外提供的调用方法,我们只要修改根据RuleName读出Expression的那段代码就行了.
private string cacheManager;
// mgr = new DbRulesManager(database, this.securityConfigurationView.ConfigurationContext);
//用这个函数替换上面的函数
mgr = new DbRulesManager(database,cacheManager,this.securityConfigurationView.ConfigurationContext);
//增加下面的函数
private BooleanExpression GetParsedExpression(string Expression)
{
Parser p = new Parser();
return p.Parse(Expression);
}
//修改IConfigurationProvider的public override void Initialize(ConfigurationView configurationView)增加这句代码
cacheManager = data.CacheManager;
第一次随笔,组织得会比较乱,没办法,就这水平了,请见谅。