1

我需要通过单独的授权规则列表来保护我的业务对象属性。我希望在各种操作期间暂停我的授权规则,例如转换为 DTO 和执行验证规则(验证当前用户无权查看的属性值)。

我正在研究的方法将调用包装在一个范围对象中,该对象使用 [ThreadStatic] 属性来确定是否应该运行授权规则:

public class SuspendedAuthorizationScope : IDisposable
{
    [ThreadStatic]
    public static bool AuthorizationRulesAreSuspended;

    public SuspendedAuthorizationScope()
    {
        AuthorizationRulesAreSuspended = true;
    }

    public void Dispose()
    {
        AuthorizationRulesAreSuspended = false;
    }
}

这是 IsAuthorized 检查(来自基类):

public bool IsAuthorized(string memberName, AuthorizedAction authorizationAction)
{
    if (SuspendedAuthorizationScope.AuthorizationRulesAreSuspended)
        return true;

    var context = new RulesContext();

    _rules.OfType<IAuthorizationRule>()
        .Where(r => r.PropertyName == memberName)
        .Where(r => r.AuthorizedAction == authorizationAction)
        .ToList().ForEach(r => r.Execute(context));

    return context.HasNoErrors();
}

这是演示用法的 ValidateProperty 方法(来自基类):

private void ValidateProperty(string propertyName, IEnumerable<IValidationRule> rules)
{
    using (new SuspendedAuthorizationScope())
    {
        var context = new RulesContext();
        rules.ToList().ForEach(rule => rule.Execute(context));
         if (HasNoErrors(context))
            RemoveErrorsForProperty(propertyName);
        else
            AddErrorsForProperty(propertyName, context.Results);
    }
    NotifyErrorsChanged(propertyName);   
}

我对范围对象进行了一些测试,表明只要 lambda 在 using 语句的范围内解析,就会使用 SuspendedAuthorizationScope.AuthorizationRulesAreSuspended 的预期/正确值。

这个设计有什么明显的缺陷吗?就线程而言,我应该关注 ASP.NET 中的任何内容吗?

4

3 回答 3

2

如果您可以完全控制您的代码并且不关心由于神奇的静态值而导致的隐藏依赖项,那么您的方法将起作用。请注意,你给你/任何支持你的代码的人带来了很大的负担,以确保你的 using 块内永远不会有异步处理,并且每个魔法值的使用都用适当的using block.

一般来说,这是个坏主意,因为:

  • 线程和请求不是一一对应的,因此您可能会遇到线程本地对象正在更改其他请求的状态的情况。在您使用带有异步处理程序的 ASP.Net MVC4+ 时,这种情况更有可能发生。
  • 任何类型的静态值都是代码异味,您应该尽量避免它们。

存储请求相关信息应该在HttpContext.Items中完成,或者可能Session(会话也会持续更长时间,并且需要更仔细地管理清理状态)。

于 2014-03-16T03:37:03.890 回答
2

我对您提出的方法有两个担忧:

  1. using创建时未使用SuspendedAuthorizationScope将导致保留超出预期范围的开放访问。换句话说,一个容易犯的错误会导致安全漏洞(尤其是当新员工开始挖掘未知代码并错过这个微妙的案例时,考虑到未来验证你的代码/设计)。
  2. 将这个魔法标志附加到ThreadStaticnow 可以通过保留对另一个页面的开放访问的可能性来放大前一个项目符号,因为线程将用于在处理完当前页面后处理另一个请求,并且它的授权标志之前没有被重置。因此,现在授权的范围比它应该更长的时间不仅超出了对 的丢失调用.Dispose(),而且实际上可以泄漏到另一个请求/页面和完全不同的用户。

也就是说,我所看到的解决此问题的方法确实涉及检查授权并标记一个允许稍后绕过的魔术标志,然后将其重置。

建议: 1. 为了至少解决最坏的变体(上面的#2),你能不能把魔法 cookie 移动到你的基页面类的成员中,并让它有一个实例字段,它只对那个页面的范围有效,而不是其他情况?2.为了解决所有情况,是否可以使用 Functors 或类似的方式传递给授权函数,然后在成功授权后启动运行所有逻辑并保证清理的 Functor?请参见下面的伪代码示例:

void myBizLogicFunction()
{
   DoActionThatRequiresAuthorization1();
   DoActionThatRequiresAuthorization2();
   DoActionThatRequiresAuthorization3();
}

void AuthorizeAndRun(string memberName, AuthorizedAction authorizationAction, Func privilegedFunction)
{
    if (IsAuthorized(memberName, authorizationAction))
    {
        try
        {
            AuthorizationRulesAreSuspended = true;

            privilegedFunction();
        }
        finally
        {
            AuthorizationRulesAreSuspended = true;
        }
    }
}

有了上面的内容,我认为它可以是线程静态的,因为它可以finally保证运行,因此授权不能泄漏到privilegedFunction. 我认为这会起作用,尽管可以使用其他人的验证和验证......

于 2014-03-16T02:49:49.917 回答
0

我担心的是你离开 using 块的时间和垃圾收集器处理你的对象所花费的时间之间的潜在延迟。您处于虚假“授权”状态的时间可能比您预期的要长。

于 2014-03-16T02:51:41.790 回答