使用Windows Azure Microsoft.Web.DistributedCache.DistributedCacheOutputCacheProvider
作为MVC3应用程序的outputCache提供程序。以下是相关的操作方法:
[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();
}
从Web浏览器加载此视图时,出现以下异常:
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?
在Evan的回答之后,我在IIS Express(Azure之外)中测试了上述操作方法。这是我对OutputCache属性上的VaryByCustom =“User”属性的覆盖:
public override string GetVaryByCustomString(HttpContext context, string custom)
{
return "User".Equals(custom, StringComparison.OrdinalIgnoreCase)
? Thread.CurrentPrincipal.Identity.Name
: base.GetVaryByCustomString(context, custom);
}
当我以me@mydomain.tld的身份访问示例缓存页面时,页面的输出被缓存,并且视图显示“此页面缓存在2011年12月 31日11:06:12 (UTC)”。如果我然后注销并登录为another@otherdomain.tld和访问该页面时,会显示“此页在12/31/2011 11:06缓存:38 AM(UTC)”。以me@mydomain.tld身份重新登录并重新访问该页面会导致缓存再次显示“此页面缓存在2011年12月 31日11:06:12 (UTC)”。进一步的登录/注销尝试显示,不同的输出将被缓存并返回,具体取决于用户。
这导致我相信输出是基于用户单独缓存的,这是我的VaryByCustom =“User”设置和覆盖的意图。问题是它不适用于Azure的分布式缓存提供程序。埃文,你回答只是缓存公共内容仍然存在?
我挖掘了源代码,并发现开箱即用的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
将缓存验证委托给protected virtual HttpValidationStatus OnCacheAuthorization(HttpContextBase)
当然不是静态的。为什么它不是静态的一个原因是,正如在上面的重要评论中指出的那样,它会调用protected virtual bool AuthorizeCore(HttpContextBase)
。
为了从静态缓存验证回调方法执行任何AuthorizeCore逻辑,它需要知道AuthorizeAttribute实例的用户和角色属性。但是,似乎并没有简单的插件方式。我必须重写OnAuthorization才能将这两个值放入HttpContext(Items集合?),然后重写OnCacheAuthorization以将其取消。但是那味道很脏。
如果我们小心地使用OutputCache属性中的VaryByCustom =“User”属性,我们是否可以重写OnCacheAuthorization以始终返回HttpValidationStatus.Valid?当action方法没有OutputCache属性时,我们不需要担心这个回调会被调用,是否正确?如果我们确实有一个没有VaryByCustom =“User”的OutputCache属性,那么显然页面可以返回任何缓存版本,而不管哪个用户请求创建了缓存副本。这有多危险?
发布于 2018-03-21 10:43:10
缓存发生在Action之前。可能需要自定义授权机制来处理缓存方案。
我认为可以帮助你的部分是一种自定义的Authorize Attribute,它的OnAuthorize()
方法处理缓存。
下面是一个代码块,例如:
/// <summary>
/// Uses injected authorization service to determine if the session user
/// has necessary role privileges.
/// </summary>
/// <remarks>As authorization code runs at the action level, after the
/// caching module, our authorization code is hooked into the caching
/// mechanics, to ensure unauthorized users are not served up a
/// prior-authorized page.
/// Note: Special thanks to TheCloudlessSky on StackOverflow.
/// </remarks>
public void OnAuthorization(AuthorizationContext filterContext)
{
// User must be authenticated and Session not be null
if (!filterContext.HttpContext.User.Identity.IsAuthenticated || filterContext.HttpContext.Session == null)
HandleUnauthorizedRequest(filterContext);
else {
// if authorized, handle cache validation
if (_authorizationService.IsAuthorized((UserSessionInfoViewModel)filterContext.HttpContext.Session["user"], _authorizedRoles)) {
var cache = filterContext.HttpContext.Response.Cache;
cache.SetProxyMaxAge(new TimeSpan(0));
cache.AddValidationCallback((HttpContext context, object o, ref HttpValidationStatus status) => AuthorizeCache(context), null);
}
else
HandleUnauthorizedRequest(filterContext);
}
}
/// <summary>
/// Ensures that authorization is checked on cached pages.
/// </summary>
/// <param name="httpContext"></param>
/// <returns></returns>
public HttpValidationStatus AuthorizeCache(HttpContext httpContext)
{
if (httpContext.Session == null)
return HttpValidationStatus.Invalid;
return _authorizationService.IsAuthorized((UserSessionInfoViewModel) httpContext.Session["user"], _authorizedRoles)
? HttpValidationStatus.Valid
: HttpValidationStatus.IgnoreThisRequest;
}
https://stackoverflow.com/questions/-100004281
复制相似问题