WCF服务授权模式

时间:2020-03-06 14:50:16  来源:igfitidea点击:

我正在实施安全的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