我需要通过一组单独的授权规则来保护我的业务对象属性。我希望在各种操作期间挂起我的授权规则,例如转换到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);
}
我已经得到了一些测试的范围对象,显示预期/正确的值的SuspendedAuthorizationScope。只要lambda在using语句的作用域中解析,AuthorizationRulesAreSuspended就会被使用。
这个设计有什么明显的缺陷吗?ASP中有什么东西吗?到目前为止,我应该关心的线程?我看到你提出的方法有两个问题:
- 在创建
SuspendedAuthorizationScope
时未能使用using
将导致保留超出预期范围的开放访问。换句话说,一个容易犯的错误会导致安全漏洞(特别是当一个新员工开始挖掘未知的代码,而忽略了这个微妙的情况时,考虑到未来的代码/设计)。 - 将这个神奇的标志附加到
ThreadStatic
现在放大了以前的子弹有可能留下对另一个页面的开放访问,因为线程将被用来处理另一个请求后,它与当前页面完成,其授权标志以前没有重置。因此,现在授权的范围比它应该停留的时间更长,不仅超出了丢失对.Dispose()
的调用,而且实际上可以泄漏到另一个请求/页面和完全不同的用户。
也就是说,我所看到的解决这个问题的方法确实涉及到检查授权和标记一个允许稍后绕过的魔术标志,然后重置它。
建议:1. 为了解决最糟糕的变种(上面的第2条),您是否可以将magic cookie移动为基页面类的成员,并使其具有仅对该页的作用域有效而对其他实例无效的实例字段?2. 为了解决所有情况,是否可以使用传递给授权函数的Functor或类似的方法,从而在成功授权后启动运行所有逻辑并保证清理的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
的调用。我认为这将工作,虽然可以使用验证和验证由其他人…
如果你完全控制你的代码,并且不关心由于神奇的静态值而隐藏的依赖关系,那么你的方法将有效。请注意,你给你/支持你代码的人带来了很大的负担,以确保在你的using块中永远不会有异步处理,并且每次使用magic值都用适当的using block
包装。
一般来说,这是一个坏主意,因为:
- 线程和请求不是一对一的,所以当你的线程本地对象正在改变一些其他请求的状态时,你可能会遇到这种情况。在使用ASP时,这种情况更有可能发生。Net MVC4+与异步处理程序。 任何类型的
- 静态值都有代码气味,您应该尽量避免使用它们。
存储请求相关信息应该在HttpContext中完成。项目或Session
(session也会持续更长时间,需要更仔细地管理清理状态)。
我担心的是离开using块的时间和垃圾收集器处理对象的时间之间的潜在延迟。