【React系列】手把手带你撸后台系统(Redux与路由鉴权)

来源:https://juejin.im/post/5d9b5ddee51d45781b63b8f7

上一篇我们介绍了系统架构,这一篇将继续介绍:

  • Redux的应用
  • 登录授权
  • 路由鉴权

二、Redux应用

侧边导航栏(Sidebar)我们实现了根据配置渲染菜单项,现在我们要继续完善它的功能:导航高亮与鉴权。我们通过redux管理我们Sidebar的状态,想要redux的store可用,我们必须使用它的和connect():

// Page.jsimport { createStore } from 'redux'import store from 'store' // 新建store目录ReactDOM.render(, document.getElementById('root'))复制代码

的作用是让store在整个App中可用,connect()的作用是将store和component连接起来。

// Sidebar.jsimport { connect } from 'react-redux'import { updateSidebarState } from 'store/action'class Sidebar extends Component { // 省略代码...}const mapStateToProps = (state, owns) => ({ sidebar: prop('sidebarInfo', state), // 从store中取出sidebarInfo数据 ...owns})const mapDispatchToProps = dispatch => ({ dispatchSideBar: sidebar => dispatch(updateSidebarState(sidebar)) // 更新数据状态})export default connect( mapStateToProps, mapDispatchToProps)(SideBar)复制代码

2.1 初始化Sidebar数据

初始化Sidebar数据主要做两点:

  • 默认的路由配置数据里面没有标识高亮状态,初始化过程增加active标志位;
  • 为父级导航项增加key值标志位,用于检测收合状态;
// store/reducer.jsfunction initSidebar(arr) { return map(each => { if(each.routes) { each.active = false each.key = generateKey(each.routes) // 生产唯一key值,与路由path关联 each.routes = initSidebar(each.routes) } return each }, arr)}// 更新sidebar状态function updateSidebar(arr, key='') { return map(each => { if(key === each.key) { each.active = !!!each.active } else if(each.routes) { each.routes = updateSidebar(each.routes, key) } return each }, arr)}export const sidebarInfo = (state=initSidebar(routes), action) => { switch (action.type) { case 'UPDATE': return updateSidebar(state, action.key) default: return state }}复制代码

经处理后的路由配置数据新增了active和key两个属性:

2.2 检测高亮渲染侧边导航栏过程需要检测高亮状态:根据当前路由path与导航项的key值相比较:

// Sidebar.jsclass Sidebar extends Component { constructor(props) { // ... this.state = { routeName: path(['locaotion', 'pathname'], this.props), // 获取当前路由信息 routes: compose(this.checkActive.bind(this), prop('sidebar'))(this.props) // 返回检测后的路由数据 } } // 省略代码... checkActive (arr, routeName='') { const rName = routeName || path(['location', 'pathname'], this.props) if(!rName) return arr return map((each) => { const reg = new RegExp(rName) if(reg.test(each.key)) { each.active = true } else if (each.routes) { each.routes = this.checkActive(each.routes, rName) } return each }, arr) }}export default connect( mapStateToProps, mapDispatchToProps)(withRouter(SideBar))复制代码

特别注意: Sidebar组件需要经由withRouter包裹后才能在组件内获取路由相关信息。

三、登录授权

这里设定的场景是:用户的登录数据在当前会话期内有效(sessionStorage存储用户信息),用户信息全局可用(redux管理)。假定我们存储的用户数据有:

