此代码允许您将 [OutputCache] 和 [Authorize] 放在一起执行操作,而不会冒为授权用户生成的响应被缓存并提供给未经授权的用户的风险。
这是来自 AuthorizeAttribute.cs 的源代码注释:
由于我们在操作级别执行授权,因此授权代码在输出缓存模块之后运行。在最坏的情况下,这可能允许授权用户使页面被缓存,然后未经授权的用户稍后将获得缓存的页面。我们通过告诉代理不要缓存敏感页面来解决这个问题,然后我们将自定义授权代码挂钩到缓存机制中,以便我们对是否应该从缓存中提供页面拥有最终决定权。
那么这个属性在做什么呢?它首先禁用此响应的代理缓存,因为代理无法正确确定哪些用户有权或无权查看它。如果代理向未经授权的用户提供响应,这是一件非常糟糕的事情。
现在 AddValidationCallback 呢?在 ASP.NET 中,输出缓存模块挂钩在 HTTP 处理程序之前运行的事件。由于 MVC 实际上只是一个特殊的 HTTP 处理程序,这意味着如果输出缓存模块检测到此响应已被缓存,则该模块将直接从缓存中提供响应,而根本不经过 MVC 管道。如果输出缓存为未经授权的用户提供响应,这也可能是一件非常糟糕的事情。
现在仔细看看CacheValidateHandler:
private void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus) {
validationStatus = OnCacheAuthorization(new HttpContextWrapper(context));
}
// This method must be thread-safe since it is called by the caching module.
protected virtual HttpValidationStatus OnCacheAuthorization(HttpContextBase httpContext) {
if (httpContext == null) {
throw new ArgumentNullException("httpContext");
}
bool isAuthorized = AuthorizeCore(httpContext);
return (isAuthorized) ? HttpValidationStatus.Valid : HttpValidationStatus.IgnoreThisRequest;
}
这实际上只是将AuthorizeCore方法与缓存的响应相关联。当输出缓存模块检测到匹配时,它将重新运行 AuthorizeCore 方法以确保当前用户确实被允许看到缓存的响应。如果 AuthorizeCore 返回 true,则将其视为缓存命中 (HttpValidationStatus.Valid),并且从缓存中提供响应而不通过 MVC 管道。如果 AuthorizeCore 返回 false,则将其视为缓存未命中 (HttpValidationStatus.IgnoreThisRequest),并且 MVC 管道照常运行以生成响应。
顺便说一句,由于为 AuthorizeCore 形成了一个委托(因此捕获了 AuthorizeAttribute 的特定实例)并保存在静态缓存中,这就是为什么所有继承 AuthorizeAttribute 的类型都必须是线程安全的。