基于UserId的MVC3路由异常处理

时间:2012-06-28 08:43:15

标签: asp.net-mvc-3 exception routing

如果move.UserId不等于currentUserId,则尝试构建异常,然后重定向到Action,如果move.UserId等于currentUserId则返回View。

请参阅此处的代码:

public ActionResult Details(int id)
    {
        MembershipUser currentUser = Membership.GetUser();
        Guid currentUserId = (Guid)currentUser.ProviderUserKey;
        Move move = db.Moves.Where(m => m.UserId == currentUserId)
            .FirstOrDefault();

            if (currentUser != null && currentUser.ProviderUserKey != null && currentUser.IsApproved)
            {
                if (move.UserId == currentUserId)
                {
                    return View(move);
                }
            }

        return RedirectToAction("Oops", new RouteValueDictionary(
 new { controller = "Account", action = "Oops", area = "", id = UrlParameter.Optional }));
    }

我想把它绑定到将带回Move /(int)的url,这样如果用户将其修改为返回move.UserId!= currentUserId的移动的(int),那么它们也会重定向。目前,他们可以修改Url以获取其他人的动作。

myController的

    public ViewResult Index()
    {
        if (User.Identity.IsAuthenticated)
        {
            MembershipUser currentUser = Membership.GetUser();
            Guid currentUserId = (Guid)currentUser.ProviderUserKey;
            if (currentUser != null && currentUser.ProviderUserKey != null && currentUser.IsApproved)
            {
                var results = db.Moves.Where(move => move.UserId == currentUserId)
                    .ToList();
                return View(results);
            }
        }
        return View(db.Moves.ToList());
    }

    [ClientValidation]
    public ActionResult Details(Move move)
    {
        return View(move);
    }

MyView的

@model MovinMyStuff.Domain.Entities.Move
@{
ViewBag.Title = "Details";
    }
<div>

    @Html.DisplayFor(model => model.StartCity),
    @Html.DisplayFor(model => model.StartState)
    @Html.DisplayFor(model => model.StartZip) -
    @Html.DisplayFor(model => model.EndCity),
    @Html.DisplayFor(model => model.EndState)
    @Html.DisplayFor(model => model.EndZip)
</div>
<fieldset>
    <div class="job-details">
    @Html.HiddenFor(model => model.MoveId)
    @Html.HiddenFor(model => model.UserId)
        <ul class="distance">
            <li>
                <div>
                    Distance</div>
            </li>
            <li>1,978.6 Miles</li>
        </ul>
        <ul class="address-wrapper">
            <li>
                <ul class="address from">
                    <li>
                        <div>
                            From</div>
                    </li>
                    <li><span>Address: </span>
                        @Html.DisplayFor(model => model.StartStreetNumber)
                        @Html.DisplayFor(model => model.StartStreetName)
                    </li>
    ...
    </fieldset>

2 个答案:

答案 0 :(得分:1)

要测试的第一件事是LINQ查询是否返回了Move。如果不是,则表示用户正在尝试显示不属于他的移动,因为在您的查询中,您有一个.Where子句限制当前用户仅移动。

现在你将得到一个NullReferenceException。

所以:

[Authorize]
public ActionResult Details(int id)
{
    MembershipUser currentUser = Membership.GetUser();
    Guid currentUserId = (Guid)currentUser.ProviderUserKey;
    Move move = db.Moves.Where(m => m.UserId == currentUserId && m.MoveId == id).FirstOrDefault();
    if (!currentUser.IsApproved || move == null)
    {
        // the user is trying to display a move that doesn't belong to him =>
        // redirect him or throw a 403 HTTP exception
        return RedirectToAction("Oops", new RouteValueDictionary(new { controller = "Account", action = "Oops", area = "", id = UrlParameter.Optional }));
    }

    // at this stage we can grant access to the user because we know
    // that he is authenticated, he is approved and that the move he is
    // trying to consult belongs to him
    return View(move);
}

显然,如果您需要在许多控制器操作中重复此逻辑,则值得编写自定义Authorize属性:

[EnsureUserAllowedToConsultMove]
public ActionResult Details(Move move)
{
    // at this stage we can grant access to the user because we know
    // that he is authenticated, he is approved and that the move he is
    // trying to consult belongs to him
    return View(move);
}

以下是此自定义Authorize属性的外观:

public class EnsureUserAllowedToConsultMoveAttribute : AuthorizeAttribute
{
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        var authorized = base.AuthorizeCore(httpContext);
        if (!authorized)
        {
            return false;
        }

        if (!httpContext.Request.RequestContext.RouteData.Values.ContainsKey("id"))
        {
            return false;
        }

        var id = (string)httpContext.Request.RequestContext.RouteData.Values["id"];
        var currentUser = Membership.GetUser();
        var currentUserId = (Guid)currentUser.ProviderUserKey;
        return db.Moves.Any(m => m.UserId == currentUserId && m.MoveId == id);
    }

    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        var values = new RouteValueDictionary(new
        {
            controller = "account",
            action = "oops",
            area = ""
        });
        filterContext.Result = new RedirectToRouteResult(values);
    }
}

现在,您已设法将关注点分开,并将授权逻辑外部化为自定义授权属性。您的控制器不再需要使用此类代码进行污染。

答案 1 :(得分:0)

您应该使用“授权”过滤器进行此类工作。请查看此更多信息ASP.NET MVC Custom Authorization