{ username: '', // 帐号 permission: [], // 用户权限列表 isAdmin: false // 管理员标识}复制代码

3.1 初始化用户信息

// store/reducer.jsconst userInfo = getSessionStore('user') || { // 首先从sessionStorage中获取数据 username: '', permission: [], isAdmin: false}export const user = (state=userInfo, action) => { switch (action.type) { case 'LOGIN':  return pick(keys(state), action.user) default: return state }}复制代码

3.2 实现登录

首先将store的state和action注入login组件:

import { connect } from 'react-redux'import { doLogin } from 'store/action'import Login from './login'const mapStateToProps = (state, owns) => ({ user: state, ...owns})const mapDispatchToProps = dispatch => ({ dispatchLogin: user => dispatch(doLogin(user))})export default connect( mapStateToProps, mapDispatchToProps)(Login)复制代码

继而在login.js中实现登录逻辑:

class Login extends Component { login () { const { dispatchLogin, history } = this.props const { form } = this.state const user = { username: '安歌', permission: ['add'], isAdmin: false } dispatchLogin(user) // 更新store存储的用户数据 setSessionStore('user', user) // 将用户数据存储在sessionStorage // login success history.push('/front/approval/undo') // 登录重定向 } }复制代码

四、路由鉴权

上一篇中我们实现了页面级路由,现在我们需要根据路由配置文件,注册应用级路由。回顾下我们的路由配置:

export default [ { title: '我的事务', // 页面标题&一级nav标题 icon: 'icon-home', routes: [{ name: '待审批', path: '/front/approval/undo', component: 'ApprovalUndo' }, { name: '已处理', path: '/front/approval/done', auth: 'add', component: 'ApprovalDone' }] }]复制代码

我们根据path和component信息注册路由,根据auth信息进行路由鉴权。看下我们如何实现各个路由对应的组件。

在views目录存放了我们所有的页面应用组件,index.js则作为组件的入口文件:// views/index.jsimport AsyncComponent from 'components/AsyncComponent'const ApprovalUndo = AsyncComponent(() => import(/* webpackChunkName: "approvalundo" */ 'views/approval/undo'))const ApprovalDone = AsyncComponent(() => import(/* webpackChunkName: "approvaldone" */ 'views/approval/done'))export default { ApprovalUndo, ApprovalDone}复制代码

说明: 关于AsyncComponent的说明已在上篇有所介绍。

4.1 注册路由

// router/index.js import routesConf from './config'import views from 'views'class CRouter extends Component { render () { return (  { pipe(map(each => { const routes = each.routes return this.generateRoute(routes, each.title) }), flatten)(routesConf) }  } />  ) } generateRoute (routes=[], ) { // 递归注册路由 return map(each => each.component ? (  { const reg = /?S*g/ const queryParams = window.location.hash.match(reg) // 匹配路由参数 const { params } = props.match Object.keys(params).forEach(key => { // 去除?参数 params[key] = params[key] && params[key].replace(reg, '') }) props.match.params = { ...params } const merge = { ...props, query: queryParams ? queryString.parse(queryParams[0]) : {} } const View = views[each.component] const wrapperView = ( // 包装组件设置标签页标题  ) return wrapperView } } /> ) : this.generateRoute(each.routes, title), routes) }}const mapStateToProps = (state, owns) => ({ user: prop('user', state), ...owns})export default connect( mapStateToProps)(CRouter)复制代码

我们的路由配置文件支持多级嵌套,递归注返回的Route路由也是嵌套的数组,最后需要借助flatten将整个路由数组打平。

4.2 权限管理

权限管理分为登录校验和权限校验,默认我们的应用路由都是需要登录校验的。

class CRouter extends Component { generateRoute (routes=[], ) { // ... // 在上一个版本中直接将wrapperView返回,这个版本包裹了一层登录校验 return this.requireLogin(wrapperView, each.auth) } requireLogin (component, permission) { const { user } = this.props const isLogin = user.username || false // 登录标识, 从redux取 if(!isLogin) { // 判断是否登录 return  } // 如果当前路由存在权限要求,则再进入全权限校验 return permission ? this.requirePermission(component, permission) : component } requirePermission (component, permission) { const permissions = path(['user', 'permission'], this.props) // 用户权限, 从redux取 if(!permissions || !this.checkPermission(permission, permissions)) return  return component } checkPermission (requirePers, userPers) { const isAdmin = path(['user', 'isAdmin'], this.props) // // 超管标识, 从redux取 if(isAdmin) return true if(typeof userPers === 'undefined') return false if(Array.isArray(requirePers)) { // 路由权限为数组 return requirePers.every(each => userPers.includes(each)) } else if(requirePers instanceof RegExp) { // 路由权限设置为正则 return userPers.some(each => requirePers.test(each)) } return userPers.includes(requirePers) // 路由权限设置为字符串 }}复制代码

在checkPermission函数中实现了字符串、数组和正则类型的校验,因此,在我们路由配置文件中的auth可以支持字符串、数组和正则三种方式去设置。

如上,结合redux的应用,我们轻松实现可配置、高可复用的路由鉴权功能。系统会根据当前用户的权限列表(一般通过接口由后端返回)与配置文件中定义的权限要求进行校验,如果无权限,则重定向至Permission Error页面。

dispatch作用 react_「React系列」手把手带你撸后台系统(Redux与路由鉴权)相关推荐

  1. 「springcloud 2021 系列」Spring Cloud Gateway + OAuth2 + JWT 实现统一认证与鉴权

    通过认证服务进行统一认证,然后通过网关来统一校验认证和鉴权. 将采用 Nacos 作为注册中心,Gateway 作为网关,使用 nimbus-jose-jwt JWT 库操作 JWT 令牌 理论介绍 ...

  2. 手把手带你撸一个校园APP(五):新闻中心模块

    这个项目是很早之前在学校做的,如今再回首.很多代码很是粗糙,逻辑也不尽完善.还望各位看官海涵. 前言 通过上一篇文章的功能设计,我们可以发现新闻通知公告等是APP的最主要功能点.主要是聚合展示学校官网 ...

  3. windows系统和linux系统可以使用相同的js代码吗_「React 手册 」在 Windows 下使用 React , 你需要注意这些问题...

    大家好,本篇内容,我要和大家聊聊使用 Windows 开发 React ,你需要注意的一些问题.首先说明下,我不是使用 windows 进行开发,因为其配置开发环境来说不是特别方便,我更喜欢 苹果ma ...

  4. java8 lambda maplist排序_「java8系列」流式编程Stream

    前言 「Java8系列」神秘的Lambda 「Java8系列」神奇的函数式接口 继上两篇之后,本文已经java8系列的第三篇了.本篇文章比较长,但我希望大家都能认真读完.读不完可以先收藏,在找时间读. ...

  5. 「Hudi系列」Hudi查询写入常见问题汇总

    点击上方蓝色字体,选择"设为星标" 回复"面试"获取更多惊喜 八股文教给我,你们专心刷题和面试 阅读本文前必读: 1. 「Apache Hudi系列」核心概念与 ...

  6. react路由鉴权 / 路由守卫(常用经实践可行 推荐阅读)

    react路由鉴权 / 路由守卫 首先路由鉴权就是在访问一个页面时判断是否需要权限,比如电商App结算账单的时候需要登录用户的账号 路由守卫就是在该页面需要登录用户的情况下去判断是否登录 总的来说就是 ...

  7. 手把手带你撸一个校园APP(六):失物招领二手交易模块

    代码经过简单的整理,已经放到Github上了.https://github.com/zhengweichao/Hevttc 回首来看,代码质量一般,里面也有各种逻辑问题,还望各位看官海涵.接下来有时间 ...

  8. 如何制作linux系统硬盘,手把手带你自制Linux系统之二 简易Linux的制作

    手把手带你自制Linux系统之二 简易Linux的制作 本文利用CentOS5.5自带内核制作一个可以正常启动的Mini Linux. 打开上一篇准备工作中创建的CentOS虚拟机,为另一台虚拟机Mi ...

  9. react路由鉴权 / 路由守卫

    react路由鉴权 / 路由守卫 首先路由鉴权就是在访问一个页面时判断是否需要权限,比如电商App结算账单的时候需要登录用户的账号 路由守卫就是在该页面需要登录用户的情况下去判断是否登录 总的来说就是 ...

最新文章

  1. jQuery绑定事件的三种常见方式(bind、one、【change、click、keydown、hover】)
  2. 初探 开源视频会议 openmeeting
  3. AWS — AWS 上的 5G 网络
  4. 4G EPS 中的 User Plane
  5. 晒晒公司整改后的拓扑图和设备
  6. 武大上交发布首篇「图像匹配」大领域综述!涵盖 8 个子领域,汇总近 20年经典方法
  7. Kebernetes 学习总结(9)认证-授权-RBAC
  8. 计算差分方程的收敛点_数值计算(五十九)热传导方程组的差分数值求解
  9. C语言:计算1*2*3*....*100,即求100!。
  10. Quick_Cocos2d_x V3.3 Protobuf Android
  11. 二次量子化与量子计算化学
  12. nyoj592 蛇形填数
  13. 1月16日服务器例行维护更新公告,1月16日9—11点例行更新维护公告
  14. Visual Studio 2017安装使用方法
  15. MySQL第十四次作业
  16. ICT界的基建狂魔—上海季冠:借助闪星云平台一天改造150家品牌珠宝店
  17. C02-程序设计基础提高班(C++)第10周上机任务-类和对象之二
  18. 2023浙大MEM提前批面试班6月18日盛大开课:未雨绸缪,大事可成
  19. python读写rgb图像
  20. 格雷码(从零基础讲解,C++版)

热门文章

  1. Linux系统 proc self,linux – / proc / self / maps中的“— p”权限是什么意思?
  2. java 同一个package import_【编程基础】Java 中的Package和Import
  3. ppt讲解html,HTML讲解解读.ppt
  4. 华为鸿蒙搭载芯片,独立188天,荣耀50系列破冰!6nm芯片,不搭载鸿蒙
  5. php xmlhttp.responsetext,XMLHttpRequest中responseText怎么获取指定div的内容,而不是整个HTML的内容...
  6. android adm查看进程,基于android studio 的ADM对卡顿,耗时方法的检测
  7. 3399 mysql_MySQL索引
  8. jlink烧录软件_使用 MCU BootUtility 工具来烧录I.MXRT
  9. linux删除最后一个字符串,Bash删除字符串中的第一个和最后一个字符
  10. 2020 6-7月 每日花语