使用React-Router實現前端路由鑑權

React-Router是React生態裏面很重要的一環,如今React的單頁應用的路由基本都是前端本身管理的,而不像之前是後端路由,React管理路由的庫經常使用的就是React-Router。本文想寫一下React-Router的使用,可是光介紹API又太平淡了,並且官方文檔已經寫得很好了,我這裏就用一個常見的開發場景來看看React-Router是怎麼用的吧。咱們通常的系統都會有用戶訪問權限的限制,某些頁面可能須要用戶具備必定的權限才能訪問。本文就是用React-Router來實現一個前端鑑權模型。javascript

本文所有代碼已經上傳GitHub,你們能夠拿下來玩玩:https://github.com/dennis-jiang/Front-End-Knowledges/tree/master/Examples/React/react-router-usage前端

應用示例

本文要實現的功能是你們常常遇到的場景,就是要控制不一樣的用戶角色來訪問不一樣的頁面,這裏總共有四個頁面:java

  1. /index: 網站首頁
  2. /login: 登陸頁
  3. /backend:後臺頁面
  4. /admin:管理頁面

另外還有三種角色:react

  1. 未登陸用戶:只能訪問網站首頁/index和登陸頁/login
  2. 普通用戶:能夠訪問網站首頁/index,登陸頁/login和後臺頁面/backend
  3. 管理員:能夠訪問管理頁面/admin和其餘全部頁面

引入React-Router

要實現路由鑑權,咱們還得一步一步來,咱們先用React-Router搭建一個簡單的帶有這幾個頁面的項目。咱們直接用create-react-app建立一個新項目,而後建了一個pages文件夾,裏面放入咱們前面說的那幾個頁面:git

image-20200710152739352

咱們頁面先寫簡單點,先寫個標題吧,好比這樣:github

import React from 'react';

function Admin() {
  return (
    <h1>管理員頁面</h1>
  );
}

其餘幾個頁面也是相似的。web

而後咱們就能夠在App.js裏面引入React-Router作路由跳轉了,注意咱們在瀏覽器上使用的是react-router-dom,新版的React-Router將核心邏輯層和展現層分開了,核心邏輯會處理路由匹配等,展現層會處理實際的跳轉和路由變化的監聽,之因此這麼分,是由於React-Router不只僅須要支持瀏覽器,還須要支持React Native,這兩個平臺的監聽和跳轉是不同的,因此如今React-Router下面有好幾個包了:後端

react-router:核心邏輯處理,提供一些公用的基類

react-router-dom:具體實現瀏覽器相關的路由監聽和跳轉數組

react-router-native:具體實現RN相關的路由監聽和跳轉瀏覽器

在實際使用時,咱們通常不須要引用react-router,而是直接用react-router-dom就行,由於它本身會去引用react-router。下面咱們在項目裏面引入react-router-dom

import React from 'react';
import {
  BrowserRouter as Router,
  Switch,
  Route,
} from "react-router-dom";
import Home from './pages/Home';
import Login from './pages/Login';
import Backend from './pages/Backend';
import Admin from './pages/Admin';

function App() {
  return (
    <Router>
      <Switch>
        <Route path="/login" component={Login}/>
        <Route path="/backend" component={Backend}/>
        <Route path="/admin" component={Admin}/>
        <Route path="/" component={Home}/>
      </Switch>
    </Router>
  );
}

export default App;

而後能夠在Home頁面用Link加上跳轉到其餘頁面的連接,這樣就能夠跳轉了:

import React from 'react';
import { Link } from 'react-router-dom';

function Home() {
  return (
    <>
      <h1>首頁</h1>
      <ul>
        <li><Link to="/login">登陸</Link></li>
        <li><Link to="/backend">後臺</Link></li>
        <li><Link to="/admin">管理員</Link></li>
      </ul>
    </>
  );
}

export default Home;

到如今咱們的應用運行起來是這樣的:

Jul-10-2020 17-35-36

模塊劃分

