身份验证是任何Web应用程序中最重要的部分之一。本教程讨论基于令牌的身份验证系统以及它们与传统登录系统的区别。在本教程结束时,您将看到一个用Angular和Node.js编写的完整工作演示。
在继续基于令牌的身份验证系统之前,让我们先看一下传统的身份验证系统。
- 用户在登录表单中提供用户名和密码,然后点击登录。
- 发出请求后,通过查询数据库在后端验证用户。如果请求有效,则使用从数据库中获取的用户信息创建会话,然后在响应头中返回会话信息,以便将会话ID存储在浏览器中。
- 提供用于访问应用程序中受限端点的会话信息。
- 如果会话信息有效,则让用户访问指定端点,并使用呈现的HTML内容进行响应。
到目前为止一切都很好。Web应用程序运行良好,并且能够对用户进行身份验证,以便他们可以访问受限端点。但是,当您想为您的应用程序开发另一个客户端(例如Android客户端)时会发生什么情况?您是否能够使用当前的应用程序来验证移动客户端并提供受限制的内容?就目前情况而言,没有。造成这种情况的主要原因有两个:
- 会话和Cookie对于移动应用程序没有意义。您无法与移动客户端共享在服务器端创建的会话或Cookie。
- 在当前应用程序中,返回呈现的HTML。在移动客户端中,您需要包含JSON或XML等内容作为响应。
在这种情况下,您需要一个独立于客户端的应用程序。
在基于令牌的身份验证中,不会使用cookie和会话。令牌将用于对向服务器发出的每个请求进行用户身份验证。让我们使用基于令牌的身份验证重新设计第一个场景。
它将使用以下控制流程:
- 用户在登录表单中提供用户名和密码,然后点击登录。
- 发出请求后,通过在数据库中查询来验证后端的用户。如果请求有效,则使用从数据库获取的用户信息创建令牌,然后在响应标头中返回该信息,以便我们可以将令牌浏览器存储在本地存储中。
- 在每个请求标头中提供令牌信息,以访问应用程序中的受限端点。
- 如果从请求标头信息中获取的令牌有效,则让用户访问指定端点,并使用JSON或XML进行响应。
在这种情况下,我们没有返回会话或cookie,也没有返回任何HTML内容。这意味着我们可以将此架构用于任何客户端的特定应用程序。您可以看到下面的架构架构:
那么这个JWT是什么?
JWT代表JSONWeb令牌,是授权标头中使用的令牌格式。该令牌可帮助您以安全的方式设计两个系统之间的通信。出于本教程的目的,我们将JWT重新表述为“不记名令牌”。不记名令牌由三部分组成:标头、负载和签名。
- 标头是令牌中保存令牌类型和加密方法的部分,也是使用Base-64进行加密的。
- 有效负载包含信息。您可以输入任何类型的数据,例如用户信息、产品信息等,所有这些数据都使用Base-64加密进行存储。
- 签名由标头、负载和密钥的组合组成。密钥必须安全地保存在服务器端。
您可以在下面看到JWT架构和示例令牌:
您不需要实现不记名令牌生成器,因为您可以找到多种语言的已建立包。您可以在下面看到其中一些:
Node.js | https://github.com/auth0/node-jsonwebtoken |
PHP | http://github.com/firebase/php-jwt |
Java | http://github.com/auth0/java-jwt |
红宝石 | https://github.com/jwt/ruby-jwt |
.NET | https://github.com/auth0/java-jwt |
Python | http://github.com/progrium/pyjwt/ |
介绍了有关基于令牌的身份验证的一些基本信息后,我们现在可以继续讨论一个实际示例。看一下下面的架构,然后我们将更详细地分析它:
- 多个客户端(例如网络应用程序或移动客户端)出于特定目的向API发出请求。
- 请求是向https://api.yourexampleapp.com等服务发出的。如果很多人使用该应用程序,则可能需要多个服务器来提供请求的操作。
- 这里,负载均衡器用于平衡请求,以最适合后端的应用程序服务器。当您向https://api.yourexampleapp.com发出请求时,负载均衡器首先会处理请求,然后会将客户端重定向到特定服务器。
- 有一个应用程序,并且该应用程序部署到多台服务器(server-1、server-2、...、server-n)。每当向https://api.yourexampleapp.com发出请求时,后端应用程序都会拦截请求标头并从授权标头中提取令牌信息。将使用此令牌进行数据库查询。如果此令牌有效并且具有访问所请求端点所需的权限,则它将继续。如果没有,它将返回403响应代码(表示禁止状态)。
基于令牌的身份验证具有解决严重问题的多个优点。以下是其中的一些:
在基于令牌的身份验证中,令牌通过请求标头传输,而不是将身份验证信息保留在会话或cookie中。这意味着没有状态。您可以从任何类型的可以发出HTTP请求的客户端向服务器发送请求。
在当前的大多数Web应用程序中,视图在后端呈现,HTML内容返回到浏览器。前端逻辑依赖于后端代码。
没有必要建立这样的依赖关系。这带来了几个问题。例如,如果您正在与实现前端HTML、CSS和JavaScript的设计机构合作,您需要将该前端代码迁移到后端代码中,以便进行一些渲染或填充操作。一段时间后,您呈现的HTML内容将与代码机构实现的内容有很大不同。
在基于令牌的身份验证中,您可以与后端代码分开开发前端项目。您的后端代码将返回JSON响应,而不是渲染的HTML,并且您可以将前端代码的缩小、gzip版本放入CDN中。当您访问网页时,HTML内容将从CDN提供,并且页面内容将由API服务使用授权标头中的令牌填充。
CSRF是现代网络安全中的一个主要问题,因为它不检查请求源是否可信。为了解决这个问题,使用令牌池在每个表单帖子上发送该令牌。在基于令牌的身份验证中,令牌用于授权标头,而CSRF不包含该信息。
当应用程序中进行会话读、写或删除操作时,它会在操作系统的temp文件夹中进行文件操作,至少第一次是这样。假设您有多个服务器,并且在第一台服务器上创建了一个会话。当您发出另一个请求并且您的请求落入另一台服务器时,会话信息将不存在并且将得到“未经授权”的响应。我知道,你可以通过粘性会话来解决这个问题。然而,在基于令牌的认证中,这种情况自然就解决了。不存在粘性会话问题,因为请求令牌在任何服务器上的每个请求上都会被拦截。
这些是基于令牌的身份验证和通信的最常见优点。关于基于令牌的身份验证的理论和架构讨论就到此结束。是时候看一个实际例子了。
您将看到两个应用程序来演示基于令牌的身份验证:
- 基于令牌的身份验证后端
- 基于令牌的身份验证前端
在后端项目中,会有服务的实现,服务结果将是JSON格式。服务中没有返回视图。在前端项目中,将有一个用于前端HTML的Angular项目,然后前端应用程序将由Angular服务填充,以向后端服务发出请求。
在后端项目中,主要有三个文件:
- package.json用于依赖管理。
- models/User.js包含一个用户模型,用于对用户进行数据库操作。
- server.js用于项目引导和请求处理。
就是这样!这个项目非常简单,因此您无需深入研究即可轻松理解主要概念。