基于用户角色进行RESTful路由的API设计

6

我正在使用Flask-RESTful开发API,我的应用程序有三个角色。

  1. site_admin (站点管理员)
  2. department_admin (部门管理员)
  3. basic (基础用户)

对于任何给定的资源,返回的JSON对象根据每个角色有不同的键集。

例如,如果您以"site_admin"身份访问/orders,结果可能如下所示:

{
  "orders": [
    {"id": 1, "user": "foo", "paid": True,  "department": "A", "code": 456},
    {"id": 2, "user": "bar", "paid": False, "department": "A", "code": 567},
    {"id": 3, "user": "meh", "paid": False, "department": "B", "code": 678}
  ]
}

然而,如果您以“department_admin”的身份访问/orders,则结果可能如下所示:
{
  "orders": [
    {"id": 3, "user": "meh", "paid": False}
  ]
}

如果您使用/orders作为“基本”请求,那么返回的JSON响应将非常简单,如下所示:
{
  "orders": [
    {"id": 2, "paid": True}
  ]
}

如何用RESTful的方式实现这个功能?

我可以想到三种方法来实现它。

(1) 使用请求参数并根据该参数进行过滤:

class Orders(restful.Resource):
  def get(self):
    if request.args['role'] == 'site_admin':
      return admin_JSON_response()
    elif request.args['role'] == 'department_admin':
      return dept_admin_JSON_response()
    else:
      return basic_JSON_response()

api.add_resource(Orders, '/orders')

(2) 在 session 对象上进行过滤:

class Orders(restful.Resource):
  def get(self):
    if session['role'] == 'site_admin':
      return admin_JSON_response()
    elif session['role'] == 'department_admin':
      return dept_admin_JSON_response()
    else:
      return basic_JSON_response()

api.add_resource(Orders, '/orders')

(3) 每个角色具有不同的路由:

class OrdersSiteAdmin(restful.Resource):
  def get(self):
    return admin_JSON_response()
api.add_resource(OrdersSiteAdmin, '/orders_site_admin')

class OrdersDeptAdmin(restful.Resource):
  def get(self):
    return dept_admin_JSON_response()
api.add_resource(OrdersDeptAdmin, '/orders_dept_admin')

class OrdersBasic(restful.Resource):
  def get(self):
      return basic_JSON_response()
api.add_resource(OrdersBasic, '/orders_basic')

有没有一个共识,哪种方式是首选的 RESTful 方式?

非常感谢!


1
请注意,由于您使用了用户会话,第二个选项实际上并不符合RESTful的标准。REST API不应存储任何客户端状态。相反,您应该要求客户端对他们发送给您的每个请求进行身份验证。 - Miguel Grinberg
1
@Miguel,好的点子。那么你对(1)和(3)之间的区别有什么想法? - SeanPlusPlus
我认为选项(2)如果正确执行是最好的选择。让我把这个表述成一个答案。 - Miguel Grinberg
1个回答

4
您的第二个选项违反了“无状态”约束,使用用户会话在REST API中不是一个好主意,而应该要求客户端在每个请求中提供身份验证。
假设您修复了问题#2,现在有一个current_user变量,它在身份验证期间填充。然后,您可以按照以下方式重新编写示例:
class Orders(restful.Resource):
  def get(self):
    if current_user.role == 'site_admin':
      return admin_JSON_response()
    elif current_user.role == 'department_admin':
      return dept_admin_JSON_response()
    else:
      return basic_JSON_response()

api.add_resource(Orders, '/orders')

让我们逐一看看你的三个选项:
(1)在查询字符串中指定角色,这将使任何用户通过传递所需的角色来请求任何表示形式。但为什么要将角色放在查询字符串中?我假设您会对用户进行身份验证,因此知道用户也知道角色。这似乎是不必要的,并且会增加额外的验证工作量。
(3)为每个角色创建不同的资源。再次,您必须确保“基本”用户无法访问适用于更高角色的两个URL,因此在此处还需要进行一些验证工作。
(2)假设用户数据库存储了每个用户的角色,因此一旦用户经过身份验证,根据分配的角色返回正确的表示形式。我认为这是最好的选择,因为用户真的没有办法侵入他们不被允许查看的数据。
说到RESTful,我还会关注您的表示形式,这可以得到改进。考虑实现链接到其他资源,而不是提供ID,以符合HATEOAS约束。

好的回答 - 值得注意的是,提供身份验证并不一定意味着使用 Basic Auth。您也可以使用HMAC签名令牌 - Sean Vieira

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接