[譯] 用 Auth0 保證 React 應用安全

原文:auth0.com/blog/how-to…css

Auth0

Auth0 是一個全球領先的 Identity-as-a-Service (IDaaS) 服務商,爲數以千計的企業客戶提供現代身份認證解決方案。除了經典的 「用戶名密碼認證過程」 外,Auth0 也容許你增長諸如 「社交媒體登陸」「多因子認證」「無密碼登陸」 等等特性,全部這些只須要一些點擊就能完成。前端

用 Auth0 保證 React 應用安全是十分簡單方便的。react

要完成本文說明的內容,你須要一個 Auth0 帳號。若是你尚未,如今是個 註冊免費 Auth0 帳戶 (auth0.com/signup) 的好時機。npm

同時,若是你想在一個乾淨的環境中完成本章節內容,你能經過一條命令輕易建立一個 React 應用:安全

npx create-react-app react-auth0
複製代碼

而後,進入建立好的 react-auth0 目錄,就能夠按下面的步驟開發了。bash

設立一個 Auth0 應用

要爲你的 React 應用賦予一個 Auth0 帳戶,你須要建立一個 Auth0 Application。因此,根據 manage.auth0.com/#/applicati… 的描述作以下操做:前端框架

  1. 點擊 Create Application 按鈕
  2. 爲你的新應用定義一個 Name (如 "React Demo")
  3. 選擇 Single Page Web Applications 做爲其類型
  4. 點擊 Create 按鈕完成這個過程

在建立應用以後,Auth0 會將你重定向到其 Quick Start tab 頁中。你得點擊到 Settings tab 頁去設置一些白名單 URL 以供 Auth0 在認證過程後調用。這是一項 Auth0 實現的安全性措施,用以免敏感數據泄露(如 ID Tokens)。react-router

因此,當你到達 Settings tab 頁時,尋找到 Allowed Callback URLs 並在其中增長 http://localhost:3000/callback。在本教程中,這個簡單的 URL 就足夠了。app

好了!從 Auth0 的視角看,你已經開始很好的保證你的 React 應用的安全了。框架

依賴和設置

要用 Auth0 保證 React 應用安全,只有三項依賴須要安裝:

  • auth0.js
  • react-router
  • react-router-dom

要安裝這些依賴,到項目根目錄下面執行以下的命令:

npm install --save auth0-js react-router react-router-dom
複製代碼

注意: 若是你想要可得到的最佳安全性,應該依照 auth0.com/docs/univer… 上的說明進行。該方法包括了重定向用戶到一個託管在 Auth0 網站上的登陸頁面,該頁面經過 你的 Auth0 dashboard (manage.auth0.com/) 能夠方便快捷地定製化。若是你想要更多學習這種最佳實踐,可參閱 auth0.com/docs/guides… 頁面。

安裝好這三個庫以後,你就能夠建立一個服務來處理認證過程了。能夠將該服務叫作 Auth 並用以下代碼將其建立到 src/Auth/ 目錄:

// src/Auth/Auth.js
import auth0 from 'auth0-js';

export default class Auth {
  constructor() {
    this.auth0 = new auth0.WebAuth({
      // 必須更新如下三行!
      domain: '<AUTH0_DOMAIN>',
      audience: 'https://<AUTH0_DOMAIN>/userinfo',
      clientID: '<AUTH0_CLIENT_ID>',
      redirectUri: 'http://localhost:3000/callback',
      responseType: 'token id_token',
      scope: 'openid profile'
    });

    this.getProfile = this.getProfile.bind(this);
    this.handleAuthentication = this.handleAuthentication.bind(this);
    this.isAuthenticated = this.isAuthenticated.bind(this);
    this.login = this.login.bind(this);
    this.logout = this.logout.bind(this);
    this.setSession = this.setSession.bind(this);
  }

  getProfile() {
    return this.profile;
  }

  handleAuthentication() {
    return new Promise((resolve, reject) => {
      this.auth0.parseHash((err, authResult) => {
        if (err) return reject(err);
        console.log(authResult);
        if (!authResult || !authResult.idToken) {
          return reject(err);
        }
        this.setSession(authResult);
        resolve();
      });
    })
  }

  isAuthenticated() {
    return new Date().getTime() < this.expiresAt;
  }

  login() {
    this.auth0.authorize();
  }

  logout() {
    // 清除 id token 和過時時間
    this.idToken = null;
    this.expiresAt = null;
  }

  setSession(authResult) {
    this.idToken = authResult.idToken;
    this.profile = authResult.idTokenPayload;
    // 設置 id token 的過時時間
    this.expiresAt = authResult.expiresIn * 1000 + new Date().getTime();
  }
}
複製代碼

