4

我们旧的软件架构使用基于角色的验证。我们现在要使用基于声明的授权。事实上,我认为我们总是使用建模声称的东西,即使我们使用了角色库技术。

最低级别是权限。权限可以是“调用用户服务添加用户”或简称“UserService.Add”。可以将权限分配给组。用户可以是组的成员。最后,通过组成员身份,用户可以拥有一系列权限。

旧系统使用UserNamePasswordValidatorIAuthorizationPolicyCodeAccessSecurityAttribute的组合来具有写在服务方法之上的属性,并且在调用服务方法时,将检查有效性。如果用户没有所需的权限,访问将被拒绝。工作得很好。

[CompanyRoleRequired(SecurityAction.Demand, Role = "Common.Connect")]
[CompanyRoleRequired(SecurityAction.Demand, Role = "SomeServiceName.Save")]
public void Save(IEnumerable<Data> data)
{
  // code
}

现在我想使用基于声明的授权。保留上面的模型,我将为每个以前的特权创建一个声明,或者为每个具有其操作的有效值的服务创建一个声明。例如,我可以添加声明“UserService”而不是“UserService.Add”,而具有前一个特权的人将获得值为“Add”的声明。我想为服务开发人员提供同样方便的访问检查,所以我希望在服务方法上方注释所需的声明。Microsoft 已经为此提供了ClaimsPrincipalPermissionAttribute

我没有实现 IAuthorizationPolicy,而是实现了ClaimsAuthorizationManager

问题 1)授权管理器被调用两次。一次使用肥皂网址,一次使用我的属性。我用谷歌搜索了很多,这似乎是设计使然。我在区分通话和只检查我的通话时没有问题,但也许我什么也没看到。是否有一个选项或一种简单的方法可以不使用 url 调用肥皂调用而只调用属性?

问题 2)访问检查提供了检查主体是否有索赔的能力。显然,声明具有类型/名称和值。我本来希望该属性提供这样的接口。但是,该属性想了解资源和操作。我需要覆盖的访问检查功能也需要检查资源和操作。这是为什么?我是否需要将资源/操作映射到我的 AuthorizationManager 中的声明?如果我看不出有任何需要,是否可以将声明的预期类型和值作为资源和操作放入属性中并将它们 1:1 映射到授权管理器中?或者如果我这样做,我会错过一些重要的安全功能吗?

4

2 回答 2

6

Q1) 不幸的是这种情况 - 由于“自动”调用和属性/.CheckAccess 都使用相同的声明类型,因此您无法轻松区分两者。我在这里写过:http: //leastprivilege.com/2011/04/30/what-i-dont-like-about-wifs-claims-based-authorization/

Q2)你在这里错过了这个概念。这个想法是不检查特定的声明 - 而是用“你在做什么”来注释代码。编写业务代码的人通常不确切知道允许谁调用它(或者确切需要哪些声明)。您只需告诉 authZ 策略您将要添加一个客户(例如)。授权经理的工作是确定委托人是否被授权这样做(通过任何方式)。关注点分离。见这里: http: //leastprivilege.com/2011/04/30/what-i-like-about-wifs-claims-based-authorization/

于 2013-03-11T10:56:30.350 回答
1

不确定这是否会对您有所帮助,但 ClaimsAuthorizationManager 具有可被覆盖的方法 (LoadCustomConfiguration),您可以使用该方法从 XML 文件加载策略。该策略的设计方式可能允许在资源、操作和角色之间进行映射。我已经建立了代码内访问控制列表,而不是如下所示:

public interface IAccessControlList
{
   List<CustomAccessRule> Rules { get; }
}

public class CustomAccessRule
{
    public string Operation { get; set; }
    public List<string> Roles { get; set; }

    public CustomAccessRule(string operation, params string[] roles)
    {
        Operation = operation;
        Roles = roles.ToList();
    }
}

我的索赔授权管理器如下所示:

public class CustomClaimsAuthorizationManager : ClaimsAuthorizationManager
{
    private IAccessControlList _accessControlList;

    public CustomClaimsAuthorizationManager(IAccessControlList accessControlList)
    {
        _accessControlList = accessControlList;
    }

    public override bool CheckAccess(AuthorizationContext context)
    {
        string operation = context.Action.First().Value.Split('/').Last();
        CustomAccessRule rule = _accessControlList.Rules.FirstOrDefault(x => x.Operation == operation);
        if (rule == null) return true;
        if (context.Principal.Identities.First().IsInRoles(rule.Roles)) return true;
        throw new MessageSecurityException(string.Format("Username {0} does not have access to operation {1}.", context.Principal.Identities.First().Name, operation));
    }

}

这是一个服务的一个访问控制列表实现的示例:

public class SampleServiceACL : IAccessControlList
{
    public List<CustomAccessRule> Rules { get; private set; }

    public SampleServiceACL()
    {
        Rules = new List<CustomAccessRule>();
        Rules.Add(new CustomAccessRule("OpenAccount", "Manager", "Owner"));
        Rules.Add(new CustomAccessRule("CloseAccount", "Manager", "Owner"));
        Rules.Add(new CustomAccessRule("SendEmail", "User", "Manager", "Owner"));
    }
}

我正在使用以下方法在服务主机基础级别应用它:

    protected override void OnOpening()
    {
        base.OnOpening();

        IdentityConfiguration identityConfiguration = new IdentityConfiguration();
        identityConfiguration.SecurityTokenHandlers.Clear();
        identityConfiguration.ClaimsAuthorizationManager = new CustomClaimsAuthorizationManager(new SampleServiceACL());
        this.Credentials.IdentityConfiguration = identityConfiguration;

        ...
    }

结果,我根本没有使用属性,所有授权逻辑都集中在 ACL 上的声明授权管理器中。

现在,如果您不喜欢这种方法,并且您仍在追求将检查特定声明的属性,那么您可以从CodeAccessSecurityAttribute派生并实际实现该逻辑。MS开箱即用的东西是好的,但这并不意味着你应该以任何方式坚持下去。检查声明的逻辑也可以作为身份的扩展来实现,即:

public static class IdentityExtensions
{
    public static bool IsInRoles(this ClaimsIdentity id, List<string> roles)
    {
        foreach (string role in roles)
            if (id.HasClaim(ClaimTypes.Role, role)) return true;
        return false;
    }
}

因此,您可以构建扩展、自定义属性,然后在属性中使用扩展来执行验证逻辑。

同样,这只是我已经做过的事情。可能不是您正在寻找的,但它是一种自定义解决方案。

于 2013-03-11T05:42:02.060 回答