雖然咱們的跳轉實現了,可是全部人均可以訪問任何頁面,咱們前面的需求是要根據登陸的角色限制訪問的頁面的,在寫代碼前,咱們先來思考下應該怎麼作這個。固然最直觀最簡單的方法就是每一個頁面都檢測下當前用戶的角色,匹配不上就報錯或者跳回首頁。咱們如今只有幾個頁面,這樣作好像也還好,可是若是咱們的應用變大了,頁面變多了,每一個頁面都來一次檢測就顯得很重複了,因此咱們應該換個角度來思考這個問題。

仔細一看,其實咱們總共就三種角色,對應三種不一樣的權限,這三個權限還有層級關係,高級別的權限包含了低級別的權限,因此咱們的頁面也能夠按照這些權限分爲三種:

  1. 公共頁面:全部人均可以訪問,沒登陸也能夠訪問,包括網站首頁和登陸頁
  2. 普通頁面:普通登陸用戶能夠訪問的頁面
  3. 管理員頁面:只有管理員才能訪問的頁面

爲了好管理這三種頁面,咱們能夠將他們抽取成三個文件,放到一個獨立的文件夾routes裏面,三個文件分別命名爲publicRoutes.jsprivateRoutes.jsadminRoutes.js

image-20200721170127221

對於每一個路由文件,咱們能夠將這類路由組織成數組,而後export出去給外面調用,好比publicRoutes.js

import Login from '../pages';
import Home from '../pages/Home';

const publicRoutes = [
  {
    path: '/login',
    component: Login,
    exact: true,
  },
  {
    path: '/',
    component: Home,
    exact: true,
  },
];

export default publicRoutes;

而後咱們外面使用的地方直接改成:

import publicRoutes from './routes/publicRoutes';

function App() {
  return (
    <Router>
      <Switch>
        {publicRoutes.map(
          ({path, component, ...routes}) => 
            <Route key={path} path={path} component={component} {...routes}/>
        )}
        <Route path="/backend" component={Backend}/>
        <Route path="/admin" component={Admin}/>
      </Switch>
    </Router>
  );
}

這樣咱們的App.js裏面就不會有冗長的路由路由列表了,而是隻須要循環一個數組就好了。可是對於須要登陸才能訪問的頁面和管理員頁面咱們不能直接渲染Route組件,咱們最好再封裝一個高級組件,將鑑權的工做放到這個組件裏面去,這樣咱們普通的頁面在實現時就不須要關心怎麼鑑權了。

封裝高級組件

要封裝這個鑑權組件思路也很簡單,前面咱們將publicRoutes直接拿來循環渲染了Route組件,咱們的鑑權組件只須要在這個基礎上再加一個邏輯就好了:在渲染真正的Route組件前先檢查一下當前用戶是否有對應的權限,若是有就直接渲染Route組件,若是沒有就返回某個頁面,能夠是登陸頁或者後臺首頁,具體根據本身項目需求來。因此咱們的路由配置文件privateRoutes.jsadminRoutes.js裏面的路由會比publicRoutes.js的多兩個參數:

// privateRoutes.js
import Backend from '../pages/Backend';

const privateRoutes = [
  {
    path: '/backend',
    component: Backend,
    exact: true,
    role: 'user',       // 當前路由須要的角色權限
    backUrl: '/login'   // 不知足權限跳轉的路由
  },
];

export default privateRoutes;

adminRoutes.js是相似的寫法:

// adminRoutes.js
import Admin from '../pages/Admin';

const adminRoutes = [
  {
    path: '/admin',
    component: Admin,
    exact: true,
    role: 'admin',       // 須要的權限是admin
    backUrl: '/backend'  // 不知足權限跳回後臺頁面
  },
];

export default adminRoutes;

而後就能夠寫咱們的高級組件了,咱們將它命名爲AuthRoute吧,注意咱們這裏假設的用戶登陸時後端API會返回給咱們當前用戶的角色,一個用戶可能有多個角色,好比普通用戶的角色是['user'],管理員的角色是['user', 'admin'],具體的權限驗證邏輯要看本身項目權限的設計,這裏只是一個例子:

