只是想记录一下自己的想法,

以轻松的心态看问题,以怀疑的心态看答案
随笔 - 39, 文章 - 1, 评论 - 24, 引用 - 0
数据加载中……

2008年12月23日

大话权限设计

前言

      如何实现一个高效简单的系统权限体系是我们长期以来都在思考的问题,也是最近一年来我思考得最多的问题,我们所期望的权限都是应当能够根据应用的需要不断添加和扩展的权限,并且最好能够以最简单的方式来支持,那就最好不过了。

   内容

  • 什么是权限系统
  • 一个简单的设计 
  • 基于角色的访问
  • 最初的数据权限
  • 更好的解决方法 
  • 最近的战役
  • 总结

 

什么是权限系统

     那么我们需要一个什么样的权限系统呢或者说什么是权限,我查看了很多的相关资料想要试图解决这个问题,最后看一个最简单最明确的答案"安全问题就是解决谁对什么能够进行什么控制的问题”。如何来分析这段话呢,我敢保证这是我见过的最明确也是最抽象的需求,通过什么样的方式来分析和解决这个需求问题呢。

而对复杂的问题我们必须拿出强大武器才行啊,对于不明确的概念我们首先需要完成的是将“概念明确化”先将这段话中不明确的代词进行明确化,那么我们就从第一个代词开始“”是指什么呢,“员工”,“用户”,“领导”,...,还是什么其它呢,我想对于不同的问题我想需要采用不同的概念,如果这个系统只是用于解决公司内容的相关问题,那么这里使用“员工”最好不过,如果专门用于解决“领导”的个人问题,那么“领导”也不错。这里主要需要考虑我们所面临问题和抽象的层次,同时还需考虑到具体应用的行业标准。对于需要大多数情况采用“用户”或“当前用户”是一个比较好抽象,我们这里采用“当前用户”,这样比“用户”更明确一些,那么这段需求就变成了“安全问题就是解决当前用户对什么能够进行什么控制的问题”。

好解决了第一个抽象之后,对于其它问题我们同样采用相关的方式来进行分析,“当前用户对什么” 之中的“什么”当前相关问题的抽象层次之中基本上都是采用“安全资源对象”来进行抽象的,当然也可以根据实际处理的问题进行抽象,我们这里主要是考虑通用性。而对于“能够进行什么访问”之中的“什么”这里也需要根据相关问题的来进行设定,这里我们采用“访问或拒绝”,那么这一段需求就变成“安全问题就是解决当前用户能够对资源对象进行访问或拒绝控制的问题”,如果实现需要就是对功能进行控制还可以说成是“安全问题就是解决当前用户能够对功能进行访问或拒绝控制的问题"。

 

一个简单的设计

了解了问题的本质后,我们需要做的就是要完成它,那么怎样来完成这呢。首先我们需要从需求之中将我们领域模型分离出来: 

 

如果是一个简单的权限设计,那么整个权限系统不这样就完成了,非常的简单明了(对于这里各对象所需要领域访问就不在说明)。

 

基于角色的访问

当简单的设计使用的用户数量一天一天的增加的时候,我们发现给一个一个用户分别进行权限的设置是一件非常困难的工作。好,看来是我们修改设计的时候到了。最先想到的是我们必须对用户进行分组,将对用户的访问授权设置到这些组之中。好,那我们就设置一个用户组,通过用户组来设置吧,但我们又会想到在实际的业务之中一个多岗的情况很多啊,那么怎么办呢,同时如果能够以现实际之中公司的实际情况设置权限最好不过,因为这样对于系统的管理人员来说,只需要了解公司内部的情况就可以了,那么怎么样来协调这两个问题呢。

其实这个问题在安全设计的前辈们早已想到应对之策,就是将这些对象抽象以“角色”来表示这些抽象的概念。那么加入的角色对于我们的领域模型带来了什么样的变化呢?

这样对于我们的管理来说真的大大的简化了啊,但这就完全的满足了我们的条件了吗?

 

最初的数据权限

当我们正在为自己的设计而高兴的时候新的问题又来了,怎么回事呢。小郑啊,北美销售部只能够管理北美的销售啊,发动机事业部只能够销售发动机产品啊,你那个程序是怎么回事啊,怎么都能够看啊,还有怎么部长都能够审批100W以上的定单啊。

