在采用Windows认证的情况下,使用基于Windows用户组安全主体权限模式是一个不错的选择。我们可以直接使用现有的用户组设置,也可以为相应的应用或服务创建单独的用户组。但是,由于该模式对Windows认证的依赖,意味着这种模式只能使用于局域网环境中。如果采用证书和Windows帐号的映射,也可以适用于像B2B这样的外部网环境。在其他的网络环境中,基于Windows用户组的授权方式将会无能为力。此外,还具有这样一种状况:即使是在同一个局域网环境中,并且也采用Windows进行客户端认证,但是我们不想创建太多的Windows用户组,而是将用户的权限信息维护在相应的数据库中,通过单独的安全系统来维护。在这种情况下,基于ASP.NET角色管理模块的授权模式是一个不错的选择。
目录:
一、 ASP.NET Roles提供程序
二、 ASP.NET Roles授权与认证的无关性
三、 ASP.NET Roles授权 在ServiceAuthorizationBehavior中的设定
一、ASP.NET Roles提供程序
和Membership一样,Roles也是ASP.NET一个重要的提供程序,旨在解决对角色的维护和基于角色的授权。ASP.NET Roles同样采用策略设计模式,角色的添加、删除、获取以及授权功能定义在System.Web.Security.RoleProvider这个抽象类中。而ASP.NET默认提供了如下三个具体的RoleProvider,它们同时也体现了角色和授权信息的三种不同的存储形式。如果它们还不能满足你的具体授权要求,你还可以自定义RoleProvider。比如说,如果你使用的数据库是Oracle,你可以参考SqlRoleProvider自定义一个OracleRoleProvider。
- SqlRoleProvider:将角色和授权信息存储于SQL Server数据库预定义的表中;
- WindowsTokenRoleProvider:直接使用Windows用户组进行授权,这是一个只读的RoleProvider,角色(用户组)的添加和删除操作是不允许的;
- AuthorizationStoreRoleProvider:使用Authorization Manager(AzMan)库做作为角色存储。
ASP.NET Roles相关的功能基本上都可以通过调用Roles这个静态类的相应的方法来完成。下面的代码片断列出了Roles的主要方法。其中CreateRole和DeleteRole用于进行角色创建和删除;RoleExists用户确定指定的角色是否存在;而AddUser(s)ToRole(s)和RemoveUser(s)FromRole(s)则用以建立和解除用户和角色的关系。而IsUserInRole用以确定指定的用户具有相应的角色。
1: public static class Roles
2: {
3: //其他成员
4: public static void CreateRole(string roleName);
5: public static bool DeleteRole(string roleName);
6: public static bool DeleteRole(string roleName, bool throwOnPopulatedRole);
7: public static bool RoleExists(string roleName);
8:
9: public static void AddUsersToRole(string[] usernames, string roleName);
10: public static void AddUsersToRoles(string[] usernames, string[] roleNames);
11: public static void AddUserToRole(string username, string roleName);
12: public static void AddUserToRoles(string username, string[] roleNames);
13:
14: public static void RemoveUserFromRole(string username, string roleName);
15: public static void RemoveUserFromRoles(string username, string[] roleNames);
16: public static void RemoveUsersFromRole(string[] usernames, string roleName);
17: public static void RemoveUsersFromRoles(string[] usernames, string[] roleNames);
18:
19: public static bool IsUserInRole(string roleName);
20: public static bool IsUserInRole(string username, string roleName);
21: }
针对Roles的方法AddUser(s)ToRole(s),有一点值得一提:这四个方法并不会进行用户账号存在与否的验证。原因很简单,用户账号的管理属于Membership的范畴,而建立用户与角色的关系才是属于角色管理需要负责的。Membership和Roles对于ASP.NET是相互独立的两个提供程序,它们不具有任何依赖关系。你完全可以采用ActiveDirectoryMembershipProvider利用AD进行用户账号管理和认证,而采用将角色维护在基于SqlRoleProvider的SQL Server数据表中。在这着情况下,当我们调用Roles的AddUser(s)ToRole(s)方法的时候,指定的用户帐号在数据库中是不存在的。所以,Roles不会进行用户存在与否的验证,它只是负责将指定的用户名添加到相应的角色之中而以。Membership和Roles的这种独立性同样体现在WCF上。
二、ASP.NET Roles授权与认证的无关性
通过前面的介绍我们很清楚地知道了Windows用户组授权依赖于Windows认证,但是如果你采用了ASP.NET Roles安全主体权限模式,你可以采用任何非匿名客户端凭证和认证方式。也就是说,ASP.NET Roles模式真正体现了认证和授权的无关性。
在采用ASP.NET Roles安全主体权限模式下,最终创建并作为当前线程安全主体的是一个RoleProviderPrincipal对象。而该RoleProviderPrincipal的Identity与当前ServiceSecurityContext的PrimaryIdentity属性实际上是同一个对象。两者的统一性可以通过于如下的验证程序来体现。
1: IIdentity identity1 = Thread.CurrentPrincipal.Identity;
2: IIdentity identity2 = ServiceSecurityContext.Current.PrimaryIdentity;
3: Debug.Assert(object.ReferenceEquals(identity1,identity2));
原则上,只要通过本认证的用户名能够通过ASP.NET Roles正确获取到反映权限的角色列表,授权就能顺利进行。如果采用Windows认证(包括之前提到的三种情况),你需要针对Windows帐号(域名/用户名)进行角色分配。如果采用基于Membership和Custom的用户名/密码认证,则直接针对用户名角色的分配。如果采用证书凭证并不允许Windows帐号映射,那么被认证的用户名是证书主体名称和指纹的组合(<<主题名称>>; <<指纹>>),你需要以此进行权限(角色)的设置。
三、ASP.NET Roles授权 在ServiceAuthorizationBehavior中的设定
之前已经说过了,所有基于安全主体授权的编程都体现在ServiceAuthorizationBehavior这个服务行为上。如果要让WCF采用ASP.NET Roles进行授权,我们需要将ServiceAuthorizationBehavior的PrincipalPermissionMode属性设置成PrincipalPermissionMode.UseAspNetRoles,并为其RoleProvider属性指定一个具体的RoleProvider。至于RoleProvider的获取,你可以通过Roles的Provider得到默认的RoleProvider。此外,Roles还具有一个类似于字典类型的Providers属性返回所有配置的RoleProvider列表,你可以通过传入配置名称获取相应的RoleProvider。
1: public sealed class ServiceAuthorizationBehavior : IServiceBehavior
2: {
3: //其他成员
4: public PrincipalPermissionMode PrincipalPermissionMode { get; set; }
5: public RoleProvider RoleProvider { get; set; }
6: }
7: public static class Roles
8: {
9: //其他成员
10: public static RoleProvider Provider { get; }
11: public static RoleProviderCollection Providers { get; }
12: }
下面给出的是一段基于自我寄宿的代码。在开启ServiceHost之前,我们为服务指定了一个ServiceAuthorizationBehavior行为,并将其安全主体权限模式设置成PrincipalPermissionMode.UseAspNetRoles,该ServiceAuthorizationBehavior使用当前配置的默认RoleProvider。
1: using (ServiceHost host = new ServiceHost(typeof(CalculatorService)))
2: {
3: host.Authorization.PrincipalPermissionMode = PrincipalPermissionMode.UseAspNetRoles;
4: host.Authorization.RoleProvider = Roles.Provider
5: host.Open();
6: //...
7: }
我们还是一如既往地推荐采用配置的方式进行服务授权的设置。在下面这段配置中,我们在<system.web>/<roleManager>节点下配置了一个唯一的类型为SqlRoleProvider的RoleProvider。该SqlRoleProvider的配置名称为sqlRoleProvider,而目标数据库对应的连接字符串名称为aspNetDb。而ServiceAuthorizationBehavior配置在名称为aspNetRolesAuthorization的服务行为配置节中,反映其安全主体权限模式的principalPermissionMode属性被设置成UseAspNetRoles,而roleProviderName属性则正是配置的RoleProvider的名称。
1: <configuration>
2: <connectionStrings>
3: <add name="aspNetDb" connectionString="..." providerName="System.Data.SqlClient"/>
4: </connectionStrings>
5: <system.web>
6: <roleManager enabled="true" defaultProvider="SqlRoleProvider">
7: <providers>
8: <add name="sqlRoleProvider"
9: type="System.Web.Security.SqlRoleProvider, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
10: connectionStringName="aspNetDb" applicationName="AspRolesAuthorizationDemo"/>
11: </providers>
12: </roleManager>
13: </system.web>
14: <system.serviceModel>
15: <services>
16: <service name="Artech.WcfServices.Services.CalculatorService" behaviorConfiguration="aspNetRolesAuthorization">
17: <endpoint address="http://127.0.0.1/calculatorservice" binding="ws2007HttpBinding"
18: contract="Artech.WcfServices.Contracts.ICalculator"/>
19: </service>
20: </services>
21: <behaviors>
22: <serviceBehaviors>
23: <behavior name="aspNetRolesAuthorization">
24: <serviceAuthorization principalPermissionMode="UseAspNetRoles" roleProviderName="sqlRoleProvider"/>
25: </behavior>
26: </serviceBehaviors>
27: </behaviors>
28: </system.serviceModel>
29: </configuration>
为了让读者对基于ASP.ENT Roles授权方式有一个全面的认识,我们将在《下篇》做一个具体的实例演示,敬请期待。