我创建了下面的过滤器,以限制仅登录的用户访问我的应用程序的各个部分。但是,我的应用程序仍然抱怨在筛选器触发重定向之前尚未实例化用户对象。如何在操作方法有机会注意到对象为 null 之前使重定向将用户踢出?
上下文
为了完整起见,值得一提的是:
UserSession.CurrentOrDefault();
如果找到存储在当前会话中的值,则返回对象;如果会话不存在,则返回 null。
过滤器
public class RestrictAccess : ActionFilterAttribute
{
public UserRole RequiredRole { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var userSession = UserSession.CurrentOrDefault();
if(userSession != null)
{
int userRole = Convert.ToInt32(userSession.User.Role);
int requiredRole = Convert.ToInt32(this.RequiredRole);
if(userRole >= requiredRole)
{
base.OnActionExecuting(filterContext);
return;
}
else
{
HttpContext.Current.Response.Redirect("/");
return;
}
}
HttpContext.Current.Response.Redirect("/Session/Create");
}
}
抱怨的示例操作方法:
[RestrictAccess]
public ActionResult Index()
{
var userSession = UserSession.CurrentOrDefault();
// This is the part that throws the exception. userSession.User is null here.
// My expectation was for this to be unreachable if user is null because of the filter.
var model = new IndexViewModel { User = userSession.User };
return View(model);
}
为此实现自己的AuthorizeAttribute
public class Authorization : AuthorizeAttribute
{
public UserRole RequiredRole { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var userSession = UserSession.CurrentOrDefault();
if(userSession != null)
{
int userRole = Convert.ToInt32(userSession.User.Role);
int requiredRole = Convert.ToInt32(this.RequiredRole);
if(userRole >= requiredRole)
{
return true;
}
else
{
return false;
}
}
return false;
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
try
{
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
{
filterContext.Result = new RedirectResult("/Session/Create");
}
}
catch (Exception)
{
filterContext.Result = new RedirectResult("/Session/Create");
}
}
private void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus)
{
validationStatus = OnCacheAuthorization(new HttpContextWrapper(context));
}
}
当你调用HttpContext.Current.Response.Redirect
时,你正在进入MVC架构之外,所以它不知道不运行操作方法,所以继续和错误。
相反,您应该将ActionExecutingContext filterContext
的Result
设置为如下所示的RedirectResult
:
public class RestrictAccess : ActionFilterAttribute
{
public UserRole RequiredRole { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var userSession = UserSession.CurrentOrDefault();
if(userSession != null)
{
int userRole = Convert.ToInt32(userSession.User.Role);
int requiredRole = Convert.ToInt32(this.RequiredRole);
if(userRole >= requiredRole)
{
base.OnActionExecuting(filterContext);
return;
}
else
{
filterContext.Result = new RedirectResult("/");
return;
}
}
filterContext.Result = new RedirectResult("/Session/Create");
}
}