在 MEAN Stack 中将令牌保存在本地存储中17 Mar 2025 | 6 分钟阅读 在我们之前的章节中,我们成功地在应用程序中添加了令牌计时器。一小时后,令牌将过期,我们的帐户将被注销,并且我们必须再次登录。剩下的部分是将令牌保存在本地存储中,因为如果我们不注销就重新加载页面,则必须再次登录。为了将该令牌存储到本地存储中,我们将使用以下步骤 1) 我们将返回到我们的 auth.service.ts 文件并创建一个新的私有方法,即 saveAuthData。我们将其设为私有,因为我们仅从该服务内部调用它。 ![]() 2) 在此方法中,我们希望获取要保存的数据,即令牌和 expirationDate。此 expirationDate 应该是日期,而不是以秒为单位的时间,因为以秒为单位的时间是相对的度量,在将来对我们没有帮助。在那里,我们需要清楚地了解令牌何时过期。 ![]() 3) 现在,所有这些数据都将被序列化并存储在本地存储中。访问本地存储非常容易。我们可以访问本地存储 API,然后我们只需调用 setItem() 将值存储在那里。该值将以键值对的形式设置。 我们还希望设置 expirationDate。 ![]() 我们将日期转换为 ISOString,这是日期的序列化和标准样式版本,然后我们可以使用它在以后读取数据后重新创建它。 4) 我们还需要添加另一个方法,clearAuthData。在此方法中,我们将调用 removeItem() 方法来删除令牌和过期时间 - 此 clearAuthData() 方法在 logout() 方法中被调用以清除本地存储。 ![]() 5) 现在,我们需要调用 saveAuthData, 并且此方法将在 login() 方法中被调用。为了调用此方法,我们必须通过创建一个新常量来创建日期,然后将信息传递给该函数。 ![]() 6) 现在,我们将使用该信息在应用程序启动时初始化我们的身份验证状态,为此,我们将添加另一个方法,即 authAuthUser(),在这里我们尝试在本地存储中获取了信息的情况下自动验证用户。为此,我们还需要另一个方法 getAuthData() 方法。在此方法中,我们将通过像这样调用 getItem() 方法从本地存储中获取令牌和 expirationDate ![]() 7) 现在,我们将检查令牌和 expirationDate,如果它不存在,则我们不返回任何内容。否则,我们将返回一个 JavaScript 对象,其中包含我们的令牌和 expirationDate。 ![]() 8) getAuthData() 方法将在 authAuthUser() 中被调用。我们将调用此方法并将数据存储到一个新的常量中,即 authInformation。如果我们没有获得身份验证信息,我们将不返回任何内容。现在,我们将检查令牌是否仍然有效,为此,我们获取了 expirationDate。我们无法验证它是否是有效的令牌,只有服务器才能做到,但至少我们可以从过期角度判断它是否仍然有效。因此,我们将检查过期日期是否仍在将来。我们将通过创建一个新的日期对象来获取当前日期时间,然后我们可以简单地检查它是否在将来,方法是创建一个新的常量,即 isInFuture, 我们通过比较 expirationDate 与当前日期和时间来创建一个布尔值。如果 expirationdate 大于当前日期和时间,则我们有一个将来的日期。 ![]() 9) 现在,我们将检查 isInFuture 是 true 还是 false。如果为 true,则表示用户已通过身份验证,我们将 this.token 设置为本地存储中的令牌。我们还将 isAuthenticated 设置为 true 并推送该信息,因此我们将设置 authStatusListener,调用 next 并将 true 传递给每个人,因为用户现在已通过身份验证。 ![]() 10) 现在,我们还需要设置计时器。因此,由于需要在两个地方完成此操作,我们可能会在 login() 方法中重构 tokenTimer 代码并将其放入一个新地方。因此,我们将把它从 subscribe 方法中拉出来,并创建一个新方法,即 setAuthTimer()。在此方法中,我们获取持续时间并将 tokenTimer 代码粘贴在这里。 ![]() 11) expiresInDuration 现在是持续时间。现在,在 login() 方法的 subscribe 方法中,我们将调用此 setAuthTimer() 方法并将 expiresInDuration 传递给该方法。 ![]() 12) 我们还需要在 autoAuthUser() 中调用 setAuthTimer() 方法 此代码行将生成一个错误,因为我们还没有 expiresInDuration。因此,与其检查日期是否在将来,不如检索差异。我们将把名称 isInFuture 替换为 expiresInDuration。然后,此 expiresInDuration 应为 expirationDate-now。这将引发错误,因此我们将 getTime() 方法附加到 expirationDate 和当前时间。 ![]() 13) 现在,我们知道我们正在从将来某个时间戳中检测当前时间戳。如果当前时间大于 exipresIn,这也将是一个导航数字。因此,我们将检查 expiresInDuration 是否大于零,因为如果是,我们知道该日期在将来。如果它小于或等于零,则表示就在现在或过去,因此过期时间将在过去,这意味着它已过期。 ![]() 14) 我们以毫秒为单位获取 exipresInDuration,但我们的 authTimer 以秒为单位工作。我们将通过将其除以 1000 来做到这一点。 ![]() 15) 现在,一切都很好,剩下的事情是运行该方法,并且运行它的好地方是在 app 组件中。在那里,我们知道此组件会在我们的应用程序启动时首先加载。因此,我们将在 OnInit 中实现,并且在 OnInit() 方法中,我们希望使用来自我们的身份验证服务的某些内容。因此,我们也需要在这里注入我们的身份验证服务。 ![]() 16) 现在,在 ngOnInit() 方法中,我们将从服务调用 autoAuthUser() 方法,以启动该自动身份验证工作流程。 ![]() 现在,我们保存所有文件并返回到我们的 angular 应用程序。我们登录,登录后,我们重新加载页面。 ![]() ![]() 重新加载后 ![]() 17) 它工作正常,但问题是我们的标题以某种方式没有反映此更新的信息。重新加载后,我们仍然看到登录和注册按钮。因此,标题没有获得有关用户已通过身份验证的信息。因此,为了解决此问题,我们将返回到 header.component.ts 文件,并且除了设置此侦听器外,我们还应该出于与之前在帖子列表组件中相同的理由,将 userIsAuthenticated 设置为 this.authService.getIsAuth()。 ![]() 现在,我们将保存该文件,并且我们的应用程序将重新加载。我们将获取标题,其中包含注销和新建帖子按钮。 ![]() 现在,一切都工作正常,并且缺少一件事,那就是,我们没有将我们的用户连接到我们的帖子。我们稍后将在新部分中执行此操作。 下载完整项目(Authentication.zip) |
我们请求您订阅我们的新闻通讯以获取最新更新。