Django API 开发:身份认证原理

举报
宇宙之一粟 发表于 2022/06/27 11:28:27 2022/06/27
【摘要】 在传统的整体式 Django 网站认证中,认证更为简单,并且涉及基于会话的 Cookie 模式,我们将在下面进行回顾。 但是使用 API 会有些棘手。 请记住,HTTP 是无状态协议,因此没有内置的方式可以记住用户是否从一个请求到另一个请求进行了身份验证。 每次用户请求受限资源时,它都必须验证自己。解决方案是在每个 HTTP 请求中传递唯一的标识符。 令人困惑的是,此标识符的形式尚无公认的方...

在传统的整体式 Django 网站认证中,认证更为简单,并且涉及基于会话的 Cookie 模式,我们将在下面进行回顾。 但是使用 API 会有些棘手。 请记住,HTTP 是无状态协议,因此没有内置的方式可以记住用户是否从一个请求到另一个请求进行了身份验证。 每次用户请求受限资源时,它都必须验证自己。


解决方案是在每个 HTTP 请求中传递唯一的标识符。 令人困惑的是,此标识符的形式尚无公认的方法,它可以采用多种形式。 Django REST Framework 仅随附了四个不同的内置身份验证选项! 而且还有更多第三方软件包提供了其他功能,例如 JSON Web 令牌( JSON Web Tokens,JWT)。


在本文中,我们将彻底探索 API 身份验证的工作原理,回顾每种方法的利弊,然后为我们的 Blog API 做出明智的选择。 到最后,我们将创建用于注册,登录和注销的API端点。

基本认证

HTTP 身份验证的最常见形式称为“基本”身份验证。 客户端发出 HTTP 请求时,必须在授予访问权限之前强制发送批准的身份验证凭据。


完整的请求/响应流如下所示:


  1. 客户端发出 HTTP 请求

  2. 服务器以包含 401(未授权)状态的HTTP响应进行响应代码和 WWW-Authenticate HTTP 标头以及有关如何授权的详细信息

  3. 客户端通过 Authorization HTTP 标头发送回凭据

  4. 服务器检查凭据并以 200 OK 或 403 Forbiddenstatus 响应码


批准后,客户端将使用授权 HTTP 标头凭据发送所有将来的请求。 我们还可以将这种交换可视化如下:




请注意,发送的授权凭证是 <username>:<password>的未加密的 base64 编码版本。 所以在我的情况下,这是 wsv:password123 的 base64 编码为 d3N2OnBhc3N3b3JkMTIz


这种方法的主要优点是简单。 但是有几个主要缺点。 首先,对于每个单个请求,服务器必须查找并验证用户名和密码,这是低效的。 最好先进行一次查询,然后传递某种表示该用户已被批准的令牌。 其次,用户凭证通过明文传递,而不是完全不加密。 这是非常不安全的。 任何未经加密的互联网流量都可以轻松地捕获和重用。 因此,基本身份验证只能通过HTTPS(HTTP的安全版本)使用。

会话认证

像传统的 Django 一样,整体式网站长期以来一直使用替代身份验证方案,该方案将会话和 cookie 结合在一起。 在较高级别上,客户端使用其凭据(用户名/密码)进行身份验证,然后从服务器接收会话 ID(存储为 cookie)。 然后,此会话ID将在以后的每个 HTTP 请求的标头中传递。


传递会话 ID 后,服务器将使用它来查找会话对象,该对象包含给定用户的所有可用信息,包括凭据。


这种方法是有状态的,因为必须在服务器(会话对象)和客户端(会话 ID )上都保存并维护一条记录。


让我们回顾一下基本流程:


  1. 用户输入其登录凭据(通常是用户名/密码)

  2. 服务器验证凭据是否正确,并生成一个会话对象,该会话对象,然后存储在数据库中

  3. 服务器向客户端发送会话 ID,而不是会话对象本身,该会话 ID 作为 cookie 存储在浏览器中

  4. 在所有将来的请求中,会话 ID 都包含 HTTP 标头,并且如果数据库已验证,则请求继续

  5. 用户注销应用程序后,客户端和服务器都会销毁 session ID。

  6. 如果用户以后再次登录,则会生成一个新的 session ID ,并将其作为 cookie 存储在客户端上


Django REST Framework 中的默认设置实际上是基本身份验证和会话身份验证的组合。 使用 Django 的传统的基于会话的认证系统,并通过基本身份验证在每个请求的 HTTP 标头中传递会话 ID 。


