WCF服务授权模式
我正在实施安全的WCF服务。使用用户名/密码或者Windows凭据进行身份验证。该服务托管在Windows Service进程中。现在,我正在尝试找出实现每个服务操作授权的最佳方法。
例如,考虑以下方法:
public EntityInfo GetEntityInfo(string entityId);
如我们所知,在WCF中,有一个OperationContext对象,我们可以从中检索调用者/客户端传递的安全凭证。现在,在调用该方法的第一行时,身份验证已经完成。但是,如果决定取决于输入数据本身,我们如何实现授权?例如,在上述情况下,假设"管理员"用户(其权限等存储在数据库中),被允许获取实体信息,而其他用户则不被允许……我们将授权检查放在哪里?
假设我们将其放在方法的第一行中,如下所示:
CheckAccessPermission(PermissionType.GetEntity, user, entityId) //user is pulled from the current OperationContext
现在,有两个问题:
- 我们是否在授权检查之前或者在授权检查之前验证entityId(例如,检查null /空值等)?换句话说,如果在每种方法中都应包括授权检查,那是一个好的模式吗?首先应该发生什么-参数验证或者授权?
- 当授权检查到处都是这样,并且在单元测试中没有OperationContext时,我们如何对WCF服务进行单元测试! (假设我正在尝试在不进行任何WCF设置的情况下直接测试此服务类的实现)。
有想法吗?
解决方案
对于问题1,最好先执行授权。这样,我们就不会将验证错误消息泄露给未经授权的用户。
顺便说一句,我们可以使用WCF对ASP.NET角色提供程序的现成支持,而不必使用本地开发的身份验证方法(我假设这是CheckAccessPermission所使用的身份验证方法)。完成此操作后,我们可以通过OperationContext.Current.ServiceSecurityContext.PrimaryIdentity.IsInRole()执行授权。 PrimaryIdentity是一个IPrincipal。
对于问题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框架:
- 马丁·福勒(DI)
- 杰里米·米勒(Jeremy Miller)谈DI
- Scott Hanselman的DI容器清单
- 我个人最喜欢的DI容器:The Castle Project Windsor Container