你剛剛建立的這個 Auth 服務包含了用於處理登入、登出不一樣步驟的各類函數。下面的列表概述了這些函數:

  • getProfile: 返回已登陸用戶的 profile
  • handleAuthentication: 查找 URL hash 中的認證過程結果。而後,該函數用 auth0-js 中的 parseHash 方法處理結果
  • isAuthenticated: 檢查用戶 ID token 是否過時
  • login: 初始化登陸過程,將用戶重定向到登陸頁面
  • logout: 清除用戶的 tokens 和過時時間
  • setSession: 設置用戶的 ID token、profile 及過時時間

除了這些函數,該類還包含了一個名爲 auth0 的屬性,用來從你的 Auth0 應用中提取初始化值。同時記住你 必須 替換掉其中的 <AUTH0_DOMAIN><AUTH0_CLIENT_ID> 佔位符是重要的。

注意: 對於 <AUTH0_DOMAIN> 佔位符,你得將它替換成相似 your-subdomain.auth0.com 的形式,其中 your-subdomain 是你在建立 Auth0 帳戶(或你的 Auth0 租戶名, auth0.com/docs/gettin…)時選擇的子域名。而對於 <AUTH0_CLIENT_ID>,須要將其替換爲從你以前建立的 Auth0 應用中 Client ID 域中拷貝的隨機字符串。

因爲使用了 Auth0 登陸頁面,用戶會被帶離你的應用。不過,在其認證事後,又會被自動帶回到你以前設置過的回調 URL 上 (也就是 http://localhost:3000/callback)。這意味着你須要建立一個組件來負責這個路由。

因此,建立 src/Callback 目錄並在其中建立一個叫作 Callback.js 的文件,插入以下的代碼:

// src/Callback/Callback.js
import React from 'react';
import { withRouter } from 'react-router';

function Callback(props) {
  props.auth.handleAuthentication().then(() => {
    props.history.push('/');
  });

  return (
    <div> Loading user profile. </div>
  );
}

export default withRouter(Callback);
複製代碼

這個組件,正如你所見,負責觸發 handleAuthentication 過程,並在該過程結束時將用戶帶入主頁。而當該組件處理認證結果的過程當中,只是簡單的顯示了 「loading the user profile」

AuthCallback 組件都建立完畢,就能夠重構 App 組件以整合全部事情了:

// src/App.js

import React from 'react';
import {withRouter} from 'react-router';
import {Route} from 'react-router-dom';
import Callback from './Callback/Callback';
import './App.css';

function HomePage(props) {
  const {authenticated} = props;

  const logout = () => {
    props.auth.logout();
    props.history.push('/');
  };

  if (authenticated) {
    const {name} = props.auth.getProfile();
    return (
      <div> <h1>Howdy! Glad to see you back, {name}.</h1> <button onClick={logout}>Log out</button> </div>
    );
  }

  return (
    <div> <h1>I don't know you. Please, log in.</h1> <button onClick={props.auth.login}>Log in</button> </div>
  );
}

function App(props) {
  const authenticated = props.auth.isAuthenticated();

  return (
    <div className="App">
      <Route exact path='/callback' render={() => (
        <Callback auth={props.auth}/>
      )}/>
      <Route exact path='/' render={() => (
        <HomePage
          authenticated={authenticated}
          auth={props.auth}
          history={props.history}
        />)
      }/>
    </div>
  );
}

export default withRouter(App);
複製代碼

在本例中,實際上你在一個文件中定義了兩個組件(就是爲了簡單)。首先定義一個 HomePage 組件展現已登陸用戶名的信息,以及告知未登陸用戶去登陸的信息。同時,文件中的 App 組件負責決定根據路由哪些子組件必須渲染。

要注意你在全部組件中(AppHomePageCallback)都用到了 Auth 服務。所以你須要這個服務的一個全局實例,而且將其包含在 App 組件中。

因此,要建立這個全局 Auth 實例並整合到應用中,須要更新 index.js 文件:

// src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import Auth from './Auth/Auth';
import './index.css';
import App from './App';

const auth = new Auth();

ReactDOM.render(
  <BrowserRouter> <App auth={auth} /> </BrowserRouter>, document.getElementById('root') ); 複製代碼

這樣就完成了!你已經用 Auth0 保護了你的 React 應用。若是用 npm start 啓動了應用,你將可以藉助 Auth0 的幫助本身實現認證了,也能看到 React 應用顯示了你的名字(若是你的身份提供者確實提供了一個名字的話)。

若是你想學習更多的話,Auth0 官方文檔中也提供了各類前端框架的整合方法:

https://auth0.com/docs/quickstart/spa

擴展閱讀



--End--

查看更多前端好文
請搜索 fewelife 關注公衆號

轉載請註明出處

相關文章
相關標籤/搜索