应用程序该如何拥抱变化

拥抱变化是极限编程非常重要的一个理念,OOP原则中的OCP原则(Open Close Principle,开放原则)也是拥抱变化的体现。不过,在写程序过程中,我们总是会遇到各种各样的变化,我们也尝过了“变化”的各种苦头,甚至都厌倦了再去迎接任何的变化,这并不是因为原则错误,而是我们一开始就没有准备好“变化”。这个文章对“拥抱变化”不再累述,我们直接来看一个使用面向服务架构的应用程序,体会一下拥抱变化。

 

我先说一下这个实例的内容,它非常的常见,就是设计一个权限控制服务,基本在我们的每一个业务系统中存在。不过,我有必要描述一下这个实例的背景,从而展现出为什么我需要做好变化的准备。目前我们有一个“插件工厂”,在这个插件工厂里面,我们希望向软件开发人员提供免费的界面插件、服务插件和业务插件,这样开发人员可以使用现有的插件来轻松/高效的构建自己需要的应用系统。换句话说,插件工厂的目标就是使用大众的力量帮助我们构建积木块的应用程序,插件工厂本身是一个最大的积木块仓库。

 

在插件工厂中,服务插件包括最基本的权限控制服务。这个权限控制服务向开发者提供非常简单易用的权限控制服务,并且开发者可以选择不同的权限控制服务提供商来构建自己的应用系统。打个比方,如果开发者构建的应用系统非常简单,他可以从插件工厂下载权限服务插件和一个“基于用户—权限的”权限控制服务提供商,它允许直接对用户授权;如果开发者构建的应用系统比较复杂,那么他可以下载权限服务插件和一个“基于角色的访问控制”权限控制服务;用户还可以利用现有的MemberShip实现自定义的权限控制服务。也就是说,开发者可以根据需要获取权限服务及相应的服务提供商来满足需求。经过思考,我在白板上画出了这个服务的实现原型。
应用程序该如何拥抱变化

 

在这个原型上,“Business Addin”即业务插件,是开发者需要设计的业务模块,这个业务模块将使用权限控制服务来实现权限的控制;权限服务分为契约和不同的提供商两部分,服务提供商对于业务插件来说是透明的,这些服务提供商可能是:(1)简单的权限控制服务提供商;(2)基于MemberShip的权限控制服务提供商;(3)基于角色的访问控制权限服务提供商;(4)其它服务提供商。不管开发者从插件工厂获得了哪个服务提供商,他都能够使用一致的模型来使用权限控制服务,并且这些权限控制服务能够与.NET现有的安全机制兼容。“Business Addin”使用权限服务的场景如下,我们希望尽可能使权限控制服务与开发者无关并且简单可重用。 

应用程序该如何拥抱变化 

 应用程序该如何拥抱变化

 

 “Business Addin”业务插件使用一般场景如下:

(1)首先通过[assembly: AddinPermission(“PermissionId”, “Name”)]来定义这个业务插件中需要检查的权限;
(2)然后在需要做权限检查的代码中调用权限服务的Demand方法实现检查。
应用程序该如何拥抱变化
if(PermissionService.Demand(“PermissonId”))
{
    
// 当前用户拥有Id为“PermissionId”的权限,因此允许执行以下操作
}
else
{
    
// 向用户提示,您没有此操作的权限,请联系管理员给予分配“PermissionName”权限
}
应用程序该如何拥抱变化
 

这样对于开发者的好处是显而易见的:(1)开发者也开发业务系统过程中不再需要设计任何的用户-角色-权限管理的应用模块;(2)开发者可以根据需要任意定义/扩展业务插件所需的权限,这些权限的管理最终由插件工厂里的权限服务提供商来实现。

那下面我们来看一下怎么来设计这个能够拥抱各种变化的权限服务。这个设计方案如下:
应用程序该如何拥抱变化
(1) 业务插件通过AddinPermission特性来定义一个权限,定义的权限会注册到权限服务的注册表中;
(2) 当业务插件请求一个权限时,它调用IPermissionService.Demand(“PermissionId”)来判断当前用户是否拥有该权限;
(3) 业务插件不依赖于权限服务提供商;
(4) 权限服务提供商在实现权限服务时使用权限服务的权限注册表来获取业务插件注册的权限实例,然后调用该实例来检查权限;
(5) 权限服务提供商默认权限的权限检查依赖于权限服务当前的用户/角色信息,然后对当前用户信息是否拥有该权限做出判断;
(6) 权限服务基于.NET的安全标识、实体、权限来设计,能够兼容.NET的安全机制。

 下面我们看一下相关的项目及服务定义。

应用程序该如何拥抱变化 

