经过 recompose 实现 react router4 权限

背景

近期作了一个 spa的单独项目中,有个需求就是但愿根据登陆人来看下,这我的是否是有权限进入当前页面。虽然服务端作了进行接口的权限,可是每个路由加载的时候,都要去请求这个接口太浪费了。javascript

须要考虑的

  • 登陆受权,用户没有登陆只能访问登陆页面,若是处于登陆状态则跳转到当前用户的默认首页java

  • 路由受权,当前登陆用户的角色,若是对一个 URL 没有权限访问,则会呈现403react

  • 数据受权,当访问一个没有权限的API,则跳转到 403 页面ios

  • 操做受权,当页面中某个按钮或者区域没有权限访问则在页面中隐藏redux

指望

在路由层面拦截掉,当前这我的对应路由没有权限,面抛出 403,且原组件也不会加载,固然也不会有任何请求axios

准备工做

react router4 、react-router-config、recompose、状态数据管理库(本文用的 rematch )、hoc(高阶组件)api

登陆受权方案

router 早期版本有进入路由的钩子函数能够实现这一点,可是 router4 去掉了采起了新的方式。举个例子,就比如登陆来讲。贴一段伪代码数组

// app.js
  <Router>
        <Switch> { user.userId ? <AuthorizedLayout routes={routes} /> : <UnauthorizedLayout /> } </Switch> </Router>
// AuthorizedLayout.js
<Slider routes={this.state.routes}>
            <Switch> <Route path='/' exact render={() => ( <Redirect to='/Home /> )} /> {renderRoutes(this.state.routes)} </Switch> </Slider> // UnauthorizedLayout .js 就是你的 login 组件 复制代码

路由受权方案

利用高阶组件内部判断 if 返回什么 else 返回什么。 我在下面贴下我怎么实现的。 routes.jsreact-router

/* 和服务端约定的数据格式:[ {url: 'xxx', authuserId: [],authRoleGroup } ] 该路由对应的全部角色或者角色组 */
import { Icon } from 'assets'
import { WithAuthRouter } from '@components'
import Home from './home'
// 这里也能够用异步加载看我的喜爱
const routes = [
  { path: '/Home', /
    exact: true,
    root: true,
    icon: Icon.COLLABORATION,
    title: '主页',
    component: WithAuthRouter(Home)
  }
复制代码

WithAuthRouter.js

这里我在路由层已经把redux 的props 放到路由里面去了。只要被改函数包过的组件均可以不只能够拿到 路由信息,还能够拿到 redux 的信息 这样咱们的组件就不须要 connect, z这样咱们的普通组件就比较纯粹了
同时简单介绍一下我用的几个 recompose 的 api:
  • compose:

    从右往左执行函数组合(右侧函数的输出做为左侧函数的输入
  • onlyUpdateForKeys:

    指定更新键值,不然禁止更新组件
  • branch:

    理解为 if else 返回什么组件
  • renderComponent:

    获取组件并返回该组件的高阶组件
  • renderNothing:

    返回 null
import React from 'react'
import {connect} from 'react-redux'
import { compose, onlyUpdateForKeys, branch, renderComponent } from 'recompose'
import Exception from 'ant-design-pro/lib/Exception'
// 校验路由权限组件
const isNotAuth = props => {
// route 本路由表,user 全局登陆信息、menu 服务端返回的菜单列表
  const { route: { path = null }, user = {}, menu = [] } = props
  const { userId = null, roleMap = [] } = user
  if (userId && roleMap.length) {
    const result = menu.find(i => i.menuUrl === path)
    if (result) {
      return !result.userIdList.includes(userId) && !result.userIdList.filter(i => roleMap.includes(i)).length > 0
    }

    return true
  }

  return true
}

const NotFound = () => (
  <Exception type='403' actions />
)

const renderWhileIsNotAuth = branch(
  isNotAuth,
  renderComponent(NotFound)
)

const mapState = state => ({
  user: state.user,
  menu: state.menu,
  inited: state.inited
})

const getUserAndMenu = compose(
  connect(mapState),
  onlyUpdateForKeys(['user', 'menu'])
)

export default compose(
  getUserAndMenu,
  renderWhileIsNotAuth
)

复制代码

数据受权

我这里用的 业务缘由仅仅是判断登陆失效因此和服务端约定返回失效的code码在请求方发里统一处理掉我这里用的的 axios 由于有个拦截器因此在拦截器里作了app

axios.interceptors.response.use(config => {
  const { data, status } = config
  if (status && status === 200) {
    // 图片上传接口返回的是result
    const { success, response, errorResponse, result } = data
    if (success) {
      return response || result
    } else {
      message.error(errorResponse.msg)
      if (errorResponse && errorResponse.code === 9100) {
        window.location.reload()
      }
      return null
    }
  }
  return config
}, err => {
  console.log(err)
  message.error('发生了未知错误')
  Promise.reject(err)
})
复制代码

操做受权

也是利用高阶组件

// 鉴权组件
import { branch, renderNothing } from 'recompose'
const withAuth = branch(
  props => {
    const getAuth = () => {
      return something
    }
    return !getAuth()
  },
  renderNothing
)
export default withAuth
// 调用
const AuthBtn = Auth(Button)
<AuthBtn
      {...someProps}
        >
          xxx
        </AuthBtn>
复制代码

结语

因此综上看出能够看出核心都在高阶组件,后续我会贴出项目地址, 如今先把核心方法放出来,写的比较仓促欢迎留言指正。

相关文章
相关标签/搜索