权限验证是大多数项目开发中必不可少的一项,特别是大型项目其权限分得很细验证更为复杂。所以更好更便捷的方式来进行权限验证是一件很重要的事情,大多数的人都是在调用的方法前加了一个验证的语句,这样的写法虽然可以达到目的但并不够直观也不易阅读。下面介绍下怎么使用Attribute来达到权限验证的目的。
权限验证是大多数项目开发中必不可少的一项,特别是大型项目其权限分得很细验证更为复杂。所以更好更便捷的方式来进行权限验证是一件很重要的事情,大多数的人都是在调用的方法前加了一个验证的语句,这样的写法虽然可以达到目的但并不够直观也不易阅读。下面介绍下怎么使用Attribute来达到权限验证的目的。
首先介绍几个类:
1.ContextAccessPowerValidAttribute
ContextAccessPowerValidAttribute
[AttributeUsage(AttributeTargets.Class, AllowMultiple=false, Inherited=false)]
public class ContextAccessPowerValidAttribute : ContextAttribute
{
public ContextAccessPowerValidAttribute() : base("ContextAccessPowerValid") { }
/// <summary>
/// 将当前上下文属性添加到给定的消息。
/// </summary>
/// <param name="ctorMsg"></param>
public override void GetPropertiesForNewContext(IConstructionCallMessage ctorMsg)
{
//实例化一个ContextAccessPowerValidProperty 添加到上下文属性列表中
ctorMsg.ContextProperties.Add(new ContextAccessPowerValidProperty());
}
}
ContextAccessPowerValidAttribute类继承于ContextAttribute类,将ContextAccessPowerValidProperty 添加到上下文属性列表中
2.ContextAccessPowerValidProperty
ContextAccessPowerValidProperty
public class ContextAccessPowerValidProperty : IContextProperty, IContributeObjectSink
{
public ContextAccessPowerValidProperty()
{
}
#region IContextProperty 成员
public void Freeze(Context newContext)
{
}
public bool IsNewContextOK(Context newCtx)
{
return true;
}
public string Name
{
get { return "AccessPowerValid"; }
}
#endregion
#region IContributeObjectSink 成员
public IMessageSink GetObjectSink(MarshalByRefObject obj, IMessageSink nextSink)
{
return new ContextAccessPowerValidSink(nextSink);
}
#endregion
}
ContextAccessPowerValidProperty继承于IContextProperty、IContributeObjectSink,用于返回权限验证消息接收器的实例ContextAccessPowerValidSink
3.ContextAccessPowerValidSink
ContextAccessPowerValidSink
public class ContextAccessPowerValidSink : IMessageSink
{
private IMessageSink _NextSink; //保存下一个接收器
public ContextAccessPowerValidSink(IMessageSink nextSink)
{
this._NextSink = nextSink;
}
#region IMessageSink 成员
public IMessageSink NextSink { get { return this._NextSink; } }
//IMessageSink的接口方法,当消息传递的时被调用
public IMessage SyncProcessMessage(IMessage msg)
{
//拦截消息,做前处理
Preprocess(msg);
//传递消息给下一个接收器
IMessage retMsg = this._NextSink.SyncProcessMessage(msg);
return retMsg;
}
//IMessageSink接口方法,用于异步处理,权限验证中不需要异步所以返回null
public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
{
return null;
}
#endregion
/// <summary>
/// 调用前处理方法
/// </summary>
/// <param name="msg"></param>
protected void Preprocess(IMessage msg)
{
ValidAccessPowers(msg);
}
/// <summary>
/// 验证权限
/// </summary>
/// <param name="msg"></param>
protected virtual void ValidAccessPowers(IMessage msg)
{
IMethodCallMessage call = msg as IMethodCallMessage;
AccessPowerAttribute[] mustPowers;
//取得所有验证属性
mustPowers = call.MethodBase.GetCustomAttributes(typeof(AccessPowerAttribute), false) as AccessPowerAttribute[];
if (mustPowers == null || mustPowers.Length == 0) return;
foreach (AccessPowerAttribute mustPower in mustPowers)
{
//验证未通过则抛出异常
if (mustPower.Valid(call) == false)
{
throw new NotEnoughPowerException(mustPower.UserName, call.MethodName);
}
}
}
}
ContextAccessPowerValidSink继承于IMessageSink,用于通过反射取得业务层方法上所有的验证权限属性BaseAccessPowerAttribute,并逐个进行验证。
4.BaseAccessPowerAttribute
BaseAccessPowerAttribute
[AttributeUsage(AttributeTargets.Method, AllowMultiple=true, Inherited=false)]
public abstract class BaseAccessPowerAttribute : Attribute
{
/// <summary>
/// 验证权限方法,在ContextAccessPowerValidSink中调用此方法进行验证
/// </summary>
/// <param name="parameter"></param>
/// <returns></returns>
public abstract bool Valid(IMethodCallMessage callMsg);
/// <summary>
/// 用户信息,可以自行编写取得当前用户信息的方法
/// </summary>
protected abstract AccessUserInfo UserInfo { get; }
public string UserName { get { return UserInfo.Name; } }
}
抽象类BaseAccessPowerAttribute继承于Attribute,用于验证调用方法所需要的权限,具体的验证规则与用户信息由继承类编写。
5。AccessPowerAttribute
AccessPowerAttribute
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
public class AccessPowerAttribute : BaseAccessPowerAttribute
{
private AccessPower _needPower;
public AccessPowerAttribute(AccessPower power)
{
this._needPower = power;
}
/// <summary>
/// 所需要的权限
/// </summary>
public AccessPower NeedPower { get { return _needPower; } }
/// <summary>
/// 验证权限方法
/// </summary>
/// <param name="parameter"></param>
/// <returns></returns>
public override bool Valid(IMethodCallMessage callMsg)
{
if (this.UserInfo == null) return false;
return ((AccessPower)this.UserInfo.Powers & this.NeedPower) == this.NeedPower;
}
/// <summary>
/// 用户信息,这里是使用业务层基类BLLBase提供的当前用户信息
/// </summary>
protected override AccessUserInfo UserInfo { get { return BLL.BLLBase.CurrentUserInfo; } }
}
AccessPowerAttribute继承于BaseAccessPowerAttribute,重写了基类的验证规则和当前用户信息的属性。
6.BLLBase
BLLBase
public class BLLBase : ContextBoundObject //基类必须继承于ContextBoundObject
{
private static AccessUserInfo _CurrentUserInfo;
public static AccessUserInfo CurrentUserInfo { get { if (_CurrentUserInfo == null) SetCurrentUserInfo("test", AccessPower.User_Add | AccessPower.User_Search); return _CurrentUserInfo; } }
public static void SetCurrentUserInfo(string name, AccessPower powers)
{
_CurrentUserInfo = new AccessUserInfo(name, powers);
}
}
业务层基类BLLBase继承于ContextBoundObject,并定义了当前用户信息。
7.NewsBLL
NewsBLL
[ContextAccessPowerValidAttribute]//此属性是必须的 否则调用不会被捕获从而无法进行验证
public sealed class NewsBLL : BLLBase
{
[AccessPowerAttribute(AccessPower.User_Add)]//设置验证属性和所需要的权限,以下同
public void Insert(NewsInfo dataInfo)
{
MessageBox.Show("Insert成功");
}
[AccessPowerAttribute(AccessPower.User_Edit)]
public void Update(NewsInfo dataInfo)
{
MessageBox.Show("Update成功");
}
[AccessPowerAttribute(AccessPower.User_Delete)]
public void Delete(long id)
{
MessageBox.Show("Delete成功");
}
[AccessPowerAttribute(AccessPower.User_Search)]
public void Search()
{
MessageBox.Show("Search成功");
}
}
业务类NewsBLL继承于BLLBase,用于News模块业务逻辑的调用
8.AccessUserInfo
AccessUserInfo
public class AccessUserInfo
{
private string _Name;
private AccessPower _Powers;
public AccessUserInfo(string name, AccessPower powers)
{
this._Name = name;
this._Powers = powers;
}
/// <summary>
/// 用户名
/// </summary>
public string Name { get { return _Name; } }
/// <summary>
/// 用户的权限
/// </summary>
public AccessPower Powers { get { return _Powers; } }
}
AccessUserInfo,用户信息,存储了用户名和用户权限。
以上就是主要的类,现在来说明这些类是如何运作的。
当初始化业务类NewsBLL,并调用其任意一个方法时ContextAccessPowerValidSink类中的SyncProcessMessage方法会被调用,方法ValidAccessPowers在SyncProcessMessage中被调用了。
ValidAccessPowers
protected virtual void ValidAccessPowers(IMessage msg)
{
IMethodCallMessage call = msg as IMethodCallMessage;
BaseAccessPowerAttribute[] mustPowers;
//取得所有验证属性
mustPowers = call.MethodBase.GetCustomAttributes(typeof(BaseAccessPowerAttribute), false) as BaseAccessPowerAttribute[];
if (mustPowers == null || mustPowers.Length == 0) return;
foreach (BaseAccessPowerAttribute mustPower in mustPowers)
{
//验证未通过则抛出异常
if (mustPower.Valid(call) == false)
{
throw new NotEnoughPowerException(mustPower.UserName, call.MethodName);
}
}
}
在ValidAccessPowers方法中将参数msg转换为IMethodCallMessage类型的变量call。然后根据call的属性call.MethodBase可以取得被调用的方法的信息,由此可以得到该方法中所有继承于BaseAccessPowerAttribute的属性。
设置属性:
[AccessPowerAttribute(AccessPower.User_Add)]//设置验证属性和所需要的权限,以下同
public void Insert(NewsInfo dataInfo)
{
MessageBox.Show("Insert成功");
}
取得属性:
BaseAccessPowerAttribute[] mustPowers = call.MethodBase.GetCustomAttributes(typeof(BaseAccessPowerAttribute), false) as BaseAccessPowerAttribute[];
然后遍历mustPowers数组,通过BaseAccessPowerAttribute属性的方法Valid进行验证,有一个属性验证不通过则抛出异常,由此来终止方法的调用保证其不被越权调用。
foreach (BaseAccessPowerAttribute mustPower in mustPowers)
{
//验证未通过则抛出异常
if (mustPower.Valid(call) == false)
{
throw new NotEnoughPowerException(mustPower.UserName, call.MethodName);
}
}
调用Valid方法时将call作为参数传递到验证方法中是为了可以取得调用需验证的权限时使用的参数。如:
NewsBLL newsBLL = new NewsBLL();
newsBLL.Insert(new NewsInfo { Title = "title", Content = "content" });
public override bool Valid(IMethodCallMessage callMsg)//重写AccessPowerAttribute的验证方法
{
NewsInfo newsInfo = (NewsInfo)callMsg.Args[0]; //这样就可以取得调用newsBLL.Insert时用的参数
}
在一些更复杂的验证规则中就可以用到。如验证News是否是当前用户发布的。
可能说得不太清楚,大家可以下载示例运行看看。示例项目