啊,我快要完蛋了。

好,兵来将挡,水来土淹什么问题都能不倒我。

看来我们还必须要设计满足数据权限的要求才行啊。

好吧,那我怎么样子来实现呢...?好吧,我应在安全资源之中加一个数据规格,在访问控制之中加入一个值的设定,怎么样,这下行了吧?

 

好吧,现在就看我来实现它吧,以我精湛的技术。

啊,什么,一个用户多个角色,不同的角色有不同的数据规则控制。啊,还需要数据控制的合并,没事我们使用提供者模式来解决,实现不同的合并提供者,使用提供者来合并结果。

啊,什么,怎么样转换到数据访问的控制,没事,咱们硬编码。

啊,不可能吧,连个获取操作都需要进行控制啊。

啊,我不行了。倒在了黎民前了晚上。

 

更好的解决方法

如果没有能够对数据权限进行详细的分析,那么一定会在倒在黎明的前夜,没有完整的解决方法,那是不行的。好,我们先来分析一个数据权限的要求,这里我只能够写出我对于这部分的分析,数据权限的限制总是:{实体属性值 条件 允许值}这三个产品分组成,如:数据规则,销售部长只能够审核销售金额小于100W的定单这里的三者是{销售定单.合计金额 小于 100W}, 销售员小张只能够销售摩托车产品,这其中的三者是{销售项目.产品类型 属于 摩托车)

所以这里对于数据的限制都只包括三个要素:领域实体属性 条件 允许值。这三者的结合就限制的约束。那么这三都之中都有哪些情况呢?

领域实体属性:这个基本上都没有什么变化。

条件:如果是条件就只有那么几种,有大于,小于,等于,实体范围等等。

允许值:允许值这里经过我的分析包括三种情况{设置值,用户属性相关值,业务设定值}

这三种情况分别使用于以下情况:设置值表示是我们是设置权限的时候直接进行设置的如100W,用户属性相关值表示与用户的某一个属性相关,如:部门,职务等。还有一种就是业务相关的情况,这类情况相当于是在业务之中设置数据。这类问题在SAP之中设置比较多,感兴趣的可以去了解一下SAP之中的相关设置。

在这里对于条件的设置基本上都只有固定的几种,可以采用枚举的方式来进行,而对于充许值这一个点,因为不同的业务所需要的不同的数据和相关内容不想同,因此,将这一个允许值的获取设置为一个相关的接口来进行,而在进行配置的时候只设置一个相关值,由接口去解晰和获取。这时候的领域模型图如下:

 

 

最后的战役  

在解决了领域问题之后,我们还需要考虑提供什么方式来获取这些服务,这里首先需要考虑使用权限的两种情况,一种是当需要显示相关界面时; 二是当需要执行相关操作时。

  • 第一种情况:当我们进入到提供定单的界面,那么显示出来的相关产品肯定是我们能够允许提供的产品,显示出来的国家也是我们能够进行销售的国家。
  • 第二种情况:当我们提交定单时,提供的内容必须进行检查。

对于第一种情况:我们在执行GetCountries()和GetProducts()这两个方式的内部就必须执行条件的限制,那么如何来实现这个条件的限制呢。这里必须要通过一种方式让GetCountries了解到当前正自于那一个安全的环境之中,再获取获取相应的数据限制,查看这个限制的实体是否为Country如果是那么是那个条件进行的设置编号,国家名称,上级部门。再将这些条件转换为对应的SQL来进行限制,如果这些方法都是通过硬编号来完成,那么一定是一个复杂的工作,好在现在的ORM工具直接就支持领域实体,属性,条件等内容,直接可以将这些配置的安全值设置到这些访问之上,这样就可以完成整个数据限制,同时还需要考虑到如果是在多个不同的应用之间设置这些服务,还需要考虑到安全的层次关系,在多个层次之中使用和设置多层安全环境。这就必须使用一个对应的环境管理类来管理这些环境。对于这个环境可以采用SecurityContext来表示,而对于这个环境的管理类使用SecrityContextManager的线程单例模式来进行设置。在实现的过程之中,如果需要一个新的安全环境则建立一个新的SecurityContext,在新实例的SecurityContext之中将自己注册到SecurityContextManager的单实例之中。同时在建立SecurityContext时传入相关的安全资源对象,对这些对象进行安全检查,如果没有进行授权则引发System.Security.SecurityException对象。

