WCF服务授权模式

7

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

例如,考虑以下方法:

public EntityInfo GetEntityInfo(string entityId);

正如您所知,在WCF中,有一个OperationContext对象,您可以从中检索调用方/客户端传递的安全凭据。现在,身份验证已经在方法的第一行被完成。然而,如果决策取决于输入数据,我们该如何实施授权呢?例如,在上述情况中,假设“管理员”用户(其权限等存储在数据库中)被允许获取实体信息,其他用户则不被允许......我们应该在哪里放置授权检查呢?
比如我们把它放在方法的第一行:
CheckAccessPermission(PermissionType.GetEntity, user, entityId) //user is pulled from the current OperationContext

现在有几个问题:
  1. 我们是在授权检查之前还是之后验证entityId(例如检查空值/空字符串等)?换句话说,如果授权检查应包含在每个方法中,这是一个好的模式吗?参数验证和授权哪个应该先发生?

  2. 当授权检查到处都是时,我们如何对WCF服务进行单元测试,而且在单元测试中没有OperationContext!?(假设我正在直接测试此服务类实现,而没有任何WCF设置)。

有什么想法吗?
3个回答

6

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

顺便说一下,您可以连接到WCF的ASP.NET角色提供程序的开箱即用支持,而不是使用自己编写的身份验证方法(我假设这就是您的CheckAccessPermission)。完成后,您可以通过OperationContext.Current.ServiceSecurityContext.PrimaryIdentity.IsInRole()执行授权。PrimaryIdentity是一个IPrincipal。


谢谢Paul。首先进行授权的问题在于:如果我们需要根据输入参数检查权限,那么我们如何授权用户呢?难道我们不需要先验证这些参数,然后再用它进行授权吗? - Krishna
授权应该仅取决于用户的身份。如果它依赖于输入参数,那么调用方可以发送任何需要获取所需授权的值,因此您的授权检查变得毫无意义。 - Paul Lalonde
6
不。假设我想要访问一个ID为“abc1”的对象。我是“user1”。授权决定了“user1”是否可以访问对象“abc1”。因此,首先要做的事情就是验证包含对象ID字符串的参数! - Krishna

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框架:

3
对于问题1,务必先进行授权。为了保持最严密的安全性,在授权之前不应执行任何(受您控制的)代码。Paul上面的例子非常好。
对于问题2,您可以通过子类化具体的服务实现来处理此问题。将真正的业务逻辑实现作为一个抽象类,并添加一个抽象的“CheckPermissions”方法,就像您上面提到的那样。然后创建两个子类,一个用于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”,而你对另一个类进行单元测试。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接