在 MEAN 堆栈中添加令牌以验证请求2025年3月17日 | 阅读 7 分钟 上一节中,我们成功实现了并使用了我们的 **check-Auth** 中间件来保护某些路由免受未经授权的访问。现在,我们希望在前端使用该令牌并在登录时实际存储它,然后将其附加到我们发送到后端的请求中。我们将使用以下步骤添加令牌以验证请求 1) 首先,我们将进入我们的 auth.service.ts 文件,在那里我们进行登录。我们将定义一个新属性,它的类型是字符串。最初,此属性将是未定义的,但在我们获取响应的 login() 方法中,我们知道此响应将包含令牌。因此,我们应该能够从响应中提取令牌。应该有一个令牌属性,我们可以像这样配置此 POST 请求以使其了解它 ![]() 2) 我们想在应用程序的其他部分使用该令牌。我们希望在某些请求的帖子服务中使用它。为此,我们将在 auth 服务文件中添加一个新方法,因为令牌字段是私有的。我们可以将其变为公共的,但我们只是在此处添加一个新方法,即 **getToken()**,并在其中返回此令牌。 ![]() 3) 现在,我们将返回到我们的 **service.ts** 文件并获取该令牌以使用它。现在要使用它,我们需要将 auth 服务注入到帖子服务中,然后将一个标头添加到我们所有的传出 HTTP 请求中。我们将使用一种不同的方式来完成此操作,我们将为我们的 HTTP 客户端创建一个所谓的拦截器,这是 angular http 客户端提供的一个功能。我们可以添加一个拦截器,这些拦截器是在任何传出 HTTP 请求上运行的函数,然后我们可以操作这些传出请求,例如附加我们的令牌,这正是我们想要做的。 因此,我们将在身份验证文件夹中创建一个新文件,并将其命名为 auth-interceptor.ts,以表明其中包含一个拦截器。 ![]() 4) 现在,这是 angular http 客户端提供的官方功能,我们通过创建一个普通类并将其命名为 **AuthInterceptor** 来创建这样的拦截器。此类必须实现 angular 提供的一个接口,即 **HttpInterceptor**,并且我们像这样定义 **intercept()** 方法 ![]() 5) 此 intercept() 方法接受两个参数。第一个参数是我们正在拦截的请求,因为我们提到它适用于传出请求,并且它的类型是 HttpRequest,这是一种可以包装各种信息和数据的静态类型。 ![]() 6) 第二个参数是我们在 NodeJS 端的中间件中可能知道的东西。我们也有下一个参数,这个拦截器的工作方式很像中间件,只是用于传出而不是传入请求。因此,我们没有响应对象,因为我们无法配置响应。我们只参与发送请求。 但我们确实获得了该请求,并且我们获得了 next,它允许我们离开该拦截器并允许我们应用程序的其他部分,例如我们订阅响应的部分。我们允许这些部分获取该请求及其响应。因此,这就是为什么我们需要 next 来允许拦截器或该拦截器中的请求继续其在应用程序中的旅程,并且该 next 参数的类型是 HttpHandler。 ![]() 7) 此方法必须返回一些东西,并且此方法将返回对 next.handle() 的调用。此 handle 方法由 next 提供,在此处我们允许请求继续其旅程。因此,我们只需在 handle 方法中传递请求,这将是一个不做任何事情的有效拦截器。这只会获取请求并允许它继续而不会被更改。 ![]() 8) 现在,我们将更改它,我们将创建一个新的常量来保存我们的令牌,为此,我们需要将我们的 auth-service 注入到此拦截器中,因为我们可以从那里获取令牌。接收注入服务的服务必须具有 @Injectable 注解才能将服务注入到其他服务中。我们无论如何都将其添加到我们所有其他服务中,因为我们提供它们的方式。我们将以不同的方式提供拦截器,因为 angular http 客户端要求我们以不同的方式提供它。 但是我们仍然需要添加一个空的 injectable 注解,这样我们就可以真正将服务注入到此服务中,这是 angular 的一个要求。 ![]() 9) 现在,我们将在该拦截器类中使用 authservice。我们将通过简单地调用 authservice 类的 getToken() 方法来创建新令牌,如下所示 ![]() 10) 现在,我们操作请求以保存此令牌,并且在我们操作它之前应该克隆它。我们必须这样做,因为如果我们直接编辑该传出请求,由于请求在内部工作和处理的方式,我们将导致不必要的副作用和问题。因此,我们将创建一个新的常量,在那里我们将调用请求的 clone() 方法,它将创建该请求的副本。我们可以将克隆的配置传递给克隆,不仅克隆请求,还可以编辑克隆。我们想要编辑它,实际上,我们想要精确地编辑它的标头。 标头应该是我们原始请求的标头,但我们还想查看一个额外的标头,我们可以使用 set 来完成。 ![]() 11) Set 听起来会覆盖所有旧标头,但它不会。它只会添加一个新标头,并为其设置值,但如果该标头已经存在,它将覆盖它。因此,在 clone 函数中,我们将设置 Authorization 标头,其值将是我们的 authToken。 ![]() 这将创建一个包含此 Authorization 标头和我们令牌的请求,现在它是我们想要转发的 authRequest,它现在应该离开我们的应用程序。 12) 现在我们正在获取或操作传入请求,并且我们在 Authorization 标头上添加了我们的令牌。更准确地说,这不起作用。请记住我们提到的关于 Bearer 单词的事情。我们的值实际上应该是 Bearer,然后是空格,然后是 authToken,如下所示 ![]() 这是我们在后端提取数据的方式,我们可以省略它,这只是我们经常看到的一种约定。 13) 所以,现在我们已经添加了它,但我们的中间件被忽略了。我们不需要将其添加到我们的 Angular 应用程序中。要添加它,我们必须将其注入为服务或提供为服务,但不是使用 provided root,而是略有不同。我们将进入我们的 **module.ts** 文件,并在其中向提供者数组添加一个新对象或 JavaScript 对象。此对象必须具有几个属性。 a. 第一个属性是 provide 属性,在那里我们提供一个令牌,即 **HTTP_INTERCEPTOR**。此令牌是从 @angular/common/http 导入的。 b. 因为现在我们正在告诉 Angular,对于 Angular 将寻找的此标识符,Angular http 客户端将寻找它,对于此令牌,我们想要提供一个新值。然后使用 useClass 属性提供该值,在这里,我们必须指向我们的拦截器,即 **Auth-interceptor**。 c. 现在,我们可以在一个应用程序中拥有多个拦截器,因此我们将第三个值,即 **multi**,并将其值设置为 true。 这只是告诉 Angular 不要覆盖现有拦截器,而是将其添加为附加拦截器,并且 Angular http 客户端将处理内部。 ![]() 现在,我们的拦截器已注册,每个传出请求都将收到该令牌。这也意味着不需要该令牌的请求也将收到它,但它将是 undefined 并且应该不是问题。 我们将返回我们的应用程序并尝试在登录后插入一个新帖子。 注意:我们将收到“Request header field Authorization is not allowed by Access-Control-Allow-Headers in preflight response”错误。我们将收到此错误,因为我们不允许此标头。因此,我们将在后端的 app.js 文件中允许此标头并设置 Authorization 标头,如下所示![]() 现在,如果我们转到我们的 Angular 应用程序,我们没有收到该错误。 ![]() ![]() ![]() 现在,一切都运行良好,但我们的标头看起来很糟糕。我们将在下一节中改进 UI 标头以反映身份验证状态。 |
我们请求您订阅我们的新闻通讯以获取最新更新。