3

ユーザーがログインしてデータを管理できる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 から関連データを引き出しているため、アクション メソッドのパラメーターにアクセスできるようにすると、はるかにクリーンになります。

カスタム アクション フィルター (詳細はこちら)からアクション メソッドのパラメーターにアクセスできることはわかっていますが、データ承認コードを承認フィルターに配置するべきではありませんか? Authorize フィルターの例が増えれば増えるほど、役割を処理するためだけに意図されているという印象を受けます。

私の主な質問は、カスタム Authorize 属性からアクション メソッドのパラメーターにアクセスするにはどうすればよいですか?

4

1 に答える 1

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後続のメソッドの状態を保持するのではなく、1 つのメソッドのみをオーバーライドするだけでよいため、使用がより簡単になります。

于 2014-07-04T14:09:02.220 に答える