C# 如何在ASP.NETMVC5.1Web应用程序的实体框架中使用哈希ID,以避免数据库中记录的可预测链接
我有模型课:C# 如何在ASP.NETMVC5.1Web应用程序的实体框架中使用哈希ID,以避免数据库中记录的可预测链接,c#,asp.net-mvc,entity-framework,asp.net-mvc-5.1,C#,Asp.net Mvc,Entity Framework,Asp.net Mvc 5.1,我有模型课: public class Person { public int Id { get; set; } ... } 要查看某人的详细信息,用户可以猜测其id http://localhost:17697/Person/Details/2 它们只是连续的整数 我如何告诉实体框架洗牌这些ID以使其更难猜测?您可以使用HttpServerUtility.urltokencode和HttpServerUtility.urltokedecode Encode使用bas
public class Person {
public int Id { get; set; }
...
}
要查看某人的详细信息,用户可以猜测其id
http://localhost:17697/Person/Details/2
它们只是连续的整数
我如何告诉实体框架洗牌这些ID以使其更难猜测?您可以使用
HttpServerUtility.urltokencode
和HttpServerUtility.urltokedecode
Encode使用base64编码,但替换URL不友好的字符
在前面的问题中有一个类似的答案。见公认的答案。
就我个人而言,我在URL中使用slug,而不是ID。比如:
http://localhost:17697/Person/Details/john-doe
然后根据段塞拉动对象:
db.People.SingleOrDefault(m => m.Slug == slug);
然而,“默默无闻的安全”并不是一个好的游戏计划。让ID“更难猜测”,并不能解决人们访问它的问题,因为谁不应该访问它。如果应该保护详细信息,则实施身份验证并为操作指定授权策略。如果不需要可预测的ID,则可以使用
Guid
而不是int
。“洗牌”会使过程过于复杂,而且不会给你任何保护
请记住,如果您试图保护url,请使用授权和筛选器编写适当的安全性。通过默默无闻实现的安全性实际上并不能保证任何晚到派对的安全性,但由于ASP.NET MVC中没有太多关于使用HashID的内容,因此我将使用自定义
ModelBinder
和BaseModel
类与大家分享我的解决方案。结束路由类似于/example/voQ/details
首先,您需要一个模型,您现有的模型可以从中扩展并生成一个HashId
public class abstract BaseModel
{
private static readonly Hashids __hashId = new Hashids("seed", 2);
public Id { get; set; }
[NotMapped]
public HashId
{
get { return BaseModel.__hashId.Encode(this.Id); }
}
}
活页夹需要在每个型号的Global.asax
中注册:
ModelBinders.Binders.Add(typeof(ExampleModel), new ControllerModelBinder<ExampleModel>());
设置链接是相同的,但是您需要使用BaseModel
中的HashId
属性,而不是传递Id
@Url.Action("Details", new { id = item.HashId })
最后,模型绑定器:
public class ControllerModelBinder<T> : DefaultModelBinder
where T : BaseModel
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
if (bindingContext.ModelType == typeof(T))
{
string hash = bindingContext.ValueProvider.GetValue("id").RawValue.ToString();
if (!string.IsNullOrWhiteSpace(hash))
{
int id = HashIdHelper.ToInt(hash);
if (id > 0)
{
using (ApplicationContext context = new ApplicationContext())
{
DbRawSqlQuery<T> query = context.Database.SqlQuery<T>(string.Format("SELECT * FROM {0} WHERE id = @Id LIMIT 1", EntityHelper.GetTableName<T>(context)), new MySqlParameter("@Id", id));
try
{
T model = query.Cast<T>().FirstOrDefault();
if (model != null)
{
return model;
}
}
catch (Exception ex)
{
if (ex is ArgumentNullException || ex is InvalidCastException)
{
return base.BindModel(controllerContext, bindingContext);
}
throw;
}
}
}
}
}
return base.BindModel(controllerContext, bindingContext);
}
}
公共类控制器ModelBinder:DefaultModelBinder
其中T:BaseModel
{
公共重写对象BindModel(ControllerContext ControllerContext,ModelBindingContext bindingContext)
{
if(bindingContext.ModelType==typeof(T))
{
string hash=bindingContext.ValueProvider.GetValue(“id”).RawValue.ToString();
如果(!string.IsNullOrWhiteSpace(哈希))
{
int id=hashidheloper.ToInt(散列);
如果(id>0)
{
使用(ApplicationContext上下文=新的ApplicationContext())
{
DbRawSqlQuery query=context.Database.SqlQuery(string.Format(“SELECT*FROM{0},其中id=@id LIMIT 1”,EntityHelper.GetTableName(context)),新的MySqlParameter(“@id”,id));
尝试
{
T model=query.Cast().FirstOrDefault();
如果(型号!=null)
{
收益模型;
}
}
捕获(例外情况除外)
{
如果(ex为ArgumentNullException | | ex为InvalidCastException)
{
返回base.BindModel(controllerContext、bindingContext);
}
投掷;
}
}
}
}
}
返回base.BindModel(controllerContext、bindingContext);
}
}
如果您不想要可预测的ID,请使用guid。这也提出了一个问题,为什么您不想要可预测的ID。您试图阻止什么,因为主服务器的唯一要求是它是唯一的。授权完成后,用户将能够了解个人的详细信息,我只是想让用户更难记住ID。您能演示如何在控制器和视图中使用它吗?
public class ControllerModelBinder<T> : DefaultModelBinder
where T : BaseModel
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
if (bindingContext.ModelType == typeof(T))
{
string hash = bindingContext.ValueProvider.GetValue("id").RawValue.ToString();
if (!string.IsNullOrWhiteSpace(hash))
{
int id = HashIdHelper.ToInt(hash);
if (id > 0)
{
using (ApplicationContext context = new ApplicationContext())
{
DbRawSqlQuery<T> query = context.Database.SqlQuery<T>(string.Format("SELECT * FROM {0} WHERE id = @Id LIMIT 1", EntityHelper.GetTableName<T>(context)), new MySqlParameter("@Id", id));
try
{
T model = query.Cast<T>().FirstOrDefault();
if (model != null)
{
return model;
}
}
catch (Exception ex)
{
if (ex is ArgumentNullException || ex is InvalidCastException)
{
return base.BindModel(controllerContext, bindingContext);
}
throw;
}
}
}
}
}
return base.BindModel(controllerContext, bindingContext);
}
}