运行效果图如下。在这里,我们为应用系统安装了SimplePermissionService,那么IPermissionService的提供商就是SimplePermissionService。如果用户需要其它的实现,比如ASP.NET MemberShipe实现的权限控制,就可以下载相应的插件来提供权限服务,但这对业务插件来说是透明的,它不需要做任何的代码变更。 

应用程序该如何拥抱变化

 权限服务定义如下。

应用程序该如何拥抱变化
namespace PermissionService
{
    
/// <summary>
    
/// 权限服务契约,需要兼容.NET安全机制,即兼容IIdentity/IPrincipal/IPermission模型。
    
/// </summary>
    public interface IPermissionService
    {
        
/// <summary>
        
/// 默认权限类型。如果用户使用AddinPermission没有指定权限类型,则使用该权限。
        
/// </summary>
        Type DefaultPermissionType { get; }
        
/// <summary>
        
/// 当前安全实体。由用户标识和角色集合组成。
        
/// </summary>
        IPrincipal User { getset; }
        
/// <summary>
        
/// 创建默认的权限实例。
        
/// </summary>
        
/// <returns>每次返回一个新的权限实例。</returns>
        IPermission CreateDefaultPermission();
        
// 判断当前用户是否拥有指定的权限。
        bool Demand(string permissionId);
        bool Demand(string addinId, string permissionId);
        bool Demand(Addin addin, string permissionId);
        bool Demand(RuntimeAddin addin, string permissionId);
    }

应用程序该如何拥抱变化
在业务插件中使用权限服务,这里它没有对实际的权限服务提供商产生任何的依赖,只是通过SOA中的服务注册表来绑定一个IPermissionService服务,然后使用它来验证权限。
(1)权限定义
[assembly: AddinPermission("MyPermissionId""My Permission")]
[assembly: AddinPermission(
"Guest""Guest Permission"typeof(CustomizedPermission))]

(2)获取权限服务并请求验证权限 

应用程序该如何拥抱变化
IPermissionService permissionService = 
    Context.GetFirstOrDefaultService
<IPermissionService>();
if (permissionService != null)
{
    
if (permissionService.Demand("MyPermissionId"))
    {
        Response.Write(
"Permission demanded successfully. <br />");
    }
    
else
    {
        Response.Write(
"Permission demanded failed. <br />");
    }
}
else
{
    Response.Write(
"Please install a Permission Service Provider first.");

应用程序该如何拥抱变化

 

这个拥抱变化的思想是基于面向服务架构思想来实现的,在这里,“服务=契约 + 实现,契约=面向对象中的接口,实现=实现接口的类型”。业务插件依赖的是服务的契约,并不依赖服务的实现,服务的实现针对契约的不同提供商。我们可以根据应用系统的需要,在不更改业务插件的情况下,变更服务提供商来满足实际系统的需求。

 

关于权限服务提供商的实现会稍微复杂一点点,一个典型的提供商一般包括:(1)相关实体管理界面,如用户/部分/角色/权限分配;(2)权限服务实现。以下是一个典型的权限管理界面了。

应用程序该如何拥抱变化

在这里,权限服务提供商实现时,一般需要实现:(1)定义一个默认的权限;(2)实现IPermissionService;(3)在登录时设置IPermissionService.User。

应用程序该如何拥抱变化 

应用程序该如何拥抱变化 

示例服务的实现如下。

应用程序该如何拥抱变化
namespace SimplePermissionService
{
    [Service(
typeof(IPermissionService))]  // 注册为IPermissionService服务契约的实现。
    
public class WebPermissionService : IPermissionService
    {
        
public Type DefaultPermissionType
        {
            
get { return typeof(DefaultPermission); }
        }

        
public IPermission CreateDefaultPermission()
        {
            
return new DefaultPermission();
        }

        
public IPrincipal User   // 绑定HttpContext.User安全主体。
        {
            
get
            {
                
if(HttpContext.Current != null)
                {
                    
return HttpContext.Current.User;
                }
                
return null;
            }
            
set
            {
                
if(HttpContext.Current != null)
                {
                    HttpContext.Current.User 
= value;
                }
            }
        }

        
// 判断当前用户是否拥有指定权限。其它重载方法省略。
        
public bool Demand( string addin, string permissionId)
        {
            IPermission permission 
= PermissionRegistry.GetPermission(
                addin, permissionId);
            
if (permission != null)
            {
                
try
                {
                    permission.Demand();
                    
return true;
                }
                
catch
                {
                    
// todo: Log here.
                }
            }
            
return false;
        }

        // 其它重载方法...... 

    }

应用程序该如何拥抱变化

上一篇:基于CentOS快速安装Apache服务


下一篇:经典网页设计:20个新鲜出炉的 HTML5 网站