ThreadStatic和ASP.NET

时间:2014-03-16 02:25:13

标签: c# asp.net multithreading threadstatic

我需要通过单独的授权规则列表来保护我的业务对象属性。我希望在各种操作中暂停我的授权规则,例如转换为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吗?

3 个答案:

答案 0 :(得分:2)

我对您提出的方法有两个问题:

  1. 创建using时未能使用SuspendedAuthorizationScope将导致保留超出预期范围的开放访问权限。换句话说,容易犯错将导致安全漏洞(特别是考虑到未来的代码/设计,当新员工开始挖掘未知代码并错过这个微妙的案例时)。
  2. 将此魔术标志附加到ThreadStatic现在可以放弃前一个项目符号,因为该线程将在完成当前页面后用于处理另一个请求及其授权标志以前没有被重置。因此,现在授权范围的延迟时间超过了.Dispose(),但实际上可能会泄漏到另一个请求/页面和完全不同的用户。
  3. 那就是说,我看到解决这个问题的方法确实涉及到检查授权并标记一个允许稍后绕过的魔术标志,然后重置它。

    建议:  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的调用之外。我认为这可行,但可以使用其他人的验证和验证......

答案 1 :(得分:2)

如果您完全控制代码并且不关心由于静态静态值而导致的隐藏依赖关系,那么您的方法将起作用。请注意,你给你/谁支持你的代码带来了很大的负担,以确保你的使用块中永远不会进行异步处理,并且魔术值的每次使用都包含在适当的using block中。

总的来说,这是个坏主意,因为:

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

存储请求相关信息应该在HttpContext.ItemsSession中完成(会话将持续更长时间,需要更仔细地管理清理状态)。

答案 2 :(得分:0)

我担心的是你离开使用区块的时间与垃圾收集器处理你的对象所花费的时间之间的潜在延迟。您可能处于虚假的“授权”状态,而不是您想要的状态。