c# - Web API 设计问题 - 阻止用户使用 PUT/PATCH 更新用户 ID
问题描述
我遇到了一些设计问题,如何防止用户更新发布到 API 的数据中的用户 ID 字段。我正在使用 ASP.Net Core 2.1 和 Entity Framework Core。此 API 将与 Web 应用程序一起使用(我也将构建它),并且这两个应用程序都需要身份验证以及策略授权。
我有一个事件类,它引用了创建它的用户(用户 id + 用户到我的用户实体的导航属性)。每当使用 PUT/PATCH/DELETE 调用 API 时,我都希望防止用户能够更新用户 ID 字段并删除其他用户事件。
可以使用所有 HTTP 动词调用 API,例如api/events
列出所有事件的 GET 请求或api/events
创建事件的 POST。
我的活动实体:
public Guid Id { get; set; }
public string Title { get; set; }
public string Message { get; set; }
public string Url { get; set; }
public DateTime Created { get; set; }
public DateTime ScheduledTime { get; set; }
public Guid UserId { get; set; }
public User CreatedBy { get; set; }
事件更新数据模型(用于 PUT/PATCH 请求):
public string Title { get; set; }
public string Message { get; set; }
public string Url { get; set; }
public DateTime? ScheduledTime { get; set; }
我的用户实体
public Guid Id { get; set; }
public string Email { get; set; }
public List<Event> Events { get; set; } = new List<Event>();
我的控制器的路由非常简单: [Route("api/[controller]")]
所以它通过api/events
.
目前我采用的方法是将用户 ID 显式传递给 API。在操作中,我从数据库中获取事件,将传入的用户 ID 的值与从数据库中获取的用户 ID 进行比较。如果它相同,我允许更新发生。如果它不同,我会返回 403 禁止以及错误消息。
即我在 API 中的更新操作:
[HttpPut("{id}")]
public async Task<IActionResult> UpdateEvent([FromRoute] Guid id, [FromQuery] Guid userId,[FromBody] EventUpdateDto eventToUpdate)
{
//compare userId passed to action by fetching event from database and compare values
//return 403 if not equal else continue
}
问题是我同时使用路由参数和查询字符串参数。我可以简化路线,{eventid}/{userid}
但我不知道这是否是最佳做法。这意味着我正在调用我的 API 来更新特定事件,然后还要钻入一个没有多大意义的特定用户。我也不想api/{userid}/events/{eventid}
为我的控制器采取另一种方式,因为我想访问所有用户的所有事件或我的 Web 应用程序中的单个事件 - 我不希望它仅限于拉动单个事件用户身份。
我也有与 DELETE 相同的问题,因为我不希望用户删除其他用户的帖子。所以它的设置如下:
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteEvent([FromRoute] Guid id, [FromQuery] Guid userId)
请注意,我的更新模型不包含 UserId 只是因为我想防止它被覆盖。我想出了 3 种不同的方法来解决这个问题,我想知道哪一种是正确的方法,以及是否有更好的设计。
1)从 API 中删除任何对用户 ID 验证的检查,并将逻辑放入将调用 API 的 Web 应用程序中。使用这种方法,每次用户尝试在应用程序中编辑事件时,我首先必须从 API 获取事件,将当前用户 ID 与从 API 获取的用户 ID 进行比较,然后让他们继续或抛出错误。我对此的唯一问题是必须进行一次额外的 API 之旅而不是一次(首先获取事件,然后可能发布事件)。使用这种方法,我的事件更新模型可以安全地在其中包含 userid 字段,并且我可以在没有查询字符串参数的情况下简化我的 API 调用。
2)稍微修改我上面的方法,不使用查询字符串参数,只使用路由参数。与其调用api/events/1?id=1
,不如将其修改为api/events/1/1
第一个参数是 eventId,第二个参数是 userId。我觉得在应用程序中创建这些类型的 URL 会更容易,但会偏离正确的 REST 实践。
3)保持我目前的做法。
由于可能的性能问题以及希望拥有一个自包含的 API 以便可以将其插入其他应用程序而无需在 Web 应用程序中编写复杂的逻辑,我对方法 1 有点犹豫。一种方法比另一种更好吗?有没有更好的方法来设计我的外向模型?对设计的任何帮助都会非常有帮助。
解决方案
推荐阅读
- python - 如何在 Django 分页中加载 Stripe 对象
- javascript - 在另一种方法上过滤方法?
- verilog - 如何实现超过一个时钟周期的时序逻辑?
- javascript - chrome devtool网络面板中status=Finished的含义?是什么原因造成的?
- ios - 如何在swift中使用Alamofire从API中提取数据时修复无效的json问题?
- apache-spark - 如何设置火花结构化流的最小输出文件大小?
- c# - XAML 无法识别代码隐藏中的代码
- javascript - 按钮不显示
- ios - 在后台保持 WKWebView 页面处于活动状态
- shopify - 如何检查应用程序是否安装到我的 Shopify 商店