阿不

潜水

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

    有用过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的类。在里面添加上一个属性的一个构造函数。

private string cacheManager; 
//使用的缓存管理器的名称
[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 CacheManagerNode cacheManager;
        
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.onCacheManagerRemoved    = new ConfigurationNodeChangedEventHandler(this.onCacheManagerRemoved);
            
this.onCacheManagerRenamed    = new ConfigurationNodeChangedEventHandler(this.onCacheManagerRenamed);
修改ResolveNodeReferences函数加上
CacheManagerSettingsNode cacheManagerSettingNode    = Hierarchy.FindNodeByType(typeof(CacheManagerSettingsNode)) as CacheManagerSettingsNode;
            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;
修改AddDefaultChildNodes()函数,加上
CreateCachingSettingsNode();
OK,就这样,把编译好的DLL拷到C:\Program Files\Microsoft Enterprise Library\bin,再添加 Database Authorization Provider,配置项,就加了一个CacheManager的值项了.
    现在我们看看DbRulesManager.cs,读数据库数据的操作都在这里了,现在CacheManager就用在这边了。用了缓存后,我是将数据库中所有的规则权限都一次性读到本地中,用CacheManager缓存起来。原来还以为自己要写存储过程,仔细看一下代码,它已经写好了一个方法,public AuthorizationRuleDataCollection GetAllRulesAsCollection(){},读出所有的Rule,存在AuthorizationRuleDataCollection 里。查看一下AuthorizationRuleDataCollection 的定义。太好了,它可以根据RuleName ,取出对应的RuleData。那我只要把生成的AuthorizationRuleDataCollection 对象保存起来不就可以了,还有一个问题AuthorizationRuleData是不可序列化的,没法缓存。改一下Security 的源码就行了,给AuthorizationRuleData加上[Serializable]属性。新增代码
private const string STR_CACHE_RULESCOLLECTION    = "Cache:RulesCollection";//缓存KEY

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;
整个的修改工作就这样了,把编译好的DLL Copy到Enterprise Library安装目录的bin目录下,就可以使用了,有一点要注意,这期间我修改了Security 工程的AuthorizationRuleData类,请也将它重新编译一下,Copy 到bin 目录下,否则数据将无法缓存。还有一点就是如何控制缓存的过期,在修改权限表达式的时候可要记住清空缓存喔,我曾想在DbRulesAuthorizationProvider,增加一个方法设计缓存的过期,可是程序是通过IAuthorizationProvider接口来调用的,修改它就要修改很多地方了。而为了使用的一致性又不想运行时去将它转换成DbRulesAuthorizationProvider类型.....还在考虑中,呵呵
    第一次随笔,组织得会比较乱,没办法,就这水平了,请见谅。
posted on 2005-08-11 16:03  阿不  阅读(1379)  评论(2编辑  收藏  举报