- 原文地址:Protected routes and Authentication with React and Node.js
- 原文做者:Strapi
- 譯文出自:掘金翻譯計劃
- 本文永久連接:github.com/xitu/gold-m…
- 譯者:ElizurHz
- 校對者:LeviDing
上週末我想挖掘一些沒有 Redux-Saga 這種花裏胡哨的東西的純粹的 React。css
因此我建立了一個小項目,在 Strapi — 一個包括了可擴展的管理後臺面板和一些內置功能(受權,上傳,權限控制...)的 Node.js 框架的配合下,僅使用 Create React App 建立一個小模板來實現受權流程。前端
在本教程中,咱們會使用 Strapi 的 API 提供的 JSON Web Tokens 快速地實現基本的受權流程,而且會一步步教你們在 Strapi 中使用第三方登錄受權提供器(Facebook, GitHub, Google...)來受權你的用戶登陸(這可能會更有趣)。node
注: 本文的源代碼能夠在 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 的網絡請求輔助庫
複製代碼
爲了實現身份驗證的視圖,咱們須要先建立一個 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 視圖中建立表單的操做:
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。
具體流程以下:
此時,咱們只須要在 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 developers 而且建立一個名叫 test
的應用。
Facebook login
Web
http://localhost:3000
Facebook login
> Advanced settings
中,添加:http://localhost:1337/connect/facebook/callback
到 Valid OAuth redirect URIs
字段。如今你已經在 Facebook 上建立了一個能夠用於配置你項目中 Facebook 提供器的應用。
到 Users & Permissions 區域的 Providers 標籤頁,按照以下所示填寫表單:
不要忘記保存修改。
我認爲這個工做量不大,並且很簡單!你能夠在 這裏 找到這個週末我使用 Create React App 建立的模板。
這裏 也有另外一個使用 React Boilerplate 的完整的例子,它也是已經完整實現了整個受權的流程。第二個例子使用了 React 和 Redux-Saga,它也是咱們用於構建基於 Strapi 的管理後臺的模板。
你們能夠分享並在評論中留言!
若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。