This code exists to allow you to put both [OutputCache] and [Authorize] together on an action without running the risk of having a response that was generated for an authorized user cached and served to a user that is not authorized.
Here's the source code comment from AuthorizeAttribute.cs:
Since we're performing authorization
at the action level, the authorization
code runs after the output caching
module. In the worst case this could
allow an authorized user to cause the
page to be cached, then an
unauthorized user would later be
served the cached page. We work around
this by telling proxies not to cache
the sensitive page, then we hook our
custom authorization code into the
caching mechanism so that we have the
final say on whether a page should be
served from the cache.
So just what is this attribute doing? It first disables proxy caching of this response, as proxies can't make the proper determination of which users are or are not authorized to view it. And if a proxy serves the response to an unauthorized user, this is a Very Bad Thing.
Now what about AddValidationCallback? In ASP.NET, the output caching module hooks events that run before the HTTP handler. Since MVC is really just a special HTTP handler, this means that if the output caching module detects that this response has already been cached, the module will just serve the response directly from cache without going through the MVC pipeline at all. This is also potentially a Very Bad Thing if the output cache serves the response to an unauthorized user.
Now take a closer look at 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;
}
This effectively just associates the AuthorizeCore method with the cached response. When the output cache module detects a match, it will re-run the AuthorizeCore method to make sure that the current user really is allowed to see the cached response. If AuthorizeCore returns true, it's treated as a cache hit (HttpValidationStatus.Valid), and the response is served from cache without going through the MVC pipeline. If AuthorizeCore returns false, it's treated as a cache miss (HttpValidationStatus.IgnoreThisRequest), and the MVC pipeline runs as usual to generate the response.
As an aside, since a delegate is formed to AuthorizeCore (thus capturing the particular instance of AuthorizeAttribute) and saved in a static cache, this is why all types subclassing AuthorizeAttribute must be thread-safe.