// AuthRoute.js
import React from 'react';
import { Route, Redirect } from 'react-router-dom';

function AuthRoute(props) {
  const {
    user: {
      role: userRole
    },
    role: routeRole,
    backUrl,
    ...otherProps
  } = props;

  // 若是用戶有權限,就渲染對應的路由
  if (userRole && userRole.indexOf(routeRole) > -1) {
    return <Route {...otherProps} />
  } else {
    // 若是沒有權限,返回配置的默認路由
    return <Redirect to={backUrl} />
  }
}

export default AuthRoute;

而後用咱們的AuthRoute的渲染adminRoutesprivateRoutes:

// ... 省略其餘代碼 ...

{privateRoutes.map(
  (route) => <AuthRoute key={route.path} {...route}/>
)}
{adminRoutes.map(
  (route) => <AuthRoute key={route.path} {...route}/>
)}

登陸設置權限

在咱們的AuthRoute裏面用到了user: { role }這個變量,可是咱們還沒設置它。真實項目中通常是登陸的時候後端API會返回當前用戶的角色,而後前端將這個權限信息保存在一些狀態管理工具裏面,好比Redux。咱們這裏直接在Login頁面寫死兩個按鈕來模擬這個權限了,用戶的配置就用根組件的state來管理了,Login頁面的兩個按鈕會改變對應的state

import React from 'react';
import { Link } from 'react-router-dom';

function Login(props) {
  const {loginAsUser, loginAsAdmin, history} = props;

  const userLoginHandler = () => {
    loginAsUser();      // 調用父級方法設置用戶權限
    history.replace('/backend');     // 登陸後跳轉後臺頁面
  }

  const adminLoginHandler = () => {
    loginAsAdmin();     // 調用父級方法設置管理員權限
    history.replace('/admin');     // 登陸後跳轉管理員頁面
  }

  return (
    <>
      <h1>登陸頁</h1>
      <button onClick={userLoginHandler}>普通用戶登陸</button>
      <br/><br/>
      <button onClick={adminLoginHandler}>管理員登陸</button>
      <br/><br/>
      <Link to="/">回首頁</Link>
    </>
  );
}

export default Login;

到這裏咱們這個簡單的路由鑑權就完成了,具體跑起來效果以下:

Jul-23-2020 20-40-37

本文所有代碼已經上傳GitHub,你們能夠拿下來玩玩:https://github.com/dennis-jiang/Front-End-Knowledges/tree/master/Examples/React/react-router-usage

總結

  1. React-Router能夠用來管理前端的路由跳轉,是React生態裏面很重要的一個庫。
  2. React-Router爲了同時支持瀏覽器和React-Native,他分拆成了三個包react-router核心包,react-router-dom瀏覽器包,react-router-native支持React-Native。使用時不須要引入react-router,只須要引入須要的平臺包就行。
  3. 對於須要不一樣權限的路由,咱們能夠將他們拎出來分好類,單獨建成一個文件,若是路由很少,放在一個文件導出多個數組也行。
  4. 對於須要鑑權的路由,咱們能夠用一個高級組件將權限校驗的邏輯封裝在裏面,其餘頁面只須要加好配置,徹底不用關心鑑權的問題。

本文內容偏簡單,做爲熟悉React-Router的用法還不錯,可是咱們不能只會用,還要知道他的原理。下篇文章咱們就來看看React-Router的源碼裏面蘊藏了什麼奧祕,你們能夠點個關注不迷路,哈哈~

參考資料

官方文檔:https://reactrouter.com/web/guides/quick-start

GitHub源碼地址:https://github.com/ReactTraining/react-router/tree/master/packages

文章的最後,感謝你花費寶貴的時間閱讀本文,若是本文給了你一點點幫助或者啓發,請不要吝嗇你的贊和GitHub小星星,你的支持是做者持續創做的動力。

做者博文GitHub項目地址: https://github.com/dennis-jiang/Front-End-Knowledges

相關文章
相關標籤/搜索