[译] 用 React 和 Node.js 实现受保护的路由和权限验证

上周末我想挖掘一些没有 Redux-Saga 这种花里胡哨的东西的纯粹的 Reactcss

因此我建立了一个小项目,在 Strapi — 一个包括了可扩展的管理后台面板和一些内置功能(受权,上传,权限控制...)的 Node.js 框架的配合下,仅使用 Create React App 建立一个小模板来实现受权流程。前端

React Nodejs

在本教程中,咱们会使用 Strapi 的 API 提供的 JSON Web Tokens 快速地实现基本的受权流程,而且会一步步教你们在 Strapi 中使用第三方登录受权提供器(Facebook, GitHub, Google...)来受权你的用户登陆(这可能会更有趣)。node

Strapi authentication

注: 本文的源代码能够在 GitHub 上找到。react

建立项目

在开始以前,你须要建立一个 Strapi API:android

$ npm install strapi@alpha -g
$ strapi new my-app
$ cd my-app && strapi start
复制代码

和你的前端应用:ios

$ npm install create-react-app -g
$ create-react-app good-old-react-authentication-flow
复制代码

你须要 先注册第一个用户,而后就能够开始了!git

前端应用构架

我是 React Boilerplate 框架的忠实粉丝,因此我建立了一个相似的应用来组织个人代码:github

/src
└─── containers // 与路由相关的 React 组件
|    └─── App // 应用的入口
|    └─── AuthPage // 负责全部受权页面的组件
|    └─── ConnectPage // 负责使用第三方提供器进行受权
|    └─── HomePage // 只能在用户登录后访问到
|    └─── NotFoundPage // 404 组件
|    └─── PrivateRoute // 高阶组件
|
└─── components // 展现组件
|
└─── utils
     └─── auth
     └─── request // 使用 fetch 的网络请求辅助库
复制代码

设置路由和 PrivateRoute

为了实现身份验证的视图,咱们须要先建立一个 HoC高阶组件 来检查是否用户能够访问一个特定的 URL。为此,咱们只须要遵循 官方文档,修改 fakeAuth 示例,并使用咱们的 auth.js 辅助文件:web

import React from 'react';  
import { Redirect, Route } from 'react-router-dom';

// Utils
import auth from '../../utils/auth';

const PrivateRoute = ({ component: Component, ...rest }) => (  
  <Route {...rest} render={props => (
    auth.getToken() !== null ? (
      <Component {...props} />
    ) : (
      <Redirect to={{
        pathname: 'auth/login',
        state: { from: props.location }
        }}
      />
    ):
  )} />
);

export default PrivateRoute;  
复制代码

而后咱们来建立路由吧:npm

import React, { Component } from 'react';  
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';

// Components
import AuthPage from '../../containers/AuthPage';  
import ConnectPage from '../../containers/ConnectPage';  
import HomePage from '../../containers/HomePage';  
import NotFoundPage from '../../containers/NotFoundPage';

// 这个组件是用于防止未登陆用户访问特定路由的高阶组件
import PrivateRoute from '../../containers/PrivateRoute';

// Design
import './styles.css';

class App extends Component {  
  render() {
    return (
      <Router>
        <div className="App">
          <Switch>
            {/* A user can't go to the HomePage if is not authenticated */} <PrivateRoute path="/" component={HomePage} exact /> <Route path="/auth/:authType/:id?" component={AuthPage} /> <Route exact path="/connect/:provider" component={ConnectPage} /> <Route path="" component={NotFoundPage} /> </Switch> </div> </Router> ); } } export default App; 复制代码

建立受权视图

如今全部须要用于建立视图的路由都已经实现了。 咱们声明路由的方式容许咱们建立一个可以根据 路径 建立正确的表单的组件。

首先,让咱们建立 forms.json 来处理在每一个 auth 视图中建立表单的操做:

  • forgot-password
  • login
  • register
  • reset-password

JSON 结构以下所示(你能够发如今 Input 组件中 customBootstrapClass 这个熟悉是必需的):

{
  "views": {    
    "login": [
      {
        "customBootstrapClass": "col-md-12",
        "label": "Username",
        "name": "identifier",
        "type": "text",
        "placeholder": "johndoe@gmail.com"
      },
      {
        "customBootstrapClass": "col-md-12",
        "label": "Password",
        "name": "password",
        "type": "password"
      },
      {
        "customBootstrapClass": "col-md-6",
        "label": "Remember me",
        "name": "rememberMe",
        "type": "checkbox"
      }
    ]
  },
  "data": {
    "login": {
      "identifier": "",
      "password": "",
      "rememberMe": false
    }
  }
}
复制代码

当路由变化时设置 state

若是要在用户从路由 auth/login 切换到路由 auth/register 时设置表单,咱们须要使用如下生命周期:

componentDidMount() {  
  // 使用一个函数生成表单以防
  // 表单在其余生命周期里重复
  this.generateForm(this.props);
}
复制代码
componentWillReceiveProps(nextProps) {  
  // 由于咱们对全部的 auth 视图使用一样的容器
  // 因此咱们须要在路径改变的时候更新 UI
  if (nextProps.location.match.params.authType !== this.props.location.match.params.authType) {
    this.generateForm(nextProps);
  }
}
复制代码

generateForm 方法负责从上面的 forms.json 文件中获取数据。

建立视图

要建立表单,咱们只须要映射 forms.json 中的数据。

handleChange = ({ target }) => this.setState({ value: { ...this.state.value, [target.name]: target.value } });

