购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.6 创建安全路由

问题

大多数应用程序需要在用户登录之前阻止对特定路由的访问。但怎样做到只对某一些路由进行安全保护,而开放另外一些呢?是否有可能将安全机制与用户登录和退出的界面元素分离?如何在不写大量代码的情况下做到这一点呢?

解决方案

让我们看看在React应用程序中实现一种基于路由的安全机制的方法。这个应用程序包含一个主页(/),它有一个不需要安全机制的公共页面( /public ),还有两个需要保护的私有页面( /private1 /private2 ):

我们将使用context(上下文)来构建这个安全机制。context是一个组件可以存储数据的地方,其存储的数据可以供子组件使用。BrowserRouter使用context将路由信息传递给它内部的Route组件。

我们将创建一个名为SecurityContext的自定义context:

这个context的默认值是一个空对象。我们需要给这个context添加函数以用于登录和退出操作,此处是通过创建一个SecurityProvider来实现:

在真实场景中,代码实现会非常不同。你可能会创建一个使用Web服务或第三方安全机制的组件来实现登录和退出操作。但是在我们的示例中,SecurityProvider使用一个简单的loggedIn布尔值跟踪确认我们是否已经登录。SecurityProvider在context中加入了三项内容:

·登录函数(login)

·退出函数(logout)

·一个布尔值,表示我们是否已经登录或退出(loggedIn)

放置在SecurityProvider组件中的任何组件都应该可以访问到这三项内容,因此我们需要添加一个名为useSecurity的自定义Hook:

现在我们有了一个SecurityProvider,我们需要使用它来保护一部分路由。我们将创建另一个名为SecureRoute的组件:

SecureRoute组件从SecurityContext中获取当前loggedIn状态(该状态使用useSecurity Hook获取),如果用户已经登录,SecureRoute组件将渲染该路由的具体内容,如果用户没登录,则只会显示一个登录表单

登录表单(LoginForm组件)调用login函数,如果用户登录成功,那么会导致SecureRoute重新渲染,然后显示被保护的数据。

我们如何使用这些新组件呢?下面是 App.js 文件的更新版本:

SecurityProvider包裹了整个路由功能模块,使得每个SecureRoute都可以访问到login()、logout()和loggedIn。

程序运行起来后如图2-12所示。

图2-12:主页包含了跳转到其他页面的链接

如果我们单击 Public Page 链接,相应页面就会显示(如图2-13所示)。

图2-13:公共页面无须登录即可访问

但是,如果我们单击 Private Page 1 ,那么就会显示登录界面(如图2-14所示)。

图2-14:你需要登录才能看到 Private Page 1

如果你使用用户名 fred 和密码 password 登录,你将看到私有内容(如图2-15所示)。

图2-15:登录后 Private Page 1 的内容

讨论

只有受保护的后端服务才能提供真正的安全机制。但是,安全路由可以防止用户误入无法从服务器读取数据的页面。

SecurityProvider的实现最好应该遵循一些第三方OAuth工具或其他安全服务。但是,通过将SecurityProvider从登录相关页面(Login和Logout)和主应用程序中分离出来,你可以随时更改安全机制的实现,而无须大量更改应用程序中的代码。

如果你想查看组件在用户登录和退出时的行为,你可以创建一个模拟版本的SecurityProvider来用于单元测试。

你可以从GitHub网站( https://oreil.ly/Kut73 )下载本解决方案的源代码。


[1] 在官方文档和本书后文中, /people/:id 这种字符串更具体的称谓是路径,即path。路由的英文是route,也有路径这一含义,但更多指代Route这一组件,path是Route的核心属性。

[2] 我们不会在这里展示PeopleList的代码,但你可以在GitHub仓库( https://oreil.ly/tZzMD )找到。 /qNMCzjEC+uxxbFXBzFfjPgoZWKgtQv9SzzX6ZHxWfrlXf/sTNFbQZ3S5SDo345R

点击中间区域
呼出菜单
上一章
目录
下一章
×