在开发数据访问层时,通过SecurityContextManager.Instance来获取如:Employee实体的的限制,通过前面所设计的获取允许值提供者接口来获取允许的值,并且将属性,条件,允许值,转换为对应的SQL来进行调用。这样就实现了多层次的数据权限。如果对应的ORM工具能够直接支持条件的设置则可以将这个工作直接编写对应的转换器来进行转换,这样可以大大的提高开发的效率,并且还可以根据需要对领域成员进行新的数据约束,而不必修改应用程序。

同时如果所需要限制的数据资源不是领域实体中的数据,也可以通过这种方式来设置自己的数据范围的检查。

 

总结

本文主要是介绍一种安全的实现方式,安全不是一个简单到只需要在某一具体层就能够解决的问题,而需要在多个层次进行多层次的设置。形成类似于IIS式的多层次,多角度权限设置,这里只是给出了其中最为核心的内容,没有给出具体的代码,这主要是因为不能够公布公司内部开发的程序,所以可能对于完整的理解有所不足。

 

对本文有什么好的意见或见意请大家多指教。

 

 

 

posted @ 2008-12-23 11:05 蓝色游骑兵 阅读(2336) | 评论 (12)编辑

2008年12月17日

安全系统的设计

新的需求

昨天发现开发的安全中心已不能够满足外部系统对其的安全访问需求,基本情况是这样的,当前公司需要开发一个反馈中心中,有一个快速反馈的功能,其中有一个需求是能够将快速反馈的通知已邮件的形式发送到相关处理人那里通知他们说,新的反馈信息来了,你们快点来处理啊。

以前设计的安全模型是以下方式:

使用部分为一个用户有一个或多个安全角色,一个安全角色包括一组活动授权,每一个活动授权包括一组授权限制。

而定义部分是一个业务活动包括一组活动限制。

这种方式在处理安全授权的问题的过程是,首先获取活动授权是否支持活动,在查询活动的限制,并且将结果返回给调用者,而这其中需要处理两个问题。

用户授权的检查:当一个用户包括多个安全角色时需要将这些安全角色中的授权信息进行合并,这里使用拒绝优先的原则,所以只要有授权并且不是拒绝授权,就表示包括授权,而如果没有授权或其中一个授权为拒绝就表示授权失败。这里在处理合并处理起来比较简单。

授权限制:当一个用户包括多个安全角色时,还需要将授权约束进行合并,并且这里还需要考虑合并的策略。如:审核金额的限制,数值大的合并数值小的金额,而如果这是属于一定的领域实例,那么这里的合并就是将主键按一定规则进行相加。这里在处理时我将这一合并定义为一个合并的策略接口,通过实现不同的接口来完成这个操作,基本上解决了这个问题。

 

当前模型的问题

但当出现了前面的需求时,我发现我就要完蛋了,根本就没有什么方式能够获取到处理相关反馈的人。这里在处理反馈信息时,会根据反馈类别和反馈的国家进行限制,如:质量问题,中国,日本的就由技术部的张三处理;业务问题,中国,日本的就由业务部的李四处理。通过上而的模型对于这一需求我想真的是完蛋了。

并且经过后期的实践我发现多个业务活动都需要相同的活动限制。如:业务部的李四处理与中国,日本相关的业务问题。这里有两个业务活动,提交定单和查询客户这两个业务之中,李四都只能够处理与中国,日本相关的内容。如果采用以上模型那么就必须在两个地方进行相应的处理,而如果这里相关的业务有10个那么当李四重新分配管理法国和德国的时候就必须设置10次。

而且当前公司在处理销售时基本上是这种一个人处理一些国家的方式,这样如果将这些信息全部设置在角色之中那么定然会让角色信息出现爆炸式增长。

 

模型的改进

为了实现前面提出的安全需求同时解决当前模型中的问题,这里我单独的将活动授权提取出来为“数据规则”,同时将业务活动对活动限制设置为“关联关系”。将根据授权中对授权限制的设置移动到用户之下,既用户数据限制,通过这种方式的修改后的安全模型如下:

 