这种方法的优点是更安全,因为用户凭据仅发送一次,而不是像基本身份验证那样在每个请求/响应周期中发送一次。 由于服务器不必每次都验证用户的凭据,它只需将会话 ID 与会话对象进行匹配即可快速查找,因此效率更高。


但是有几个缺点。 首先,会话 ID 仅在执行登录的浏览器中有效; 它不能跨多个域工作。 当 API 需要支持多个前端(例如网站和移动应用程序)时,这是一个明显的问题。 其次,会话对象必须保持最新状态,这在具有多个服务器的大型站点中可能是一个挑战。 您如何在每个服务器上保持会话对象的准确性? 第三,对于每个单独的请求,即使是不需要身份验证的请求,都会发送 cookie,这样效率很低。


结果,通常不建议对将具有多个前端的任何 API 使用基于会话的身份验证方案。

Token 认证

第三种主要方法以及我们将在 Blog API 中实现的方法是使用 Token 身份验证。 由于单页应用程序的兴起,这是近年来最流行的方法。


基于 Token 的身份验证是无状态的:客户端将初始用户凭据发送到服务器后,将生成唯一令牌,然后由客户端将其存储为 cookie 或本地存储。 然后,此 Token 在每个传入的 HTTP 请求的标头中传递,服务器使用它来验证用户的身份。 服务器本身不会保留用户记录,只是令牌是否有效。


Cookies vs localStorage

Cookies用于读取服务器端信息。 它们较小(4KB),并随每个HTTP请求自动发送。 LocalStorage专为客户端信息而设计。 它更大(5120KB),默认情况下,每个HTTP请求都不会发送其内容。 Cookie和localStorage中存储的令牌容易受到XSS攻击。 当前的最佳实践是使用httpOnly和Secure cookie标志将令牌存储在cookie中。


让我们看一下此 challenge/response 流中的实际 HTTP 消息的简单版本。 请注意,HTTP 标头 WWW-Authenticate 指定在响应授权标头请求中使用的令牌的使用。




这种方法有很多好处。 由于令牌存储在客户端上,因此扩展服务器以维护最新的会话对象不再是问题。 令牌可以在多个前端之间共享:同一 Token 可以代表网站上的用户和移动应用程序上的同一用户。 相同的会话 ID 无法在不同的前端之间共享,这是一个主要限制。


潜在的不利因素是令牌可能会变得很大。 令牌包含所有用户信息,而不仅仅是一个会话 ID /会话对象设置的 ID。 由于令牌是在每个请求上发送的,因此管理令牌的大小可能会成为性能问题。


令牌的实施方式也可能有很大不同。 Django REST 框架的内置 TokenAuthentication 是非常基础的设置。 因此,它不支持设置令牌过期,这是可以添加的安全性改进。 它还为每个用户仅生成一个令牌,因此网站上的用户以及随后的移动应用程序将使用相同的令牌。 由于有关用户的信息存储在本地,因此这可能会导致维护和更新两组客户信息的问题。


JSON Web令牌(JWT)是令牌的增强的新版本,可以通过几个第三方软件包将其添加到 Django REST Framework 中。 JWT 具有许多优点,包括生成唯一的客户端令牌和令牌过期的能力。 它们既可以在服务器上生成,也可以使用第三方服务(如Auth0)生成。 而且,JWT 可以被加密,从而使其更安全地通过不安全的 HTTP 连接发送。


最终,对于大多数 Web API 来说,最安全的选择是使用基于令牌的身份验证方案。 JWT 是一种不错的,现代的添加,尽管它们需要其他配置。 因此,在本书中,我们将使用内置的 TokenAuthentication。

默认认证

第一步是配置我们的新身份验证设置。 Django REST 框架随附许多隐式设置的设置。 例如,在我们将DEFAULT_PERMISSION_CLASSES 更新为 IsAuthenticated 之前,已将其设置为 AllowAny。


默认情况下,DEFAULT_AUTHENTICATION_CLASSES 设置为 SessionAuthentication 和 BasicAuthentication。 让我们将它们明确添加到我们的 blog_project/settings.py 文件中。


REST_FRAMEWORK = { 
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated', 
    ],
    'DEFAULT_AUTHENTICATION_CLASSES': [ # new 
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication'
    ],
}


为什么要同时使用两种方法? 答案是它们有不同的用途。 会话用于增强 Browsable API 的功能以及登录和注销该功能的能力。 BasicAuthentication 用于在 API 本身的 HTTP 标头中传递会话 ID。


如果您在 http://127.0.0.1:8000/api/v1/ 上重新浏览了可浏览的 API,它将像以前一样工作。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。