定义操作授权也就是为每一个操作授权指定一个名称及一个唯一标识,当然也可以再加上一段对这个操作授权进行简短描述的文字。定义操作授权的目的有以下几个:
1、将操作授权的唯一标识与用户组关联,实现将操作授权给用户组;
2、根据操作授权的唯一标识进行授权检查;
3、用户可根据操作授权的名称和描述文字对操作进行直观的操作授权管理。
在基于“操作”的用户授权(一):基本分析一文中,我提到“操作”通常对应于程序中的一个函数,因此对操作的授权实际上也就是对函数调用的授权;定义操作授权实际上也就是将函数名与操作的唯一标识相关联,这可以通过两种方法来实现:动态定义操作授权和静态定义操作授权。
动态定义操作授权
也可以称为“运行时(RUN-TIME)定义操作授权”。具体的方法是利用System.Reflection.Assembly类列举出当前应用程序所用到的所有函数,然后由管理员对需要设定权限的函数定义操作授权,为该函数指定一个操作名称、一个唯一标识及一段描述文字。这种方法的好处是无需在程序设计时添加额外的代码,所有的操作授权定义都是在程序运行时由管理员完成的。缺点是权限检查只能在函数之外进行,也就是说不能实现代码级别的操作权限检查;另外对于一个中型或者大型的网站应用程序来说,往往涉及到上百个函数,其中的大部分没有必要设定权限,管理员需要对整个程序的函数都比较了解才能规划出一个较好的授权系统,这对于商业应用软件来说是不现实的。在对这种方法进行了一番实验之后,我最终还是决定放弃了。
使用System.Reflection.Assembly类从DLL文件中获取函数名的方法如下:
string path = HttpContext.Current.Server.MapPath("~/Bin");
string[] files = Directory.GetFiles(path, "*.DLL");
foreach (string file in files)
{
System.Reflection.Assembly asm = System.Reflection.Assembly.LoadFile(file);
Type[] types = asm.GetTypes();
foreach (Type type in types)
{
MethodInfo[] infos = type.GetMethods();
foreach (MethodInfo info in infos)
{
string methodName=info.Name;
......
}
}
}
静态定义操作授权
也可以称为“在代码级别定义操作授权”。基本原理是在程序设计时,为需要定义操作的函数设置一个继承自System.Attribute的自定义函数属性。使用这种方法,程序员可以在程序设计时,在函数中添加相关代码对操作权限进行检查。这种方法的的最大好处是程序员可以根据系统需要,仅对需要进行授权的函数定义操作授权;另外由于权限检查是在代码级别进行的,所以外部程序不必过多考虑权限检查的问题。缺点是代码只有在支持操作授权的系统框架内才能运行,否则可能会导致函数无法被调用,当然也可以通过一些方法保证函数可以在不支持操作授权的环境里运行,不过我并没有这样做。
System.Attribute类有两个重要的特性:
1、可继承性
就是说如果为父类的某个函数设置了一个自定义属性,则子类的重载函数同样也继承了这个自定义属性,因此我只需要在基类中定义操作授权就可以了。
2、多属性支持
即可以为同一个函数设置多个自定义属性,这意味着我可以为同一个函数定义多个操作授权,不过仔细分析后我认为应当保持操作授权的原子性,即一个函数只能对应于一个操作授权,这样可以简化编程代码,提高权限管理和权限检查的效率。
下面是实现操作授权的类OperationAuthorization,这个类继承自System.Attribute,我使用了一个GUID来唯一标识一个操作,并将AllowMultiple属性设置为false。
using System;
namespace Tiray.Security
{
[AttributeUsage(AttributeTargets.Method , AllowMultiple = false)]
public class OperationAuthorization:System.Attribute
{
private Guid id;
private string name;
private string description;
public OperationAuthorization(string guid, string name,string description)
{
this.id = new Guid(guid);
this.name = name;
this.description = description;
}
public Guid ID
{
get { return id; }
}
public string Name
{
get { return name; }
}
public string Description
{
get { return description; }
}
}
下面的代码来自对操做授权进行数据库操作的数据提供器基类,可以看到我只对其中的两个函数定义了操作权限。
namespace Tiray.Security.Providers
{
public abstract class OperationAuthorizationProvider:ProviderBase
{
[OperationAuthorization("A355AF68-C562-47fb-A220-732E398B4369", "AddAuthorization", "Add Authorization")]
public abstract void AddAuthorization(OperationAuthorization authorization);
[OperationAuthorization("5590B290-8514-4d28-8379-DB359A860723", "SetRoleAuthorizations", "Set a role's operation authorizations")]
public abstract void SetRoleAuthorizations(string roleName, OperationAuthorization[] authorizations);
public abstract OperationAuthorization[] GetRoleAuthorization(string roleName);
public abstract OperationAuthorization[] GetUserAuthorization(string userName);
}
}
操作权限的数据存储
在基于“操作”的用户授权(一):基本分析一文中,我通过分析得出主体(也就是用户组)与操作之间是一个“多对多”的二元关系,即一个用户组可以拥有多个操作权限,一个操作权限可以被指派给多个用户组,因此在数据存储时,必须添加一张只包含操作授权ID和用户组ID的过渡表,以便将这个“多对多”关系转变为两个“一对多”关系,这是关系数据库中处理“多对多”关系的标准方法,就不多说了。下面是用户组与操作权限的数据库关系图。
在这篇文章中,我介绍了如何定义操作授权的方法,以及如何将操作权限保存在数据库中。接下来,我将介绍如何如何扩展ASP.NET安全性框架以支持操作授权。
参考:
基于“操作”的用户授权(一):基本分析
基于“操作”的用户授权(三):扩展ASP.NET安全性
基于“操作”的用户授权(四):授权检索与验证