通过这种方式当用户需要一个活动授权时,先查询该用户的活动授权情况,如果允许再通过业务活动与数据约束的相关性,获取到相关的数据约束的值。同时这个模型也减少了对于数据约束的合并的问题。

还需要解决的问题

同时如果需要获取相关的用户,这里还需要考虑到数据的过滤问题,只返回与其相知道的用户,这个问题我这里还没有想好。

以上模型基本实现功能权限和数据权限的约束,具体应用还需要我将整个安全中心按新的模型修改完成后。

这里的模型是在实际应用之中减去不必要的内容后形成的安全核心内容。

希望如果大家有什么好的意见请多赐教,欢迎大家的批评指正。

posted @ 2008-12-17 11:23 蓝色游骑兵 阅读(1713) | 评论 (2)编辑

2008年10月25日

ASP.NET 安全问题的思索

这两天在自己的努力工作之下终于将早就应当完成的安全中心基本完成,也提供了基本的B/S界面。但当这个问题才完成我又发现了自己开发程序中出现的新的问题,问题基本是这样的:

    对于程序中的安全我将其封装为一个基本的类库来进行处理的,这个库就是XXX.Security,这个库中包括了处理安全所需要基本的接口和接口的基本实现。而现在开发完成了安全中心,这个安全中心就是这个基础XXX.Security中接口的其中一个实现。

   现在就需实现这个扩展的XXX.Security,其实实现这个扩展的安全程序集并不复杂,三五两下就搞好了。但我的想法是不能让外部程序来伪造其中的身份标识IIdentity,所以我这里将SecurityImpls程序信中的Identity定义内部类,这样外部就不可能会创建出这个IIdentity接口的对象,并且在招待SecurityImpls的各种接口之前先验证这个IIdentity接口是否为内部Iidentity的标识,这样有效的保证了这个Iidentity决不会是外部伪造的,这种方式用于处理C/S的Forms应用程序绝对没有问题,直接将SecurityImpls中验证产生的IIdentity对象进行保存就可能了并且也能够很好的进行工作。

  但当这种模式用来处理B/S模式时就产生了很大的问题,主要是B/S模式不能将这个IIdentity进行保存,而如果将这个IIdentity以某一种方式进行串行化后再写入到Cookie之中,那么又通过怎么样方式来重建这个Identity呢。因为这个IIdentity对象是内部类,外部只能够通过IIdentity接口来进行访问。这里我想了可能有以下方式来处理这个问题:

   一,在Cookie之中保存这个Identity的用户名和密码,验证类型等信息,在Application_Authenticate方式之中调用用户名和密码进行重新的验证,并在当前线程之中保存这个新生成的IIdentity对象。

  二,使内部IIdentity实现串行化的接口,再读取后由SecurityImpls之中的相关中重新生成返回新的IIdentity,并设置相应值。

  三,在SecurityImpls中取消对IIdentity的检查,只检查相应的标识值,通过标识值来进行授权检查。

 第一种方式可能有效的保证访问的安全性,但第次交互都进行这样的检查可能会产生性能上的问题,并且如果当前用户修改了密码,那么还必须重新登陆操作,并且如果这个验证过程需要很长的时候可能就更加的不可靠了。

 第二种方式就让客户代码有了制造伪造IIdentity的可能性。

 第三种方式方式就直接伪造的过程都不需要了。

那么这样才能够有效的解决这个问题呢,我想先想一些必须满足的条件再根据这些条件的设定来思考怎样的解决方案,这样比较能够解决这个问题:

   1,第一次的重新验证是不能够受的,这样会让其可能成为整个系统的性能瓶颈。

   2,没有任何的安全看起来是绝对的安全,所以想要设计一个完美的安全是不可能的。

   3,应当对采用Security来完成的应用程序充分的信任,要相信它是不会做坏事的,要是它想做也只是删除自己的数据而已。

   4,不应当增加ASP.NET在使用整个Security时的复杂度,太复杂的东西是最不可靠的。

   5, 不应当增加已有Security之中的整体结构,小范围内的修改是不可必免的。

通过以上原则可能看出,采用第二种方式来进行处理是一个看起来唯一可行的办法,而原来用于提供用户验证的Security接口是:

Code

而对于身份标识的串行和反串行化最应当了解的就是这个IAuthorizationProvider接口,如何让这个接口来支持新的操作相关操作呢,我想有两种方式一种方式是添加直接的接口来进行支持接口可能定义如下:

  public interface IAuthorizationProvider
{
    IPrincipal Authenticate(
object credence);
    
string AuthenticateToString(object credence);

}

 这种方式虽然将IAuthorizationProvider的问题处理了,但这里还有IAuthorizationProvider,原有的IAuthroizationProvider接口定义如下:

public interface IAuthorizationProvider

{

   bool CheckAuthorizate(IIdentity identity,params string[] activityNames);

   AuthorizationType[] QueryAuthorizate(IIdentity identity,params string[] activityNames);

  IActivityAuthorization[] Authorizate(IIdentity identity,params string[] activityNames);

}

 通过以上方式则必须添加以下接口方法:

public interface IAuhtorizationProvider

{

   .... // 原有接口。

  bool CheckAuthorizate(string identity,params string[] activityNames);

  AuthorizationType[] QueryAuthorizate(string identity,params string[] activityNames);

  IActivityAuthorization[] Authorizate(string identity ,params string[] activityNames);

}

 这样就上对于内部Identity串行化的问题影响到了整个Security两个核心接口。

这里还可能采用第二种方式,在IAuthenticationProvider之中添加两个新的接口方法,将对于串行化的问题限定在IAuthenticationProvider接口内部,更好的实现内聚性并最少的减少对代码的变化。

public interface IAuthorizationProvider

{

   IPrincipal Authenticate(object credence);

   string To(IPrincipal principal);

   IPrincipal Form(string principal);

 

}

提供两个新的接口方法来支持向string的转换,这两个方法只有在处理ASP.NET的安全问题或需要进行串行化传送时才需要使用,而开发WinForm程序则直接使用IPrincipal方式。

同时为了让ASP.NET程序在使用时能够以更好的方式来进行处理和显示像用户名这样的内容,因此对于新生成的string还作出以下规定格式上的规定:

<userName>:<authType>:<customData>。<userName>

<userName>当前用户名

<authType>指验证的方式

<customData>部分由IAuthorizationProvider根据内部Identity自动生成。

 先就想到这里,先将代码写上,出现问题再修改。

 (新)

经过昨天下午的重要思考我发现解决客户应用程序的伪造凭证这个问题通过使用内部的IIdentity实现类来解决是根本不能够行的,因为客户应用程序可以根据设置一个与实现类相同的运行环境来再调用IAuthenticationProvider来创建这个IIdentity,再将这个IIdentity来调用另一个实际使用的IAuthenticationProvider这样就完成了整个伪造的过程,并且有效的避开内部IIdentity创建的约束。

通过这种方式可以看出采用内部IIdentity对象对于避开伪造凭证是无效的,而且采用内部凭证在使用时也会带来非常的不便。

 因此如果想你对授权的凭证进行处理,还是只能通过IIdentity的Name属性来进行设置,而通过采用相同的Name设置格式,格式定义如下:

[UserName]:[DisplayName]:[CustomeData]

[UserName]:用户名

[DisplayName]:显示名

[CustomData]:内部设置的凭证

在IAuthenticationProvider之中不需要对Iidentity的类型进行检查,只需要直接将Iidentity对象直接使用即可。而在当前的安全中心之中CustomeData用来存放第一次的登陆验证,每一次登陆后生成一个GUID作为登陆的验证。这样就算伪造了用户凭证也在用户下次登陆后变为无效凭证,同时保证同一用户只有在一个客户端使用。

 

posted @ 2008-10-25 10:15 蓝色游骑兵 阅读(119) | 评论 (0)编辑

2008年9月23日

自定义验证器的开发

在开发完成好ObjectBindingSource后,终于对于数据的访问和自动更新等操作来得方便了,特别是最天对于部分代码和接口进行重新修改后感觉使用起来真是很方便啊。

一口气就将四五个基本数据和维护写好了,只是画了些界面和设置而于,让我这三周的时间也算没有白费。

