问题1,绝对先做授权。在授权之前,不应执行任何代码(在您的控制范围内)以保持最严格的安全性。上面保罗的榜样非常好。
对于问题2,您可以通过子类化具体的服务实现来处理这个问题。如上所述,使用抽象的“CheckPermissions”方法将真正的业务逻辑实现作为抽象类。然后创建2个子类,一个用于WCF使用,另一个(在非部署的DLL中非常孤立)返回true(或者您希望它在单元测试中执行的任何操作)。
示例(注意,这些不应该在同一个文件中,甚至不应该在DLL中!):
public abstract class MyServiceImpl { public void MyMethod(string entityId) { CheckPermissions(entityId); //move along... } protected abstract bool CheckPermissions(string entityId); } public class MyServiceUnitTest { private bool CheckPermissions(string entityId) { return true; } } public class MyServiceMyAuth { private bool CheckPermissions(string entityId) { //do some custom authentication return true; } }
然后,您的WCF部署使用“MyServiceMyAuth”类,并对另一个进行单元测试。
关于问题#2,我会使用依赖注入执行此操作并设置您的服务实现,如下所示:
class MyService : IMyService { public MyService() : this(new UserAuthorization()) { } public MyService(IAuthorization auth) { _auth = auth; } private IAuthorization _auth; public EntityInfo GetEntityInfo(string entityId) { _auth.CheckAccessPermission(PermissionType.GetEntity, user, entityId); //Get the entity info } }
请注意,IAuthorization是您要定义的接口。
因为您将直接测试服务类型(即,不在WCF托管框架内运行它),您只需将服务设置为使用允许所有调用的虚拟IAuthorization类型。然而,更好的测试是模拟IAuthorization并测试它是否与您期望的参数一起调用。这允许您测试您对授权方法的调用是否有效,以及方法本身。
将授权分成它自己的类型还可以让您更容易地单独测试它是否正确。在我(尽管有限)的经验中,使用DI“模式”可以更好地分离您的类型中的关注点和可测试性,并导致更清晰的界面(这显然是有争议的)。
我首选的模拟框架是 RhinoMocks 这是免费的,具有非常好的流畅的界面,但有很多其他人在那里。如果你想了解更多关于DI的信息,可以参考一些优秀的引物和.Net框架:
对于问题1,最好先执行授权。这样,您就不会将验证错误消息泄漏回未经授权的用户。
顺便说一句,您可能能够连接到WCF对ASP.NET角色提供程序的开箱即用支持,而不是使用自行开发的身份验证方法(我假设它是您的CheckAccessPermission)。完成此操作后,您将通过OperationContext.Current.ServiceSecurityContext.PrimaryIdentity.IsInRole()执行授权。 PrimaryIdentity是一个IPrincipal。