- 原文地址:Protected routes and Authentication with React and Node.js
- 原文作者:Strapi
- 譯文出自:掘金翻譯計劃
- 本文永久連結:github.com/xitu/gold-m…
- 譯者:ElizurHz
- 校對者:LeviDing
上週末我想挖掘一些沒有 Redux-Saga 這種花裡胡哨的東西的純粹的 React。
所以我建立了一個小專案,在 Strapi — 一個包括了可擴充套件的管理後臺皮膚和一些內建功能(授權,上傳,許可權控制...)的 Node.js 框架的配合下,僅使用 Create React App 建立一個小模板來實現授權流程。
在本教程中,我們會使用 Strapi 的 API 提供的 JSON Web Tokens 快速地實現基本的授權流程,並且會一步步教大家在 Strapi 中使用第三方登陸授權提供器(Facebook, GitHub, Google...)來授權你的使用者登入(這可能會更有趣)。
注: 本文的原始碼可以在 GitHub 上找到。
建立專案
在開始之前,你需要建立一個 Strapi API:
$ npm install strapi@alpha -g
$ strapi new my-app
$ cd my-app && strapi start
複製程式碼
和你的前端應用:
$ npm install create-react-app -g
$ create-react-app good-old-react-authentication-flow
複製程式碼
你需要 先註冊第一個使用者,然後就可以開始了!
前端應用構架
我是 React Boilerplate 框架的忠實粉絲,所以我建立了一個類似的應用來組織我的程式碼:
/src
└─── containers // 與路由相關的 React 元件
| └─── App // 應用的入口
| └─── AuthPage // 負責所有授權頁面的元件
| └─── ConnectPage // 負責使用第三方提供器進行授權
| └─── HomePage // 只能在使用者登陸後訪問到
| └─── NotFoundPage // 404 元件
| └─── PrivateRoute // 高階元件
|
└─── components // 展示元件
|
└─── utils
└─── auth
└─── request // 使用 fetch 的網路請求輔助庫
複製程式碼
設定路由和 PrivateRoute
為了實現身份驗證的檢視,我們需要先建立一個 HoC:高階元件 來檢查是否使用者可以訪問一個特定的 URL。為此,我們只需要遵循 官方文件,修改 fakeAuth
示例,並使用我們的 auth.js
輔助檔案:
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;
複製程式碼
然後我們來建立路由吧:
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>
);
}
複製程式碼
那麼此時,所有授權使用者需要的檢視都應該已經建立好了!我們只需要進行 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>
);
}
複製程式碼
這些就是我們在前端應用中需要做的,現在只需要配置 Strapi 來啟用第三方授權提供器 ?
設定 Facebook 授權提供器來進行使用者註冊
到 Facebook developers 並且建立一個名叫 test
的應用。
- 在 product 區域新增
Facebook login
- 選擇
Web
- 將 Site URL 設為
http://localhost:3000
- 從 Dashboard 頁面中拷貝 App Id 和 App Secret 到你的應用中
- 在
Facebook login
>Advanced settings
中,新增:http://localhost:1337/connect/facebook/callback
到Valid OAuth redirect URIs
欄位。
配置 Strapi
現在你已經在 Facebook 上建立了一個可以用於配置你專案中 Facebook 提供器的應用。
到 Users & Permissions 區域的 Providers 標籤頁,按照如下所示填寫表單:
不要忘記儲存修改。
結論
我認為這個工作量不大,而且很簡單!你可以在 這裡 找到這個週末我使用 Create React App 建立的模板。
這裡 也有另一個使用 React Boilerplate 的完整的例子,它也是已經完整實現了整個授權的流程。第二個例子使用了 React 和 Redux-Saga,它也是我們用於構建基於 Strapi 的管理後臺的模板。
大家可以分享並在評論中留言!
如果發現譯文存在錯誤或其他需要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可獲得相應獎勵積分。文章開頭的 本文永久連結 即為本文在 GitHub 上的 MarkDown 連結。
掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。