但写好后对于数据的验证问题又出现了,采用WinForm原有的那个数据验证真可以说是浪费了我一天的时间,结果写出来后通过使用才知道那个怎么能用啊。不但让很多结构看起来极为不爽,而且操作起来还有Bug,与我的ObjectBindingSource和ObjectBindingNavigator结合起来使用起也有很多问题,特别是自己就有很多问题。使用起来特别不爽。问了一下同事他说用了两天用起来极为不爽,后来就基本不用了,自己写个方法直接检查了事,但通过查看了一下他所写的代码看起来功能就完成了,但如同对一个基本的文本框的检查很重复性的工作,所以我还是想要从根本是来解决这个问题,通过近一个小时的思考,再将所画UML图贴出,再来整理一下思续。

这里是最初的想法,其中想要通过ValidationProvider来管理所有的相关验证器,一个窗体之上可以添加多个ValidationProvider,通过不同的ValidationProvider来管理不多个验证集。要下班了,实现后再写点实现代码。

posted @ 2008-09-23 17:31 蓝色游骑兵 阅读(26) | 评论 (0)编辑

2008年9月2日

WinForm 开发

这一段时间因开发的需要,需要深入的了解一些关于WinForm的开发工作,前面刚开发好一个可灵活的应用界面结构,这两周就需要向其中添加内容了啊,不用不知道一用才知道WinForm开发其实多一麻烦的,主要是因为WinForm都的数据操作都是针对于DataSet进行的操作,而前面通过使用分层结构已将程序设计为使用自定义领域实体来表示数据,这样就有一个非常麻烦的问题,因为WinForm的数据邦定邦定到List之后直接就修改好其中对象的值,这样如果需要设置回到原来的实体值就需要重新向数据库中获取,同时那个BindingSource用起来也很不方便,连一些很简单的功能实现起来都很麻烦。感觉WinForm的设计师们对于易用性的设计没有ASP.NET之中那么简单,如没有像ObjectDataSource那样的对象。现在正在迷茫怎么样自己的开发工作可以变得很简单呢。烦啊。

posted @ 2008-09-02 14:44 蓝色游骑兵 阅读(94) | 评论 (0)编辑

2008年7月21日

MSCRM 的感受

这几天看了一下MS的CRM产品,感觉其中使用到的开发架构和开发技术都是非常不错,其中的很多想法非常的有创造性的想法,很多想法我以前从来没有听说过,像什么自定义实体、如何将这些实体与业务插件结合起来、如何与视图、工作流等集成起来。感觉想法非常先进。

但我发现其中只是定义了一个实体的一组标准的操作,目前我还没有看到添加实体的扩展自定义操作,只看到了标准的创建、更新、删除和选取等操作。这样的操作如果在实现基本的业务上可能能够满足要求,但如果需要开发一个有很多自定义业务操作的实体,我想这种操作能否满足要求。

同时其中的插件在这里我想非常的不错,相当于一个职责链和观查者模式的实现,当进行某一操作时,根据对实体和操作的插件进行调用可以触发更多的事件的执行,这相当于将整个业务逻辑代码的AOP实现,这一点非常值得学习。相当于整个实体的业务操作是配置出来的,我想可能在设计时是按以下方式来设计的:

    实现一个业务实体操作的基本插件,用于实现数据库操作和工作流的集成和其它的一些基本的功能。

    再针对于特定实体业务逻辑编写若干的插件,实现特定的业务。

    调用过程中根据注册插件的顺序先执行自定义插件,再调用基本插件完成整个操作。

    我想这一过程肯定是一个有点慢的过程,但如果能够通过相关的信息,将这一组操作通过Emit之类的技术实现为一个动态创建的类,再来调用这个类,我想就能够获取的灵活的业务它高效的运行效率。

同时我想MSCRM解决了开发中的很多个问题包括:业务实体的属性变化问题、业务逻辑它业务流程的变化问题、视图的相关问题。这些问题的变化我们通常都需要通过二次开发来解决,很可能需要修改整个项目来解决,但MSCRM在这里解决得很好,是应当学习和应用的。

但同时我想如果将这个架构和来开发大型的像ERP那样的项目是否可很,特别是增量的开发。先根据需要开发不同的应用程序,后根据不同企业的不同需要布署不同的应用,如:

CRM:客户关系管理

