Почему я не могу объединить атрибуты [Authorize] и [OutputCache] при использовании кэша Azure (приложение .NET MVC3)?

Использование Windows Azure Microsoft.Web.DistributedCache.DistributedCacheOutputCacheProvider в качестве поставщика outputCache для приложения MVC3. метод действия:

[ActionName("sample-cached-page")]
[OutputCache(Duration = 300, VaryByCustom = "User", 
    Location = OutputCacheLocation.Server)]
[Authorize(Users = "me@mydomain.tld,another@otherdomain.tld")]
public virtual ActionResult SampleCachedPage()
{
    return View();
}

Я получаю следующее исключение при загрузке этого представления из веб-браузера:

System.Configuration.Provider.ProviderException: When using a custom output cache provider like 'DistributedCache', only the following expiration policies and cache features are supported: file dependencies, absolute expirations, static validation callbacks and static substitution callbacks.

System.Configuration.Provider.ProviderException: When using a custom output cache provider like 'DistributedCache', only the following expiration policies and cache features are supported:  file dependencies, absolute expirations, static validation callbacks and static substitution callbacks.
   at System.Web.Caching.OutputCache.InsertResponse(String cachedVaryKey, CachedVary cachedVary, String rawResponseKey, CachedRawResponse rawResponse, CacheDependency dependencies, DateTime absExp, TimeSpan slidingExp)
   at System.Web.Caching.OutputCacheModule.OnLeave(Object source, EventArgs eventArgs)
   at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
   at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

Если я удалю атрибут [Authorize], представление кэшируется, как и ожидалось. Означает ли это, что я не могу включить [OutputCache] метод действия, который должен иметь [Authorize]? Или мне нужно переопределить AuthorizeAttribute с помощью настраиваемой реализации, которая использует метод обратного вызова статической проверки для кеша?

Обновление 1

После ответа Эвана я протестировал указанное выше действие в IIS Express (за пределами Azure). Вот мое переопределение для свойства VaryByCustom = "User" в атрибуте OutputCache:

public override string GetVaryByCustomString(HttpContext context, string custom)
{
    return "User".Equals(custom, StringComparison.OrdinalIgnoreCase)
        ? Thread.CurrentPrincipal.Identity.Name
        : base.GetVaryByCustomString(context, custom);
}

Когда я посещаю образец кэшированной страницы как (скрытый), вывод страницы кэшируется, и в представлении отображается «Эта страница была кэширована 31.12.2011, 11:06: 12 AM (UTC) ". Если я затем выйду из системы и войду как (скрытый) и перейду на страницу, появится сообщение «Эта страница была кэширована 31.12.2011, 11:06: 38 AM (UTC)». При повторном входе в систему как (скрытом) и повторном посещении страницы в кеше снова отображается сообщение «Эта страница была кэширована 31.12.2011, 11:06: 12 AM (UTC)».Дальнейшие попытки входа / выхода показывают, что различные выходные данные кэшируются и возвращаются в зависимости от пользователя.

Это наводит меня на мысль, что вывод кэшируется отдельно в зависимости от пользователя, что является намерением с моей настройкой и переопределением VaryByCustom = "User". Проблема в том, что он не работает с поставщиком распределенного кеша Azure. Эван, вы отвечаете, что кэширование только общедоступного контента все еще в силе?

Обновление 2

Я откопал источник и обнаружил, что стандартный атрибут AuthorizeAttribute действительно имеет нестатический обратный вызов проверки. Вот выдержка из OnAuthorization :

if (AuthorizeCore(filterContext.HttpContext)) {
    // ** IMPORTANT **
    // 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.

    HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache;
    cachePolicy.SetProxyMaxAge(new TimeSpan(0));
    cachePolicy.AddValidationCallback(CacheValidateHandler, null /* data */);
}
else {
    HandleUnauthorizedRequest(filterContext);
}

CacheValidationHandler делегирует проверку кеша защищенному виртуальному HttpValidationStatus OnCacheAuthorization (HttpContextBase) , который, конечно, не статичен. Одна из причин, по которой он не статичен, заключается в том, что, как отмечено в ВАЖНОМ комментарии выше, он вызывает защищенный виртуальный логический объект AuthorizeCore (HttpContextBase) .

Для выполнения любой логики AuthorizeCore из метода обратного вызова проверки статического кэша необходимо знать свойства Users и Roles экземпляра AuthorizeAttribute. Однако, похоже, нет простого способа подключения. Мне пришлось бы переопределить OnAuthorization, чтобы поместить эти 2 значения в HttpContext (коллекцию Items?), А затем переопределить OnCacheAuthorization, чтобы вернуть их. Но это грязно пахнет.

Если мы осторожно используем свойство VaryByCustom = "User" в атрибуте OutputCache, можем ли мы просто переопределить OnCacheAuthorization, чтобы всегда возвращать HttpValidationStatus.Действительный? Когда метод действия не имеет атрибута OutputCache, нам не нужно беспокоиться о том, что этот обратный вызов когда-либо будет вызван, верно? И если у нас есть атрибут OutputCache без VaryByCustom = "User", тогда должно быть очевидно, что страница может возвращать любую кешированную версию, независимо от того, какой пользовательский запрос создал кешированную копию. Насколько это рискованно?

19
задан danludwig 6 January 2012 в 21:17
поделиться