在.NET Remoting技术中实现用户的验证和授权并不那么容易,尤其是将其宿主在windows service这样的环境中的时候。大部分时候,我们都需要实现自定义的验证方式。具体来说也不是那么复杂,不外乎是在客户端请求的时候,提交一个相应的凭据(通过CallContext的SetData方法即可),而在服务端(或者准确地说是远程对象中),我们检查CallContext中是否有相应的凭据,并且确认其是否合法等等。
这里的一个关键是
1. 用来传递的凭据信息,如果我们封装为一个类的话,那么应该实现ILogicalThreadAffinative接口,并且可序列化
ok,搞清楚了这个逻辑,那么无外乎就是在远程对象中实现一个方法,叫做ValidateUser,然后根据CallContext中的信息去验证用户的身份,然后在每个方法中调用该ValidateUser方法先。大致如下
public string Helloworld() {
if(ValidateUser()){
return "Hello,world";
}
else
//throw exception here
}
public bool ValidateUser(){
//implement the validate logic
}
等等,这样是不是万事大吉了呢?每个方法里面都需要添加下面的代码,不是吗?
if(ValidateUser()){
//do something actually want to do
}
else
//throw exception here
我们就会想,类似用户验证这样的工作,是否可以统一编写,而无需影响我们方法本身的方法体设计呢?首先我们就会想到Attribute,如果说某些方法需要进行身份验证,那么我们就给它加一个Attribute,这样是不是就更好一些呢?
但关键在于Attribute如何触发一些操作呢?就是说凭什么说Attribute指定了,就可以进行一些操作呢?我们立即又想到了Attribute的构造函数,但相当让人失望的是,该构造函数并不会在方法调用的时候被调用,更不用说是在方法调用之前被调用了。那么Attribute的构造函数到底啥时候被调用呢? 很让人疑惑的是,它在我们用GetCustomerAttribute方法去取得它的实例的时候被调用。
Attribute这条路不通,我们就自然会想到能不能用事件来做呢?但问题在于,我们无法收到方法被调用的所谓事件。
带着这样的疑惑,我找了不少资料,也求教了不少的人。最后我找到的答案是:这种设计就是所谓的AOP:Aspect-oriented programming,有关它的一些介绍,请参考下面的链接
http://en.wikipedia.org/wiki/Aspect-oriented_programming
其中PostSharp是一套比较出名的,也是开源的框架。我利用它做出来如下的效果
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Messaging;
using PostSharp.Laos;
namespace BankLibrary
{
public class Account:MarshalByRefObject
{
[Authentication]
public string Helloworld() {
return "Hello,world";
}
}
[Serializable]
public class AuthenticationAttribute:OnMethodBoundaryAspect{
public override void OnEntry(MethodExecutionEventArgs eventArgs)
{
if (CallContext.GetData("Credential") == null)
{
throw new RemotingException("没有相应的凭据");
}
if (!ValidateUser((Credential)CallContext.GetData("Credential")))
{
throw new RemotingException("错误的凭据");
}
eventArgs.FlowBehavior = FlowBehavior.Continue;
}
private bool ValidateUser(Credential credential)
{
if (credential.UserName != "chenxizhang" || credential.Password != "password")
return false;
return true;
}
}
[Serializable]
public class Credential : ILogicalThreadAffinative
{
public string UserName{ get; set; }
public string Password { get; set; }
}
}
以上代码要能运行,你必须引用PostSharp.Laos和PostSharp.Public
PostSharp的网站是:
值得一说的是,你可以会担心用户信息传递之间的安全性,这一点,.NET 2.0已经支持对信道加密,所以无需特别设计。
实际上,PostSharp是改变了.NET 程序集的编译行为,你看看下面这个图就会明白了
微软的企业库从3.0这个版本开始也支持所谓的策略注入,依赖注入。但相对来说过于复杂了(我个人觉得)。
实际上,我觉得如果Attribute有一个专门的属性,就是指定是否要预先构造的话,那不就解决了所有的问题了么?为什么要这样麻烦去做注入呢?