我正在编写一个MVC 3应用程序,用户可以登录并管理他们的数据。我想阻止用户查看或篡改其他用户的数据。我的第一直觉就是在每个动作方法中验证对相关对象的访问,如下所示:
public ActionResult ShowDetails(int objectId)
{
DetailObject detail = _repo.GetById(objectId);
if (detail.User.UserID != (Guid)Membership.GetUser().ProviderUserKey)
{
return RedirectToAction("LogOff", "Account");
}
}
这很好用,但我认为将对象授权代码放入从AuthorizeAttribute派生的自定义Authorize属性可能会更好,然后我可以将其应用于控制器。不幸的是,我无法找到从我的自定义Authorize属性中访问操作方法参数的方法。相反,我发现访问传入objectId的唯一方法是检查httpContext.Request或filterContext.RequestContext.RouteData.Values:
public class MyAuthorizeAttribute : AuthorizeAttribute
{
private int _objectId = 0;
private IUnitOfWork _unitOfWork;
public MyAuthorizeAttribute(IUnitOfWork uow)
{
_unitOfWork = uow;
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
int.TryParse((string) filterContext.RequestContext.RouteData.Values["id"], out _objectId);
base.OnAuthorization(filterContext);
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
int objectId = 0;
if (httpContext.Request.Params.AllKeys.Contains("id", StringComparer.InvariantCultureIgnoreCase))
{
int.TryParse(httpContext.Request[idKey], out objectId);
}
if (objectId != 0)
{
if (!IsAuthorized(objectId, httpContext.User.Identity.Name))
{
return false;
}
}
if (_objectId != 0)
{
if (!IsAuthorized(objectId, httpContext.User.Identity.Name))
{
return false;
}
}
return base.AuthorizeCore(httpContext);
}
private bool IsAuthorized(int objectId, string userName)
{
DetailObject detail;
detail = _unitOfWork.ObjectRepository.GetById(objectId);
if (detail == null)
{
return false;
}
if (userName != detail.User.UserName)
{
return false;
}
return true;
}
}
我发现这种方法非常笨重。我真的不想在RouteData或Request对象中找到它们;能够访问动作方法参数会更加清晰,因为模型绑定已经从RouteData和Request中提取了相关数据。
我知道我可以从自定义操作过滤器(详细here)访问操作方法参数,但是不应该将数据授权代码放在授权过滤器中吗?我看到的授权过滤器的例子越多,我就越能得到它们仅用于处理角色的印象。
我的主要问题是:如何从我的自定义授权属性访问操作方法参数?
答案 0 :(得分:3)
回答你的主要问题:不,遗憾的是AuthorizationContext
无法提供对行动参数的访问权。
首先,您可以使用ValueProvider
来处理id是路由的一部分还是查询参数或HTTP发布,如下所示:
public override void OnAuthorization(AuthorizationContext filterContext)
{
string id = filterContext.Controller.ValueProvider.GetValue("id").AttemptedValue;
...
}
这适用于简单的数据类型,并且引入的开销很小。但是,一旦开始为操作参数使用自定义模型绑定器,就必须从ActionFilterAttribute
继承过滤器以避免双重绑定:
[MyFilter]
public ActionResult MyAction([ModelBinder(typeof(MyModelBinder))] MyModel model)
{
...
}
public class MyFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var model = filterContext.ActionParameters["model"] as MyModel;
...
}
}
虽然出于授权目的从语义继承AuthorizeAttribute
听起来更好,但没有其他理由这样做。此外,我发现使用ActionFilterAttribute
更容易,因为您只需要覆盖一个方法,而不是为后续方法保留状态。