【问题标题】:Asp.net mvc How to prevent browser from calling an action method?Asp.net mvc 如何防止浏览器调用action方法?
【发布时间】:2012-02-23 04:28:25
【问题描述】:

我的控制器 (shoppingCartController) 中有两个操作

    public ActionResult Index()
    {
        //some stuff here
        return View(viewModel);
    }


    public ActionResult AddToCart(int id)
    {

        return RedirectToAction("Index");

    }

有没有办法阻止用户通过在浏览器中输入 url 直接调用 index 动作?

例如:如果用户浏览到 shoppingCart/index 被重定向到 Home/Index。

【问题讨论】:

    标签: asp.net-mvc-3


    【解决方案1】:

    您可以在您的操作方法上使用[ChildActionOnly] 属性来确保它不会被直接调用,或者在您的操作中使用ControllerContext.IsChildAction 属性来确定您是否要重定向。

    例如:

    public ActionResult Index()
    {
        if(!ControllerContext.IsChildAction)
        {
           //perform redirect here
        }
    
        //some stuff here
        return View(viewModel);
    }
    

    如果您不能将 Index 操作设为子操作,您可以随时检查引荐来源网址,了解它不是万无一失的并且可能被欺骗。见:

    How do I get the referrer URL in an ASP.NET MVC action?

    【讨论】:

    • 如果我将 [ChildActionOnly] 放在我的索引方法上,它不会从 redirectToAction 调用。只有当我有 html.action() 时它才有效。 controllerContext.ischildAction 也不会将 redirecttoaction 识别为 childaction 调用。
    • 如果你想要一个像常规动作一样工作的动作(即你可以 RedirectToAction 到它)但只能从一个地方重定向到,你可以尝试检查动作中的引用者,重定向到如果引用者不是 AddToCart url,则为 home/index。不过,这并不是万无一失的,因为引用者可能会被欺骗。
    • 我要检查响应状态码。我应该如何检查推荐人?
    • 我检查了 Request.UrlReferrer 它没有显示它来自哪个操作它显示了它来自哪个 url。
    【解决方案2】:

    尝试将此索引控制器操作设为private。具有private 访问修饰符的方法不应从类外部访问。

    然后,比从 AddToCart 调用 RedirectToAction 更简单的方法,如下所示:

    private ActionResult Index()
    {
        //some stuff here
        return View(viewModel);
    }
    
    
    public ActionResult AddToCart(int id)
    {
    
        return Index();
    
    }
    

    【讨论】:

    • 但它不会将用户重定向到主页/索引。
    • 所以如果用户明确输入url shoppingCart/Index,它并不能解决重定向用户的问题,它只是给出了找不到资源的一些错误
    • 好的。您在 Index 中拥有的应该是一些应该从 AddToCart 调用的辅助方法。并且实际索引方法应该是公开的,并且应该重定向到 Home/Index。
    • 我建议使用internal 而不是private。这样它仍然可以被测试项目访问。
    【解决方案3】:

    如果您担心的只是用户输入 URL,那么使用 HttpPost 属性应该可以防止您的操作被这样调用:-

    [HttpPost]
    public ActionResult AddToCart(int id)
    {
    

    这可以防止 GET 请求调用该操作。但是,它不会阻止有人编写虚拟表单并发布到您的操作。

    如果您担心一些更恶意的东西,您可能想要实施某种形式的防伪令牌,here 上有一些很好的信息。

    编辑

    好的,所以重新阅读上面的问题并不能完全解决您的问题。

    路线怎么样?如果您有类似下面的内容,它将阻止调用 ShoppingCart/Index 并将用户重定向到您的站点索引。

            routes.MapRoute(
                "ShoppingCartIndex",
                "ShoppingCart/Index",
                new { controller = "Home", action = "Index" }
            );
    

    【讨论】:

    • 我认为他想要保护的不是 AddToCart 操作,而是 Index 操作,它是一个 GET。
    • 但你是对的,AddToCart 操作肯定需要是一个 POST。 :-)
    • 不用担心用户输入 example.com/shoppingcart 会看到空的 shoppingCart。
    • 如果您担心的只是一个空的购物车页面,为什么不在索引操作中检查购物车内容,如果它是空的则重定向到首页?
    • 如果是关于购物车是空的,您能否检查 ShoppingCart 控制器的 Index 操作中的商品数量,如果数量为零则重定向回来?
    【解决方案4】:

    如果启用了 SessionState,您可以使用控制器的TempData 来实现您的目标。在 AddToCart action 中设置 TempData,只有 Index action 可以获取 AddToCart action 中设置的 TempData key 时才显示 Index 视图。

    如果您需要对多个操作/项目使用此功能,请结合使用自定义操作过滤器和操作结果。像这样:

    // Controller
    [PreventDirectAccess]
    public ActionResult Index()
    {
        //some stuff here
        return View(viewModel);
    }
    
    public ActionResult AddToCart(int id)
    {
        return new PreventDirectAccessRedirectToRouteResult(new RouteValueDictionary
        {
            {"action", "Index"}
        });
    }
    
    // Filter
    public class PreventDirectAccessAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if (filterContext == null)
                throw new ArgumentNullException("filterContext");
    
            if (filterContext.Controller.TempData[PreventDirectAccessRedirectToRouteResult.Executed] == null)
                filterContext.Result = new HttpNotFoundResult();
    
            base.OnActionExecuting(filterContext);
        }
    }
    
    // ActionResult
    public class PreventDirectAccessRedirectToRouteResult : RedirectToRouteResult
    {
        public const string Executed = "PreventDirectAccessRedirectExecuted";
    
        public override void ExecuteResult(ControllerContext context)
        {
            context.Controller.TempData[Executed] = true;
            base.ExecuteResult(context);
        }
    }
    

    【讨论】:

      【解决方案5】:

      NonAction 是要使用的属性。它是 MVC 库的一部分,因此您不必创建自己的属性。

      【讨论】:

      • 我从这篇文章中理解了 NonAction:tutorialspoint.com/…(不幸的是,msdn 链接没用);感谢分享这样一个复活节彩蛋!
      【解决方案6】:

      这里是编写代码如何防止浏览器直接访问到操作方法: 在 FilterConfig.cs

      中编写以下代码
      [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
      public class NoDirectAccessAttribute : ActionFilterAttribute
      {
          public override void OnActionExecuting(ActionExecutingContext filterContext)
          {
              if (filterContext.HttpContext.Request.UrlReferrer == null ||
                          filterContext.HttpContext.Request.Url.Host != filterContext.HttpContext.Request.UrlReferrer.Host)
                  {
                  filterContext.Result = new RedirectToRouteResult(new
                                 RouteValueDictionary(new { controller = "Home", action = "Index", area = "" })); 
              }
          }
      }
      

      现在将此代码应用于您的操作方法

      [NoDirectAccess]
      public ActionResult MyActionMethod()
      

      这将限制直接调用任何类动作方法

      【讨论】:

        【解决方案7】:

        这是未经测试的,但我相信您可以使用 Validate Anti Forgery Token

        在页面上的代码中,您需要在要发布的表单中放置一个验证令牌:

        @Html.AntiForgeryToken()
        

        产生:

        <input name="__RequestVerificationToken" type="hidden" value="s9+jDREFMlNPkAT2zOlmhJZQbbDOzMhuarSTG1BVAC4GeHiNL5VtuQo7CQTF8obw8hEYIQac9YaQh+qVcF0xj0eNO7lVdezz+JxuSKGQo2d2gEdtkEdR+XTTFas4Gh6fjSYc7A1rWF8AAhxjZ9j6GlbRhECZOPAlPAItnjz49QQ=" />
        

        具有此属性的任何操作都会自动获取此令牌:

        [ValidateAuthenticationToken]
        public ActionResult AddToCart(int id)
        {
           return Index();
        }
        

        如果请求是直接的,则会发生错误。

        【讨论】:

        • 我喜欢这个主意,但是如果我的这个链接不在表单中怎么办?
        • @retslig - 如果您的意思是标准 GET 请求,then you can't use this method
        【解决方案8】:

        你也可以使用子动作属性。只是分享:)

        public class MyController {
        
          [ChildActionOnly]
          public ActionResult Menu() {
            var menu = GetMenuFromDB();
              return PartialView(menu);
          }
        
        }
        

        【讨论】:

          猜你喜欢
          • 2019-08-03
          • 1970-01-01
          • 1970-01-01
          • 2011-08-21
          • 1970-01-01
          • 1970-01-01
          • 2013-12-20
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多