[譯] 用 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前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索