PM(Product Management):产品管理

SM (Sale Management):销售管理

如这三个产品是公司目前的产品,针对于三种不同的用户,

其中的CRM与PM、SM之中都包括有Product,但其中的CRM之中包括有最简单的Product而,PM之中包括有产品最详细的技术资料,那么,当同时为同一家公司部署这三个产品之时,对于这个Product怎么样解决呢,能否相互使用相同的Product呢。或许还需要进行更多的思考。

posted @ 2008-07-21 13:37 蓝色游骑兵 阅读(18) | 评论 (0)编辑

2008年6月12日

测试驱动开发

在这次开发安全组件的过程中,全程使用的测试驱动开发,相比经往的测试驱动开发,虽然都是同样先写测试后写代码,但感觉以前只是形似而神不似。通过这次开发算是真正的有了一个好的开端吧。先将自己对测试驱动开发的感想写这样里方便以下自己查阅。使用了测试驱动开发后我感觉到的好处是:
1、代码的分散实现。以前可能需要实现很多的函数和很多的类后,再通过使用功能来检查其中代码是否运行正常,这就让我必须同时关注很多的内容,而通过测试驱动开发可以让我同一时间只关注一个类的实现,不必须关心其它的实现,这一点真是太爽了。上周四我实现一个ActivityPermission的类,需要支持IPermission接口,通过一步一步的测试和实现让我在下班之前就完成了这个类,并且我很放心这个类会正常的工作,而不必等到使用时。
2、便于得到简单的设计:我感觉自己在没有开发程序之前会对这个程序想很多种情况,让自己头都想痛了。会设计很多接口来表现出自己对设计很在行,水平很高。但通过测试驱动开发让我只面要解决当前的问题,前方发现一个山头,冲过去。再发现一个再冲,而不必在没有发现一个同头时为自己设计10个山头。
3、测试便于重构:不必多说,重构是测试的一个过程。
4、测试便于交流:测试中的代码可能是自我说明性最强的代码,如果我要测试A?? > Abc 我就会直接写一个测试方法名字就中 测试A两个问号大于Abc,而其中的第一个类的变量名就叫做:A两个问号再设置相应的值ABC相同来进行比较。简直就是代码版的API帮助文件。
5、测试有利于设计:当我在进行一个设计时最初的想法很可能是朦胧的,但通过测试不断的修正我的想法不断的强化各种概念,等所有功能都完成了,再来检查代码看那里不顺眼,再修改它而不必担心会破坏代码。
目前只感受到这么多。

而对于什么部分进行测试我认为有以下方面:
测试接口的契约:如在编写安全组件时我定义了一个用于进行授权检查的

IAuthorizationProvider.Authorizate(IIdentity identnty,string[] activityNames);

接口方法,其中我定义了一个契约当identity是null时将引发ArgumentNulLException异常。这里我对于这个方法的第一个测试就是测试这个参数不能为空。

测试心中的不快:有很多代码在进行设计的时候就在想怎么来进行操作,而编写完成后又感觉到一些方法可能会有问题,这时候心中就会有一种不放心,让自己担心掉胆,长此以会定会对身体产生不适。那么现在行动起来让编写一个测试让自己放心,解决自己的身体。

测试对代码的假设:这种测试可能会只有一个测试也可能会有10个测试,在编写GenericValueToken时为了相互的比较性我测试了:ab + a? = a? a? + a* = a,a?? + a? = a??等5种以上各种情况的测试,对每一种结果都编写一个测试。

不要因为实现简单就不进行测试:不能因为认为简单就不进行测试,因为很多情况下一组简单的测试形成一个测试组,这一组测试很有可能是在不同层面上来验证假设。如果不进行测试那么这就很有可能会产生问题。

如果使用三角法进行测试:三角法测试看起来是一种比软SB的一种测试方法,将同一代码用不同的数据执行两次。但三角法也有很重要的作用,特别是在进行简单问题的测试时,如果只进行一次可以直接返回一个常量值可能就满足要求了,但如果使用三角法就不能这样做了。
所以我认为三角法主要用于检查简单功能的测试,因为针对于一个复杂的问题可能会编写多个测试从多个方面来进行验证,而进行测试简单的功能通过只需要一个测试。

