WCF服务授权模式

时间:2008-09-28 02:31:22

标签: c# .net wcf authorization

我正在实施一个安全的WCF服务。使用用户名/密码或Windows凭据进行身份验证。该服务托管在Windows服务进程中。现在,我正在尝试找出为每个服务操作实施授权的最佳方法。

例如,请考虑以下方法:

public EntityInfo GetEntityInfo(string entityId);

您可能知道,在WCF中,有一个OperationContext对象,您可以从中检索调用方/客户端传入的安全凭据。现在,身份验证在调用方法的第一行时已经完成了。但是,如果决策取决于输入数据本身,我们如何实施授权?例如,在上面的例子中,说'admin'用户(其权限等存储在数据库中),允许获取实体信息,不允许其他用户...我们在哪里进行授权检查?< / p>

假设我们把它放在方法的第一行,如下所示:

CheckAccessPermission(PermissionType.GetEntity, user, entityId) //user is pulled from the current OperationContext

现在,有几个问题:

  1. 我们是否在授权检查或INSIDE授权检查之前验证entityId(例如检查null /空值等)?换句话说,如果每个方法都应该包含授权检查,那么这是一个好的模式吗?哪个应该首先发生 - 参数验证或授权?

  2. 当授权检查遍布这样的地方时,我们如何对WCF服务进行单元测试,并且我们在单元测试中没有OperationContext!? (假设我在没有任何WCF设置的情况下尝试直接测试此服务类实现)。

  3. 任何想法的人?

3 个答案:

答案 0 :(得分:6)

对于问题1,最好先执行授权。这样,您就不会将验证错误消息泄漏回未经授权的用户。

BTW,您可能能够连接到WCF对ASP.NET角色提供程序的开箱即用支持,而不是使用自行开发的身份验证方法(我假设您的CheckAccessPermission是什么)。完成此操作后,您将通过OperationContext.Current.ServiceSecurityContext.PrimaryIdentity.IsInRole()执行授权。 PrimaryIdentity是一个IPrincipal。

答案 1 :(得分:6)

关于问题#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框架:

答案 2 :(得分:3)

对于问题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”类,并对另一个进行单元测试。