从kbmMW v.4.40开始,引入了一个新的非常灵活的授权管理器。
它的目的是为开发人员提供为用户定义资源权限的功能,这是一个可选功能,将现有的授权事件驱动方案内置到kbmMW中,使授权开发任务更容易实现。
据说一张图片值1000字......让我们看看是不是这样:
上述图显示了一个典型的用户如何与应用服务器(绿色的东西)内的功能相互作用。 此外,它还显示了新的身份验证管理器如何添加到应用程序服务器,以及它如何在内部运行。
基于这张图,我还是要用1000多的文字来解释这是怎么回事。
首先:什么是你感兴趣的呢?
如果您计划或已经为一个或多个客户端提供本地集中功能,并且所提供的功能应取决于客户端的确切位置以及客户端的行为方式,那么您可能会对此感兴趣。
这是安全性的问题。 安全性通常作为对客户端中的信任方案实现,或者作为数据库中的强制功能实现。
对客户信任为基础的安全方案基本是一致的,因为没有安全性可言,不需要太多幻想,客户端软件(至少部分地)就可以轻易被一个不那么好的行为客户端替换,造成的结果也可想而知,系统由此变的不安全,数据被盗窃或者被破坏。客户端应用程序必须表现良好以确保您的数据不受错误影响这是作为开发者的责任,此外,系统还很难添加另一种客户端应用程序(例如移动应用程序),保证他安全地访问和操作数据库中的数据,添加的客户端需要实现与第一种客户端相同的安全层,这意味着代码需要重用,不要大量复制/粘贴相同的代码,最终可能导致出安全漏洞。
数据库强制安全性要好得多,因为实际上有一个活动的中间层负责决定客户端可以做什么而不用处理数据库中的数据。 但是,如果您执行的操作不仅仅是从数据库访问相当简单的数据结构,那么如果数据库实际上包含自己的编程语言,您将不得不求助于特定于数据库的编程语言,如TransactSQL,PL / SQL等,或者 您将把功能移动到客户端,在上面的段落中指出这是一个次优的解决方案。
更好的解决方案是使用Delphi或C ++作为编程语言,将所有开发人员/业务定义的代码(如报告生成,数据导出/导入,计算等)放在开发人员控制器应用程序服务器上。换句话说,做一个三层的解决方案:客户端,应用服务器,数据库服务器。 这为您提供了最高的灵活性,更轻松地支持其他客户端类型,同时仍然维护一个可以强制执行安全措施的层。
kbmMW总是有事件和功能来处理请求的授权,但开发人员有责任编写实际代码来执行繁琐任务。
现在,随着kbmMW v4.40发布,提供了一个名为TkbmMWAuthorizationManager新组件。
还有之后的安全保护功能,可以定义多种“模式”。 我们选择使用一个非常灵活的,基于下面三个定义:
- Actors
- Roles
- Resources
一个Actor通常是同一个用户,具有一个登录名和口令,以及默认的角色(role)。 Actor也不一定为现实中的用户,它也可以由应用程序本身使用,例如允许应用程序在特殊情况下充当特定的Actor。
一个Actor可以有不同的“帽子”..基于系统中不同的角色(role)运行。
Resource是基本的东西,你作为开发商,希望把某种需要保护的东西命名。 例如,它可以是特定的kbmMW服务,或kbmMW服务中的特定功能,但由于开发人员100%负责选择资源的命名方案,开发人员可以想到的任何东西都可以成为资源(resource)。
基于这三个定义,开发人员就可以定义授权了。 授权指属于特定角色(role)的用户(actor)是否可以访问特定资源。
此外,授权可能仅在特定时间段内有效(例如上午9点至下午3点,或仅周三,或仅4月1日等),并且可能仅在请求来自特定物理客户端时才有效。 在kbmMW中,我们称之为约束的限制。授权可标记有零个或多个约束。
我们已经谈了很多关于actor。 但在现实世界中,我们拥有用户,这些用户需要进行身份验证并被识别为已定义的角色之一。 为此,我们在kbmMW的授权系统中有最终的子组件:登录。
一个登录是验证已知列表中的actor的用户名/密码。 如果用户名/密码通过验证,则会根据任何已定义的登录约束做进一步检查,登录约束定义了基于例如时间或客户端位置等可能不允许登录的特殊情况。
现在,我们为演示应用服务器增加安全功能。
如上图,拖放一个TkbmMWAuthorizationManager到Form上,然后设置kbmMWServer的属性AuthorizationManager指向刚拖放的组件。现在,kbmMWServer就知道在适当的时候去请求他的授权管理器为他处理授权了。
什么是适当的时候? 当未登录到系统的用户尝试访问任何内容时,或者当已登录的用户尝试访问服务标志表明需要授权运行的服务时。 这可以在通过服务向导创建kbmMW服务时定义,但也可以在之后定义。
让我们看一个演示的查询服务(Query Service),即从SQLite数据库向客户端返回数据的查询服务。
unit Unit2; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, kbmMWServer, kbmMWQueryService, Db, kbmMemTable, kbmMWCustomConnectionPool, kbmMWCustomDataset, kbmMemCSVStreamFormat, kbmMWSecurity, kbmMWBinaryStreamFormat, kbmMWStreamFormat, kbmMWResolvers, kbmMWSQLite, kbmMWServiceUtils; type TTestQuery = class(TkbmMWQueryService) kbmMWBinaryStreamFormat1: TkbmMWBinaryStreamFormat; ClientSideQuery: TkbmMWSQLiteQuery; ALL_EVENTS: TkbmMWSQLiteQuery; SELECTED_EVENT: TkbmMWSQLiteQuery; kbmMWSQLiteResolver1: TkbmMWSQLiteResolver; SELECTED_SPECIES: TkbmMWSQLiteQuery; ALL_SPECIES: TkbmMWSQLiteQuery; private { Private declarations } public { Public declarations } class function GetFlags:TkbmMWServiceFlags; override; end; var TestQuery: TTestQuery; implementation uses Unit1; {$R *.DFM} class function TTestQuery.GetFlags:TkbmMWServiceFlags; begin Result:=[mwsfListed,mwsfRunRequireAuth]; end; end.
没有太多的代码,除GetFlags类函数返回一组服务标志。 通过确保mwsfRunRequireAuth包含在服务标志集中,从而告诉kbmMWServer只允许对此查询服务中的所有功能都要经过授权才可以调用。
现在,需要决定我们的授权方案应该如何定义了。
可以直接在应用服务器中定义Actor,也可以在请求授权时通过从外部数据源(例如在数据库中定义的用户)来动态定义。
角色通常直接在应用服务器中定义,资源和授权也是如此,但角色实际上也可以通过XML文件来保存及调入所有内容,从而实现在外部进行动态定义。
为简单起见,我们选择此应用程序服务器已知Gertrude和Franz作为actor,Administrator和User作为两个角色。并在MainForm的OnCreate事件中定义他们,当然也可以在其他地方定义,但必须遵循,在定义前不能执行授权给任何人。
procedure TForm1.FormCreate(Sender: TObject); var sd:TkbmMWCustomServiceDefinition; begin sd:=kbmMWServer1.RegisterServiceByName('KBMMW_QUERY',TTestQuery,false);
kbmMWAuthorizationManager1.AddRole('Administrator',0);
kbmMWAuthorizationManager1.AddRole('User',0);
kbmMWAuthorizationManager1.AddActor('Gertrude','GertrudesPassword','User');
kbmMWAuthorizationManager1.AddActor('Franz','FranzPassword','Administrator');
end;
加入两个角色(Role),两个演员(Actor)。每个演员(Actor)的定义有一个默认角色(Role)。 任何actor都可以通过除默认角色之外的其他角色请求访问。 如果允许actor通过该角色访问资源,则仅取决于我们需要很快定义的授权。
接下来,需要弄清楚我们的应用服务器哪些资源需要保护,虽然已经定义了TTestQuery服务上的功能需要授权,但这并不具体。
如果我们让kbmMW完全自动地处理授权,kbmMW定义要由包含所请求的服务名称,点和所请求的函数名称的字符串标识的资源,如:“KBMMW_QUERY.QUERY”。
在我们的示例中,通过RegisterServiceByName将查询服务注册为“KBMMW_QUERY”,客户端请求查询服务时将使用该标识,并且客户端可以使用查询服务支持的功能,即“QUERY”,“DEFINITIONS”,“EXECUTE”,“RESOLVE”,“INVENTORY”或“METADATA”这些功能。
这意味着当客户端打开查询时,至少会在服务上执行“QUERY”功能。
如果您使用自己的自定义服务,则可能在服务端定义了大量自定义函数,而这些函数将是你的客户端要使用的函数。
在默认授权模式下,构造的资源名称为“KBMMW_QUERY.QUERY”,“KBMMW_QUERY.DEFINITIONS”,“KBMMW_QUERY.EXECUTE”等。
资源通过授权管理器的方法AddResource定义,必须提供资源名称和可选的父资源引用。我们可以使用定义资源树的功能,以便在我们为用户定义授权时最大限度地减少代码量。
var adminResources,userResources:TkbmMWAuthorizationResource; … adminResources:=kbmMWAuthorizationManager1.AddResource('AdminResources',nil);
userResources:=kbmMWAuthorizationManager1.AddResource('UserResources',adminResources);
kbmMWAuthorizationManager1.AddResource('KBMMW_QUERY.QUERY',userResources);
kbmMWAuthorizationManager1.AddResource('KBMMW_QUERY.DEFINITIONS',userResources);
kbmMWAuthorizationManager1.AddResource('KBMMW_QUERY.EXECUTE',userResources);
kbmMWAuthorizationManager1.AddResource('KBMMW_QUERY.INVENTORY',userResources);
kbmMWAuthorizationManager1.AddResource('KBMMW_QUERY.METADATA',userResources);
kbmMWAuthorizationManager1.AddResource('KBMMW_QUERY.RESOLVE',adminResources);
我们已经定义了两个虚拟资源。一个名为AdminResources,没有父资源,另一个名为UserResources,其AdminResources为父资源。 换句话说,UserResources是AdminResources的子集,因此允许访问AdminResources的actor/role也可以访问UserResources。
现在用下面的代码定义授权:
kbmMWAuthorizationManager1.Grant('','User','UserResources',[mwapExecute]);
kbmMWAuthorizationManager1.Grant('','Administrator','AdminResources',[mwapExecute]);
第一个语句授予任何扮演'User'角色的actor并在'UserResources'组中请求资源的授权,具有mwapExecute(执行请求)的权限。
由于可以明确的指定actor名称,而不是空字符串,因此您可以根据需要为特定人员制作非常精确的授权。 但是,在正常情况下,仅仅授权角色资源就更清晰了。
除了Grant之外,您还可以使用与上面相同的语法,仅使用Deny方法拒绝授权。 但是,它应该很少需要,如果是这样,它可以表明您应该检查资源树和角色定义,看看它们是否可以以不同的方式排列,以更好地满足您的授权需求。
如果任何客户端尝试连接到应用程序服务器并从查询服务请求数据,则将始终拒绝访问它们。 为什么?我们还是很怀念登录客户端。
下图为登录流程:
最简单的方法是让kbmMW应用服务器完全自动处理的登录流程。 我们可以通过将Options属性设置为包含mwaoAutoLogin来实现。
接下来的事情就是让客户端提供相关的用户名和密码。
这有多种方法,其中之一是使用TkbmMWSimpleClient组件,通过他的实例提供用户名与密码,然后所有的TkbmMWClientQuery组件通过Client属性使用TkbmMWSimpleClient组件实例,从而取代TkbmMWClientQuery内部使用的Client实例。
如果客户端在多线程中使用TkbmMWClientQuery,更好的方法是将TkbmMWClientQuery.ClientAsTemplate设置为true,并确保查询动作发生前先通过SimpleClient执行一次请求(它可能是一个虚拟的“LOGON”服务器端功能)。 该函数不必执行任何操作,用它确保在查询开始之前触发kbmMW登录过程,从服务器取得登录标记。
本文只涉及了什么可以与授权管理器来完成授权的基本概念,还没有讨论如何在实际数据表上添加更细粒度的授权,如何在不使用后登录超时,如何保存和加载XML的完整授权设置,如何将约束添加到登录和授权,如何允许匿名/未知用户访问特定功能,如何基于外部资源(例如数据库)动态定义角色等。
但是这一切都支持用新的授权管理机制来实现,至少我是希望能够更好地了解你的需求。