一定要失败/成功/重构的方式来进行整个开发过程,在实际的过程中我发现这样做的好处有以下作用:
一:测试测试代码自身的有效性:在开发中我有一个问题是测试身份验证时密码不正确时返回空,我发现当我直接返回一个null值时,我怎么也不能将这个测试红起来。这就说明这个测试可能自己就有问题,应当合并到密码正确返回有值,而错误返回空值。
二:减少不必须要代码:如在进行契约的验证时当参数为null时引发ArgumentNullException,但通过测试发现其中根本就不必写这个代码,因为可能实际上一个信赖的方法进行了这种检查(不知道这样好不好)。

虽然测试驱动开发有很多好处,但在实际中,当我正编写问题的测试过程中,我发现我完全忘记了整个的设计,而我又是不喜欢写文档的。完成一个类的开发后,我总需要思考一下自己的以前的设计(不知道这是好是坏)。

posted @ 2008-06-12 16:40 蓝色游骑兵 阅读(51) | 评论 (0)编辑

异常使用的思考

经过看一个月以来的设计和开发,对于安全组件的设计也基本完成了,就是还需要进行重构和一个XML实现的完成, 在这其中我深深的感觉到异常的使用上的复杂性.C#有一点不怎么好就是不能像JAVA一样申明可能会产生的异常,当代码一级一级的产生信赖后,其到后来执行一个方法会产生什么样的异常我想可能自己都搞不明白了,就更别说使用的人了。
所以我想在实际的使用过程中应当尽量的少定义异常,如.NET的SqlExcetpion,当产生所有与SqlClient相关的异常时都引发的是这个异常。其它的产生的都是常见的异常如:ArgumentNullException,ArgumentException等异常。像这种异常基本上都是用来进行参数检查的。

posted @ 2008-06-12 15:16 蓝色游骑兵 阅读(37) | 评论 (0)编辑

2008年6月3日

读UML精髓第三版之感受

这两天通过当当买了两本书,本想好好学习.先就看了《UML精粹》本想终于可以让搞明白UML了,虽然UML经常使用,但对有两种图还搞不怎么明白。结果看了一百页不到,终于决定让自己心情好点再看吧。
对其中的翻译只能用无语来表示,连人们评为翻译很差的《ASP.NET内幕》我都感觉没有这种感觉,一种想骂人的感觉。啊,从来看书我就没有想过去看一下译者,但这次我看了。啊,经过如此多的审核我发现,其中的概念没有一个翻译是正确的,用例、用况我见多了,从来还没有听说过案况的。其中还有部分使用了什么什么之的,感觉回到的民国。
啊,收拾一下心情,等我心情好点后再看吧,我想能将如此之差翻译的书能够看完本身就是一种巨大的进步吧。

posted @ 2008-06-03 22:33 蓝色游骑兵 阅读(79) | 评论 (1)编辑

使用静态构造函数与使用单件

今天需要编写一个AuthenticationProviderFactory的工厂类,需要向外部提供注册、注消和获取IAuthenticationProvider的服务,并且在构造时能够将配置中的信息获取并设置一些相应的值。
这里我想了很长时间是使用单件还是使用静态方式来提供这三个功能。虽然使用单件与使用静态在原则上很好区分(如果将这个类是工具集则使用静态否,如果表示的是一种概念使用单件),但具体做起来就不那么好做了。
所以我就想了一下使用那一种方式使用和维护起来更加方便,使用静态方式使用起来肯定要方便很多,但先需要获取配置信息所以就必须编写一个静态构造器来设置配置的值。而如果使用静态构造来初始化则当配置信息出错时就会引发调用失败,并且这种失败不会显示出明确的信息,同时还会使用整个应用程序崩溃。而如果使用单件则当调用这个单件的过程中调用初始化配置,如果配置错误则会在这里引发,如果不调用本方法则程序不会出现问题,并且其它部分还可以正常运行这对程序的容错性有很大帮助。
所以最后我还是决定采用单件来处理这个问题,并且也让我想到在具体的使用过程中

尽量不要使用静态构造器,如果必须则必须要保证静态构造器不会产生异常才行啊。

posted @ 2008-06-03 10:52 蓝色游骑兵 阅读(29) | 评论 (0)编辑