在基于 React.js 的单页应用(Single Page Application, SPA)中,我们经常需要实现用户登录认证功能,其中一个重要的需求就是当用户在一段时间内没有操作时,系统会自动跳转到登录页面以保护用户的信息安全。本文将介绍如何使用 React.js 来实现一种通用的登录超时自动跳转登录页面的方法。
1. 实现方案
在 SPA 中,路由(Route)是非常重要的一个概念,因为页面的切换通常是通过改变 URL 栏中的路径实现的。基于这一点,我们可以在路由组件上增加一个监听器,当用户没有操作时,记录当前时间;如果在某一段时间内没有操作,则跳转到登录页面。
具体实现步骤如下:
- 在 App 组件中添加一个 state,用于存储用户最后一次的操作时间:
------ ----- ---- -------- ----- --- ------- --------------- - ------------------ - ------------- ---------- - - --------------- ----------- -- ----------------------- - ----------------------------------- - ------------------- - ------------------------------------ ------------------------- ---------------------------------- ------------------------- - ---------------------- - --------------------------------------- ------------------------- ------------------------------------- ------------------------- - -------------------- - --------------- --------------- ---------- --- - -------- - ------ - -- --- -- - - ------ ------- ----
在 componentDidMount 和 componentWillUnmount 函数中,我们分别对 window 对象绑定和解除 mousemove 和 keydown 事件的监听器。当用户有这两种事件时,handleUserActivity 函数会更新 lastActiveTime 状态值。
- 在每个需要验证登录状态的路由组件中,判断当前时间和 lastActiveTime 的差值是否超过一定的时间(例如 30 分钟),如果超时,则自动跳转到登录页面:
------ ----- ---- -------- ------ - -------- - ---- ------------------- ----- -------------- - -- ---------- ---------- ------- -- -- - ----- ---------- - --------------------------------------- ----- -------------- - ------------------------------------------------- ----- ------- - -- - -- - ----- -- ---- ----- ----- - ---------- - --------------- ------ - ------ --------- --------------- -- ---------- -- ----- - ------- - - ---------- ---------- -- - - - --------- ----- --------- --------- ------ - ----- -------------- -- -- -- - - -- -- -- ------ ------- ---------------
在 ProtectedRoute 组件中,我们判断用户是否处于登录状态,然后通过 localStorage 获取用户最后一次的操作时间 lastActiveTime。如果时间超过 timeout,就通过 Redirect 组件将用户自动跳转到登录页面。
2. 存在问题
然而,我们发现在单页应用中实现自动跳转存在以下问题:
在路由组件中增加重定向等逻辑,导致代码中的业务逻辑混杂在组件内部,影响代码的可维护性和可读性。
不同路由组件中的验证逻辑可能会重复,造成代码冗余和难以维护。
如果浏览器中有多个页面处于登录状态,用户操作其中任何一个页面都会更新其他页面的 lastActiveTime,这显然不符合用户体验的预期。
3. 解决方案
为了解决以上问题,我们可以考虑使用一个名为 AuthService 的服务类来管理用户的登录状态和验证逻辑。AuthService 封装了存储功能和验证功能,路由组件只需要从 AuthService 中获取当前用户的登录状态即可。
具体实现如下:
----- ----------- - ------------- - --------------- - --------------------------------------- ------------------- - ------------------------------------------------- ---------------------------------- --- -- - -- ------ --- --------------- - --------------- - ------------- - -- ------ --- ------------------- - ------------------- - ------------------- - --- - ------------------------ - ----- ----- - ---------- - -------------------- ------ --------------- -- ----- - -------- - ---------------------------------- - -- ------------------------- - ----- -- - - --------- --------- ------ - ----- -------------- - -- --------------------------------- --- ------------- -------------------------- ------ ----- - ------ ------ - ---------------------- - ---------------------------------------- ----------------------- - - ------ ------- --- --------------
在 AuthService 构造函数中,我们初始化了用户的登录状态和最后一次操作时间。在 window 对象上监听了 storage 事件,并且在事件回调中更新了 AuthService 的状态值。这样,在一个页面中更新了用户的登录状态或最后一次操作时间后,其他页面也会及时得到更新。
AuthService 中还有两个方法:
isAuthenticated:根据 AuthService 的状态值,判断用户是否处于登录状态且操作时间没有超时。
redirectUnlessAuthenticated:如果用户没有处于登录状态或操作时间超时了,那么就通过 window.history 和 props.history 将用户自动跳转到登录页面。
现在,只需要在路由组件中调用 AuthService 中的 isAuthenticated 方法,通过这种方式来实现登录超时自动跳转到登录页面的功能:
------ ----- ---- -------- ------ - -------- - ---- ------------------- ------ ----------- ---- ---------------- ----- -------------- - -- ---------- ---------- ------- -- -- - ----- ------- - -- - -- - ----- -- ---- ------ - ------ --------- --------------- -- ------------------------------------ - - ---------- ---------- -- - - - ---------------------------------------------- -- ----- - - -- -- -- ------ ------- ---------------
在这个 ProtectedRoute 组件中,我们只需要调用 AuthService 的 isAuthenticated 和 redirectUnlessAuthenticated 方法,不需要再进行任何逻辑配置。AuthRoute 中的 isAuthenticated 和 redirectUnlessAuthenticated 方法将会自动根据 AuthService 中的状态值来判断用户是否应该跳转到登录页面。
AuthRoute 的配置也非常简单:
------ ----- ---- -------- ------ - ------------- -- ------- ----- - ---- ------------------- ------ --------- ---- -------------- ------ --------- ---- -------------- ------ --------- ---- -------------- ----- --- - -- -- - ------ - -------- ------ ------------- --------------------- -- ---------- -------- --------------------- -- --------- -- -- ------ ------- ----
相对于之前的实现方式,使用 AuthService 的方式将路由组件和验证逻辑分离开来,大大提高了代码的可维护性和可读性。此外,AuthService 还可提供其他的验证逻辑、登录状态存储实现等功能,是一种非常通用的用户登录认证方案。
4. 总结
本文介绍了使用 React.js 实现 SPA 应用中的登录超时自动跳转登录页面的方法。通过这种方式,可以轻松实现登录认证的功能,并且将路由组件和验证逻辑分离开来,提高了代码的可维护性和可读性。我们还介绍了使用 AuthService 这个服务类管理用户登录状态和验证逻辑的方式,提供了一种通用的解决方案。
完整代码示例:
App.js:
------ ----- ---- -------- ------ - ------------- -- ------- ----- - ---- ------------------- ------ --------- ---- -------------- ------ --------- ---- -------------- ------ --------- ---- -------------- ----- --- ------- --------------- - ------------------ - ------------- ----------------------- - ----------------------------------- - ------------------- - ------------------------------------ ------------------------- ---------------------------------- ------------------------- - ---------------------- - --------------------------------------- ------------------------- ------------------------------------- ------------------------- - -------------------- - ---------------------------------------- ----------------------- - -------- - ------ - -------- ------ ------------- --------------------- -- ---------- -------- --------------------- -- --------- -- - - ------ ------- ----
AuthRoute.js:
------ ----- ---- -------- ------ - ------ -------- - ---- ------------------- ------ ----------- ---- ---------------- ----- --------- - -- ---------- ---------- ------- -- -- - ------ - ------ --------- --------------- -- ----------------------------- - - ---------- ---------- -- - - - --------- ----- --------- --------- ------ - ----- -------------- -- -- -- - - -- -- -- ------ ------- ----------
AuthService.js:
----- ----------- - ------------- - --------------- - --------------------------------------- ------------------- - ------------------------------------------------- ---------------------------------- --- -- - -- ------ --- --------------- - --------------- - ------------- - -- ------ --- ------------------- - ------------------- - ------------------- - --- - ----------------------- - -- - -- - ----- - ----- ----- - ---------- - -------------------- ------ --------------- -- ----- - -------- - ---------------------------------- - -- ------------------------- - ----- -- - - --------- --------- ------ - ----- -------------- - -- --------------------------------- --- ------------- -------------------------- ------ ----- - ------ ------ - ---------------------- - ---------------------------------------- ----------------------- - - ------ ------- --- --------------
LoginForm.js:
------ ------ - -------- - ---- -------- ----- --------- - -- --------- ------- -- -- - ----- ---------- ------------ - ------------- ----- ---------- ------------ - ------------- -------- -------------- - ------------------- ------------------------------------ ------- ---------------------------------------- ----------------------- ----- ---- - --------------- -- -------------------- -- - --------- --- -- ---------------------- - ------ - ----- ----------------------- ----- ------- ---- ------ ----------- ---------------- ------------- -- ---------------------------- -- -------- ------ ----- ------- --- ------ --------------- ---------------- ------------- -- ---------------------------- -- -------- ------ ------- ------------------------- ------- -- -- ------ ------- ----------
Dashboard.js:
------ ----- ---- -------- ------ ----------- ---- ---------------- ----- --------- ------- --------------- - ------------------ - ------------- ----------------- - ----------------------------- - ------------------- - ----------------------------------- - -------------- - ---------------------------------------- -------------------------------------------- - -------- - ------ - ----- ---------------- ------- --------------------------------------- ------ -- - - ------ ------- ----------
来源:JavaScript中文网 ,转载请联系管理员! 本文地址:https://www.javascriptcn.com/post/648bef6848841e9894a38f95