登录访问控制 - 鉴权(React)

·  阅读 2547

对于一些 PC 端项目来说,

  • 有的页面不需要登录就可以访问,比如,登录页
  • 有的页面需要登录后才能访问,比如,项目后台首页、内容管理等(除了登录页面,其他页面需要登录才能访问)

因此,就需要对项目进行登录访问控制,让需要登录才能访问的页面,必须在登录后才能访问。 在没有登录时,直接跳转到登录页面,让用户进行登录。

  • 如何实现登录访问控制呢?

    • 分析:不管哪个页面都是通过路由来访问的,因此,需要从路由角度来进行控制

    • 思路:创建 AuthRoute 组件,判断是否登录,

      1 登录直接显示要访问的页面 
      2 没有登录跳转到登录页面
      复制代码

难点:react中没有导航守卫,需要自己封装

访问控制-用Route组件的render来解决

Route的作用是:当前path是/login能成功时,渲染组件。

 <Route path="/login" component={}>
   
复制代码

而我们的要求是:

当前path是/login 并且localStorage已经登录能成功时,渲染组件,否则渲染无权限组件。

render的基本用法

render接收一个函数。它的入参很重要。

 <Route path="/home" render={(props)=>{
   console.log(props); 
   return <Layout {...props}/>
 }
 }
复制代码

如果不补充 props给Layout组件,则渲染时,不会

权限控制

 <Route path="/home" render={(props)=>{
   // itcast_geek_pc
   if(localStorage.getItem('itcast_geek_pc')) {
     return <Layout {...props}/>
   } else {
     return (<Redirect to="/login"/>)
   }
 }}></Route>
复制代码

访问控制-AuthRoute组件封装

分析 AuthRoute 鉴权路由组件

  • 场景:限制某个页面只能在登录的情况下访问。
  • 说明:在 React 路由中并没有直接提供该组件,需要手动封装,来实现登录访问控制(类似于 Vue 路由的导航守卫)。
  • 如何封装?参考 react-router-dom 文档中提供的鉴权示例 。
  • 如何使用?使用 AuthRoute 组件代替默认的 Route 组件,来配置路由规则。
  • AuthRoute 组件实际上就是对原来的 Route 组件做了一次包装,来实现了一些额外的功能。
  • <Route path component render> render 方法,指定该路由要渲染的组件内容(类似于 component 属性)。
  • Redirect 组件:重定向组件,通过 to 属性,指定要跳转到的路由信息。
  • state 属性:表示给路由附加一些额外信息,此处,用于指定登录成功后要进入的页面地址。
 // 使用方式:
 <AuthRoute path="/rent/add" component={Rent} />
复制代码

实现自己的AuthRoute组件

  • 处理token的方法
 // 封装本地存储的操作
const TOKEN_KEY = 'itcast_geek_pc'

// 获取 token
export function getToken () {
  return localStorage.getItem(TOKEN_KEY)
}

// 本地存储 token
export function setToken (token) {
  localStorage.setItem(TOKEN_KEY, token)
}

// 删除 token
export function removeToken () {
  localStorage.removeItem(TOKEN_KEY)
}

// 判断有无 token
export function hasToken () {
  return !!getToken()
}

复制代码
  • 权限判断
import { hasToken } from '../utils/storage'
import { Redirect, Route } from 'react-router-dom'

import React from 'react'
import PropTypes from 'prop-types'

export default function AuthRoute (props) {
  console.log(props)
  const Com = props.component
  return (
    <Route
      path={props.path}
      render={() => {
        // 判断有无 token
        if (hasToken()) {
        //  有 token, 表示已经登录,可以访问需要登录才能访问的页面(如首页...)
          return <Com />
        } else {
          // 没有 token ,表示未登录,不能访问主页,跳转登录页
          return <Redirect to="/login" />
        }
      }}
    />
  )
}

AuthRoute.propTypes = {
  path: PropTypes.string.isRequired,
  component: PropTypes.func.isRequired
}

复制代码
  • 使用AuthRoute
 {/* 路由规则 */}
 <Switch>
   // 导入封装的访问控制组件,替换原来的 Route 
   <AuthRoute path="/home" component={Layout}></AuthRoute>
   <Route path="/login" component={Login}></Route>
 </Switch>
复制代码
  • 登录成功处理
 submit = async (values) => {
   const { mobile, code } = values
   console.log(this.props)
   try {
     const res = await login(mobile, code)
     // 存储token
     // localStorage.setItem('itcast_geek_pc', res.data.token)
     setToken(res.data.token)
     // 跳转到首页
     const { state } = this.props.location
     if (state) {
       this.props.history.push(state.from.pathname)
     } else {
       this.props.history.push('/home')
     }
     message.success('登录成功', 1)
   } catch (err) {
     message.warning(err.response.data.message, 1)
   }
 }
复制代码

route组件的作用

 <Route path="/home" component={Home}></Route>
 <Route path="/login" component={Login}></Route>
复制代码

Route组件会根据当前地址的中地址 和 Route的path进行匹配,,,如果路径一致,那么这个对应的组件就会被渲染出来

Route没有判断用户是否登录的能力,只会根据path判断是否要渲染对应的组件。

需求:让Route组件能够有逻辑,能够判断用户是否登录,需要通过Route组件的render属性

处理history

(我的另一篇博客有详细介绍-非组件获取路由信息-)

优化页面跳转

封装history模块

  • history处理 utils/history.js
 import { createHashHistory } from 'history'
 ​
 const history = createHashHistory()
 ​
 export default history
复制代码

App.js修改

 import history from 'utils/history'
 ​
 <Router history={history}>
 </Router>
复制代码

修改响应拦截器

  • 用history.push来替代
 import history from './history'
 // 响应拦截器
 instance.interceptors.response.use(
   function (response) {
     return response.data
   },
   function (error) {
     console.log(error.response)
     if (error.response.status === 401) {
       // token过期
       removeToken()
       // 跳转到登录页
 +     history.push('/login')
       message.warning('用户信息已过期')
     }
     return Promise.reject(error)
   }
 )
复制代码
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改