C# 将参数从视图传递到控制器并作为视图返回的安全方法

C# 将参数从视图传递到控制器并作为视图返回的安全方法,c#,asp.net-mvc,parameter-passing,C#,Asp.net Mvc,Parameter Passing,我正在为querystring寻找一种安全的替代方法,我将把一个参数传递给同一控制器上的控制器方法,并将其作为视图返回 类别视图: @foreach (Project.Framework.ModelClasses.productCategories productCategories in ViewBag.Categories) { @Html.ActionLink(productCategories.description, "Products",

我正在为querystring寻找一种安全的替代方法,我将把一个参数传递给同一控制器上的控制器方法,并将其作为视图返回

类别视图:

@foreach (Project.Framework.ModelClasses.productCategories productCategories in ViewBag.Categories)
    {           
        @Html.ActionLink(productCategories.description, "Products", "Shop", new { id = "", categoryID = productCategories.categoryID, description = productCategories.description }, null)
    }

控制器方法:

public ActionResult Products(string categoryID, string keyword, string description)
    {
        ViewBag.appPath = ConfigurationManager.AppSettings["appPath"].ToString();
        Methods Methods = new Methods();
        Methods.tokenHeader = (string)Session["token"];
        Methods.cookieContainer = (CookieContainer)Session["cookies"];

        Response shopnowProductsResponse = Methods.shopnowProductsGet(categoryID, keyword);
        if (shopnowProductsResponse.Code == "000")
        {
            List<product> products = new List<product>();
            products = (List<product>)shopnowProductsResponse.Data;

            string FPRFlag = "0";
            foreach (product product in products)
            {
                if (FPRFlag != "1")
                {
                    if (product.FPRFlag == "1")
                    {
                        FPRFlag = product.FPRFlag;
                    }
                }
                else
                {
                    goto nextState;
                }
            }
        nextState:

            ViewBag.FPRFlag = FPRFlag;
            ViewBag.Description = description;
            ViewBag.Products = products;
        }

        return View();
    }
公共ActionResult产品(字符串类别ID、字符串关键字、字符串描述) { ViewBag.appPath=ConfigurationManager.AppSettings[“appPath”].ToString(); 方法=新方法(); Methods.tokenHeader=(字符串)会话[“token”]; Methods.cookieContainer=(cookieContainer)会话[“cookies”]; 响应shopnowProductsResponse=Methods.shopnowProductsGet(categoryID,关键字); if(shopnowProductsResponse.Code==“000”) { 列表产品=新列表(); 产品=(列表)shopnowProductsResponse.Data; 字符串FPRFlag=“0”; foreach(产品中的产品) { 如果(FPRFlag!=“1”) { 如果(product.FPRFlag==“1”) { FPRFlag=product.FPRFlag; } } 其他的 { 后藤下州; } } 下一州: ViewBag.FPRFlag=FPRFlag; ViewBag.Description=说明; ViewBag.Products=产品; } 返回视图(); }
注意:关键字是可选的

假设您只想以最终用户无法从URL中获取所有产品数据的方式对这些链接进行编码,您可以通过在链接中使用替代代码来实现这一点。我会使用散列值:

@foreach (Project.Framework.ModelClasses.productCategories category in ViewBag.Categories)
{
    @Html.ActionLink(category.description, "Products", "Shop", 
        new { 
            hc = category.categoryID.GetHashCode() 
        })
}
然后在控制器上查找哈希代码,而不是
类别ID

这仍然存在两个问题:

  • 哈希代码将显示在浏览器的地址栏上
  • 哈希代码列表每次都是相同的
  • 第一个可以通过中间操作修复,该操作将哈希代码转换回
    类别ID
    ,并将结果存储在
    会话
    存储中,然后重定向到
    产品
    页面:

    public ActionResult SetCategory(int hc)
    {
        string catID = HashToCategoryID(hc);
        Session["categoryID"] = catID;
        return RedirectToAction("Products");
    }
    
    这不仅会将用户的浏览器放回
    产品
    页面,而且(在大多数情况下)还会确保
    设置类别
    URL甚至不会显示在浏览器历史记录中。一般来说,浏览器历史记录甚至不会显示它首先从
    产品
    页面导航出去,因此点击
    后退
    按钮将把它们从页面带到它们第一次进入之前所在的位置

    另一个问题-哈希代码不改变-可以通过两种方式解决:使用一个在每次访问时都会改变的值(如会话ID)来添加哈希值,或者生成随机值并将其存储在
    会话
    存储器中进行查找

    例如:

    // after loading your products:
    
    // something to salt the hash codes with:
    string salt = (DateTime.Now - DateTime.Today).TotalMilliseconds.ToString();
    
    // generate hash codes for all the product categories
    var hashcodes = products.Select(p => new { h = (salt + p.categoryID).HashCode(), c = p.categoryID });
    
    // create hash->categoryID dictionary and save in Session
    var hashtocat = hashcodes.ToDictionary(hc => hc.h, hc => hc.c);
    Session["HashToCategory"] = hashtocat;
    
    // create categoryID->hash dictionary and save in ViewBag
    var cattohash = hashcodes.ToDictionary(hc => hc.c, hc => hc.h);
    ViewBag["CategoryToHash"] = cattohash; 
    
    现在,每次加载页面时,都会得到一组不同的哈希值。然后,您的链接生成将变为:

    @{ // grab hashcode dictionary from ViewBag
        Dictionary<string, int> hashcodes = ViewBag["CategoryToHash"] as Dictionary<string, int>;
    }
    
    @foreach (Project.Framework.ModelClasses.productCategories category in ViewBag.Categories)
    {
        @Html.ActionLink(product.description, "SetCategory", new { hc = hashcodes[product.categoryID] })
    }
    
    @{//从ViewBag中获取hashcode字典
    Dictionary hashcodes=ViewBag[“CategoryToHash”]作为字典;
    }
    @foreach(ViewBag.Categories中的Project.Framework.ModelClasses.productCategories类别)
    {
    @ActionLink(product.description,“SetCategory”,新的{hc=hashcodes[product.categoryID]})
    }
    
    实际上,您现在有一个过期的一次性代码列表,每次用户重新访问
    产品
    页面时,这些代码都会被替换为新代码。让同一代码在同一类别中多次运行的几率是微乎其微的,除非在同一会话中单击链接,否则链接将失败


    说到失败。。。您需要在其中添加一系列错误处理。

    您在问题中使用的“安全”是什么意思?(也许数据签名可以解决这个问题?)?通过将其作为路由的一部分?出于安全原因,我无法通过URL传递任何参数,因为我正在传递敏感数据。请定义“安全”。您需要对用户隐藏它吗?对监视交通的人隐瞒?您是否需要能够进行完整性检查?”“安全”意味着很多事情。如果它是敏感数据,那么您应该将其保存在服务器端并将其置于会话中。根据定义,发送到客户端(浏览器)的任何内容都是不安全的。您当然可以对其进行加密,但既然可以将其保存在服务器端,为什么还要经历所有这些麻烦呢。这就是我在项目中所做的,但没有使用哈希。谢谢你的回答。现在我在想,浏览器中的用户是否有可能进入这个方法。如果你为每个页面加载生成哈希或随机键,那么你就相当安全了。他们最多只能列出为当前页面生成的哈希值/键,但这不会给他们提供关于实际数据结构的太多信息。