render() {  
  const inputs = get(forms, ['views', this.props.match.params.authType, []);

  return (
    <div>
      <form onSubmit={this.handleSubmit}>
        {inputs.map((input, key) => (
          <Input
            autoFocus={key === 0}
            key={input.name}
            name={input.name}
            onChange={this.handleChange}
            type={input.type}
            value={get(this.state.value, [input.name], '')}
          />
        ))}
        <Button type="submit" />
      </form>
    </div>
  );
}
复制代码

Strapi login view

那么此时,全部受权用户须要的视图都应该已经建立好了!咱们只须要进行 API 调用便可访问该应用。

将数据发布到 API

为了进行 API 调用,我写了一个 request 的辅助文件(你能够在这里访问 demo app),咱们只须要在咱们的 handleSubmit 函数中使用它:

handleSubmit = (e) => {  
  e.preventDefault();
  const body = this.state.value;
  const requestURL = 'http://localhost:1337/auth/local';

  request(requestURL, { method: 'POST', body: this.state.value})
    .then((response) => {
      auth.setToken(response.jwt, body.rememberMe);
      auth.setUserInfo(response.user, body.rememberMe);
      this.redirectUser();
    }).catch((err) => {
      console.log(err);
    });
}

redirectUser = () => {  
  this.props.history.push('/');
}
复制代码

这里没有什么花里胡哨的操做,当咱们得到了 API 的响应后,咱们只要将所需的信息存到 localStorage 或者 sessionStorage 中,而后咱们能够将用户重定向至 HomePage。

咱们刚实现了最困难的部分,由于使用像 Facebook 这样的第三方受权提供器很是容易!

使用受权提供器

不管你选择 Facebook、GitHub 仍是 Google,在 Strapi 使用第三方受权提供器来受权你的用户登录是很是简单的 🙈。在这个例子中,我将为你们展现怎样使用 Facebook 的第三方受权提供器。

由于 Strapi()没有提供 Javascript SDK 来对接 Strapi 的 API 和 Facebook 的 API。

具体流程以下:

  • 用户“点击使用 Facebook 登陆”
  • 将用户重定向至另外一个页面,在那里他能够进行受权
  • 受权以后,Facebook 会将用户重定向到你的应用里,并带在 URL 中附带一个 code
  • 把这个 code 发送给 Strapi

此时,咱们只须要在 componentDidMount 生命周期中发起 API 的请求,而后根据 ConnectPage 容器中的响应内容将用户重定向至相应页面:

componentDidMount() {  
  const { match: {params: { provider }}, location: { search } } = this.props;
  const requestURL = `http://localhost:1337/auth/${provider}/callback${search}`;

 request(requestURL, { method: 'GET' })
   .then((response) => {
      auth.setToken(response.jwt, true);
      auth.setUserInfo(response.user, true);
      this.redirectUser('/');
   }).catch(err => {
      console.log(err.response.payload)
      this.redirectUser('/auth/login');
   });
}

redirectUser = (path) => {  
  this.props.history.push(path);
}
复制代码

在 AuthPage 中显示受权提供器

为此,咱们须要一个以下所示的 SocialLink 组件:

/**
*
* SocialLink
*
*/

import React from 'react';  
import PropTypes from 'prop-types';

import Button from '../../components/Button'

function SocialLink({ provider }) {  
  return (
    <a href={`http://localhost:1337/connect/${provider}`} className="link">
      <Button type="button" social={provider}>
        <i className={`fab fa-${provider}`} />
        {provider}
      </Button>
    </a>
  );
}

SocialLink.propTypes = {  
  provider: PropTypes.string.isRequired,
};

export default SocialLink;
复制代码

而后咱们须要把它加入到 AuthPage 中:

render() {  
  const providers = ['facebook', 'github', 'google', 'twitter']; // 若是要把一个提供器移除,只要把它从这个数组中删除便可...

  return (
     <div>
       {providers.map(provider => <SocialLink provider={provider} key={provider} />)}
       {/* Some other code */}
     </div>
  );
}
复制代码

Login page

这些就是咱们在前端应用中须要作的,如今只须要配置 Strapi 来启用第三方受权提供器 😎

设置 Facebook 受权提供器来进行用户注册

Facebook developers 而且建立一个名叫 test 的应用。

  • 在 product 区域添加 Facebook login
  • 选择 Web
  • 将 Site URL 设为 http://localhost:3000

Facebook setup

  • 从 Dashboard 页面中拷贝 App Id 和 App Secret 到你的应用中

Facebook setup

  • Facebook login > Advanced settings 中,添加:http://localhost:1337/connect/facebook/callbackValid OAuth redirect URIs 字段。

Facebook setup

配置 Strapi

如今你已经在 Facebook 上建立了一个能够用于配置你项目中 Facebook 提供器的应用。

Users & Permissions 区域的 Providers 标签页,按照以下所示填写表单:

Admin FB setup

不要忘记保存修改。

结论

但愿这个小教程能够帮助你使用 ReactStrapi 进行用户受权登录。

我认为这个工做量不大,并且很简单!你能够在 这里 找到这个周末我使用 Create React App 建立的模板。

这里 也有另外一个使用 React Boilerplate 的完整的例子,它也是已经完整实现了整个受权的流程。第二个例子使用了 React 和 Redux-Saga,它也是咱们用于构建基于 Strapi 的管理后台的模板。

你们能够分享并在评论中留言!

若是发现译文存在错误或其余须要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可得到相应奖励积分。文章开头的 本文永久连接 即为本文在 GitHub 上的 MarkDown 连接。


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOS前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏

相关文章
相关标签/搜索