将用户的权限存储在JWT声明中或在每次请求时在服务器上检查它是否更有效?

Tom*_*ock 9 security optimization authorization http jwt

JWT是一种确保数据发送给用户并且不会被篡改的好方法,但这会带来一些艰难的选择.目前,我处于两难选择之中,即在授权数据中存储授权数据并仅为授权触摸数据库一次,或者只是存储用户ID并检查每个请求与数据库的服务器的授权级别.

使这个如此艰难的选择的原因是该应用程序使用多个授权级别,这使得base64编码的URL非常长且笨重(参见下面可以预期存储为授权级别的内容).

另一方面,要获得授权,必须在数据库中进行两次查找.

所以我的问题如下:通过向服务器发送权限,每个请求的额外开销是否值得避免在每次请求时查找权限的麻烦?

作为旁注; 在权限更改的情况下,查找数据库方法的好处是不需要用户再次登录(请参阅post).

"perms": {
    "roles": [
        {
            "name": "Admin",
            "id": 1,
            "assigned": true
        },
        {
            "name": "Webmaster",
            "id": 8,
            "assigned": true
        }
    ],
    "actions": [
        {
            "id": 1,
            "name": "cms-edit",
            "parameters": null,
            "parameterized": null
        },
        {
            "id": 9,
            "name": "admin-syslog",
            "parameters": null,
            "parameterized": null
        },
        {
            "id": 10,
            "name": "admin-debug",
            "parameters": null,
            "parameterized": null
        },
        {
            "id": 12,
            "name": "member-list-extended",
            "parameters": null,
            "parameterized": null
        },
        {
            "id": 2,
            "name": "cms-list",
            "parameters": null,
            "parameterized": null
        },
        {
            "id": 3,
            "name": "cms-add",
            "parameters": null,
            "parameterized": null
        },
        {
            "id": 5,
            "name": "member-list",
            "parameters": null,
            "parameterized": null
        },
        {
            "id": 7,
            "name": "member-view",
            "parameters": null,
            "parameterized": null
        },
        {
            "id": 8,
            "name": "member-edit",
            "parameters": null,
            "parameterized": null
        }
    ]
Run Code Online (Sandbox Code Playgroud)

Aka*_*kar 15

你的第一个问题:

通过向服务器发送权限,每个请求的额外开销是否值得避免在每次请求时查找权限的麻烦?

回答:

让我们看一下jwt.io提供的关于何时使用JWT的描述:

授权:这是使用JWT的最常见方案.一旦用户登录,每个后续请求将包括JWT,允许用户访问该令牌允许的路由,服务和资源.Single Sign On是一种现在广泛使用JWT的功能,因为它的开销很小,并且能够在不同的域中轻松使用.

这意味着一旦用户登录,您需要在服务器端生成令牌.

它包含:

  • 用户(作为id或名称)
  • 客户端的角色(用户,管理员,访客,无论如何......)

一旦客户端向服务器请求或发送数据,服务器首先检查给定令牌是否有效并且是否已知,然后检查角色是否满足访问某个资源的标准.

所有角色/访问数据都可以在系统启动时读取一次并保存在内存中.此外,客户端具有的角色只能从数据库中.这样,客户端日志阅读,一旦你有没有后续的数据库访问,因此,有很大的性能提升.

另一方面,如果客户端请求数据或想要执行操作,则需要一种身份验证机制来评估传递的令牌是否获得了执行此操作所需的角色.

这样我们解决了数据库的麻烦,加上我们消除了向客户端暴露太多信息的危险(即使客户端无法篡改数据,它也可以读取数据!)

请注意:https://jwt.io/introduction

请注意,使用签名令牌,令牌中包含的所有信息都会向用户或其他方公开,即使他们无法更改.这意味着您不应该在令牌中放置秘密信息.

参见A3(敏感数据暴露):https://www.owasp.org/index.php/Top_10-2017_Top_10

最后:如果客户端空闲时间过长或故意注销,也会使令牌无效.

后续问题:

许可的情况改变了数据库中的查找方法,其好处是不需要用户再次登录

回答:

根据服务器的基础结构,您可以编写刷新机制(如果角色更新,服务器生成新令牌并将其与生成的答案一起发送到客户端,使旧的无效,客户端仅使用最近的令牌并覆盖旧的)或在服务器端添加一些状态,如客户端会话:

消除令牌上的角色/权限.您最好为客户端生成会话并在服务器端提供会话角色/权限.客户端获取可以进行身份​​验证的会话令牌(通常是id).一旦权限/角色发生变化,我们就必须做两件事:

  1. 更新数据库
  2. 更新会话的角色/权限

同样,每个后续请求都将在内存中执行角色/权限检查,并且不需要数据库通信,而客户端只有一个小会话令牌(或您的JWT).因此,角色/权限更改对客户端是透明的(不需要重新连接),我们消除了JWT刷新要求.

  • 我很惊讶地看到这个答案被接受,因为答案没有得出关于在哪里/如何管理角色权限的任何结论。如何在内存中完成角色权限或如何在不同服务上传播更新?相当混乱且不完整的答案 (8认同)
  • 您可以扩展更多...我认为发明“JWT”的全部原因是“删除会话”,因为会话有内存限制和分发限制...所以放入 JWT 是唯一的选择?然后是我通常阅读的选项,回到会话......我真的很想理解。虽然我从来没有找到足够详细的答案......文章通常只说优点和缺点,而不是如何解决问题。具有动态权限的自定义角色,如何实现。使用其中之一,为什么一个比另一个更好,如果整个看跌期权由于其局限性而更加远离会话。 (2认同)
  • 由于拥有微服务架构:有一些小的独立服务相互通信。人们如何知道会话是否有效?每个服务每次都必须与一个中央实例通信,想想所产生的开销。使用 JWT:您可以信任的一切都在 JWT 中。您消除了这个麻烦,因为只有第一个服务必须执行更新/无效和获取。其他人只需检查签名是否有效。您在哪里实施角色?在需要特定权利的每项服务中。所有角色从哪里获取?第一个服务与之对话的中央身份验证服务。 (2认同)

B12*_*ter 7

取决于你如何解释效率。在谈论资源效率时,请记住您的 JWT 是在每个请求上传输的。因此,如果您有一个带有细粒度访问控制列表 (ACL) 的大型应用程序,并且您总是在每个请求-响应中来回访问这些列表,那么这肯定会花费您几千字节,具体取决于您发出的请求数量。