学习总结后台管理系统

后台页面的权限验证安全性是非常重要的,可以说是一个后台项目一开始就必须考虑和搭建的基础核心功能。

我们前端所要做的是:不同的权限对应着不同的路由,同时侧边栏也需要根据不同的权限,异步生成

技术栈主要有: vue,vue-router,vuex,axios,vue-cli 3.x(没有webpack.config.js配置文件,取而代之的是vue.config.js文件), fiddle.php,nodejs(express框架配合myspl搭建过一个简单的后台系统框架,cookie.session配合使用,验证登录状态,但是我们这个项目使用的是token来验证)

一. 登入界面

1.登录:当用户填写完账号和密码后向服务端验证是否正确,服务端返回一个token,拿到token之后(我会将这个token存储到cookie中,保证刷新页面后能记住用户登录),前端会根据token再去拉取一个user_info的接口来获取用户的详细信息(如用户权限,用户名等等信息)。

2.权限验证:通过token获取用户对应的role,动态根据用户的role算出其对应有权限的路由,通过router.addRoutes动态挂载这些路由。

这些都是通过VUEX全局管理控制的(补充说明: 刷新页面后vuex的内容也会丢失)

具体实施: 

        首先做一个静态登入页面,两个input的框, 一个登录账号,一个登录密码,在放置一个登录按钮,绑定click事件,点击登录之后向服务端提交账号和密码进行验证,在向服务端提交账号和密码之前我们前端还可以进行一次简单的校验,减轻服务器压力,优化前端代码

        click绑定登录按钮,当点击按钮,提交账号密码,登录成功之后在这里推荐是用第三方登录平台不重定向到首页, this.showDialog = true //弹出选择第三方平台的dialog,利用this.$store.dispatch提交username信息到vuex中的异步action,并将token储存在cookie之中,这样下次打开页面的时候能记住用户的登录状态,不用在登录页面重新登录了.

注意: 为了安全性,我司在后台所有token有效期都是seeion,就是浏览器关闭了就丢失了,重新打开浏览器都需要重新登录一次,确保用户不会因为电脑遗失或者其他原因被人随意使用账号

1.1. 获取用户信息

用户登录成功之后,我们在全局钩子`router.beforeEach`中拦截路由,判断是否已获得token,在获取token之后我们就要去获取用户的基本信息了

(同时要注意一点的是: 我们之后存储一个用户token,并没有存储别的用户信息{用户名,用户头像等})

(假设我把用户权限和用户名存在本地,如果我在这时候有另一台电脑登录并修改了自己的用户名,那再用之前的电脑登录,,那么他会默认去读取本地cookie中的名字,并不会去拉取新的用户信息)

所以现在的策略:

页面会从cookie中查看是否存在token,没有,就走一遍上部分的流程重新登录,如果有token,就会把这个token返给后端去拉取user_info,保证用户信息是最新的.(如果做了单点登录功能的话, 用户信息存储在本地也是可以得,当你一台电脑登录时,另一台会被提下线,所以总会重新登录获取最新的内容)

而且从代码层面我建议还是把 login和get_user_info两件事分开比较好,在这个后端全面微服务的年代,后端同学也想写优雅的代码~

二. 权限篇

在工作中,前端会有一个路由表,他表示了每一个路由可访问的权限.

1. 用户登录之后,通过token获取用户的role

2. 动态根据用户的role 算出其对应应有权限的路由

3. 再通过router.addRouetes 动态挂载路由(这些都只是路由级的,后端的权限是逃不掉的)

        现在,就是前端来控制页面级的权限,不同权限的用户显示不同的侧边栏和限制其所能进入的页面(还有少许的按钮级别的权限控制),后端会验证每一个涉及请求的操作,验证其是否有该操作的权限,每一个后台的请求不管是get还是post都会让前端在请求header里面携带用户token , 后端会根据改token来验证在token是否有权限执行该操作,如果没有权限就会抛出一个对应的状态码,前端测到状态码,做出相应的操作

三. 具体实现

1. 创建vue实例的时候将vue-router挂载 , 但这个时候vue-router挂载一些登录或者不用权限的公用的页面

2. 当用户登录后, 获取用role, 将role和路由表每个页面需要的权限作比较,  生成最终用户可访问的路由表

3. 调用router.addRoutes(store.getters.addRouters)添加用户可访问的路由

4. 使用vuex管理路由表,  根据vuex中可访问的路由渲染侧边栏组件

router.js中书写实现路由表:

1. 首先我们要实现如首页和登录页和一些不用权限的公用页面vue-router如登录页和首页

2. 之后实例化vue的时候只挂载上面不用权限的路由export default new Router({routers: 上面的路由})

3. 异步挂载路由:   动态需要根据权限加载路由表,在这里我们根据vue-router官方推荐的方法meta路线元字段(可以`meta`在定义路径时包含字段,写在你children里面)标签来标示页面访问的权限有哪些 `meta: {role: ['admin' , 'super_editor']}` 表示该页面只有admin个超级编辑才能有资格进入

注意事项:  这里有一个需要非常注意的地方就是404 页面一定要最后加载 , 如果放在constantRouterMap 一同声明了 404 , 后面的所有页面都会拦截404

main.js数据入口文档(关键的main.js)

在main.js中主要用的是router.beforeEach(to,from,next) => { if (store.getters.token) {if (to.path === '/login') {next ({path: '/'}))}}}也就是登录之后,判断是否有token,如果没有,那就在免登录白名单中查找,如果有就是直接进入,如果没有那么就跳转到登录页

        如果有,并且入口路径to是从/login登录页中进入的,那么就redirect重定向跳转到首页,

        否则先判断当前用户是否已拉取完user_info信息  if(store.getters.roles.length === 0) , 如果是,那么user_info 拉取info`store.dispatch('GetInfo方法名').then`在从这个异步操作中获取所有的值,const roles = res.data.role,生成可访问的路由表store.dispatch('GenerateRoutes', {roles}),在获取到可访问的路由表后,我们在动态添加可访问的路由表router.addRoutes(store.getters.addRouters)  ,在通过next({ ...to , replace: true})hack方法 确保addRouters 已完成 , set the replace: true

如果没有拉取到info信息就返回err .catch(err => {console.log(err)})

如果当有用户权限的时候,说明所有可访问路由已生成 , 如果没权限的页面会自动进入404页面

如果页面没有token时,如果在面登入的白名单中,就直接进入if(whiteList.indexOf(to.path) !== -1){next()} , 否则全部重定向到登入页面

下面是store/permission.js

这里就是干一件是,通过用户权限和之前在router.js里面asyncRouterMap的每一个页面所需要的权限做匹配 , 最后放回一个该用户能够访问路由有哪些

这是一个vuex状态管理模式,vuex的状态管理是响应式式的,当vue组件从store好读取状态的时候,若store中的状态发生改变 , 那么相应的组件也会发生改变

但是,你不能直接改变store中的状态.改变store中的状态唯一的途径就是,显示的提交(commit ) mutation . 在vue组件中获取vuex状态

封装hasPermission函数,判断进入页面是否需要权限,还有封装vuex中mobule模块

侧边栏

基于element-ui的NavMenu侧边栏来实现的

遍历之前算出来的permission_routers , 通过vuex拿到之后动态v-for渲染(这里因为一些业务需要要加很多判断,比如我们定义路由的时候会加很多参数)

hidden:true  是否显示,默认为flase   

redirect: noredirect如果重定向为conredirect那么重定向不会再面包屑中显示 

name: 'router-name'   名称由<keep-alive>(必须设置!!)使用

meta: {role  title   icon   noCache}  

role: ['admin' , 'editor']  将控制页面角色(您可以设置多个角色)

title: 'title'   子菜单和面包屑中显示的名称(推荐集)

icon: 'svg-name'  侧边栏中显示的图标

noCache: true  如果fasle,页面将不会被缓存(默认为false)

侧边栏高亮问题: element-ui官方給了default-active

:default-active="$route.path" 将default-active一直指向当前路由就可以了,就是这么简单  

按钮级别权限控制

现在是通过获取到用户的role之后,在前端用v-if手动判断来区分不同权限对应的按钮的。

理由前面也说了,我司颗粒度的权限判断是交给后端来做的,每个操作后端都会进行权限判断。而且我觉得其实前端真正需要按钮级别判断的地方不是很多,如果一个页面有很多种不同权限的按钮,我觉得更多的应该是考虑产品层面是否设计合理。

axios拦截器

首先我们通过request拦截器在每个请求头里面塞入token,好让后端对请求进行权限验证。并创建一个resques拦截器,当服务端返回特殊的状态码,我们统一做处理,如没权限或者token失效等操作。

两步验证

考虑到安全性,简简单单一个账号+密码的方式很难保证安装性,推荐借助腾讯的微信或qq作为第三方绑定

账号和密码验证成功之后还需要绑定一个第三方平台验证,只需要在原有登录的逻辑上改造一下就好,登录成功之后,不直接跳到首页而是让用户两步登录,选择登录平台,第三方平台登录一样要通过OAuth2.0授权

如微信还必须是你授权账号的一级域名。所以你授权的域名是vue-element-admin.com,你就必须重定向到vue-element-admin.com/xxx/下面,所以你需要写一个重定向的服务,如vue-element-admin.com/auth/redirect?a.com 跳到该页面时会再次重定向给a.com。

所以我们后台也需要开一个authredirect页面:代码。他的作用是第三方登录成功之后会默认跳到授权的页面,授权的页面会再次重定向回我们的后台,由于是spa,改变路由的体验不好,我们通过window.opener.location.href的方式改变hash,在login.js里面再监听hash的变化。当hash变化时,获取之前第三方登录成功返回的code与第一步账号密码登录之后返回的uid一同发送给服务端验证是否正确,如果正确,这时候就是真正的登录成功。

外卖后台管理系统

后台界面的首页:

是由element的  时间区间插件 , 省市区级联插件,  树形插件选择不同的角色;这几个条件, 来筛选对应条件的数据

        我们把后台返回的数据放到Echarts的折线图,饼状图里面,每次登录系统每个角色看到的这个统计数据是不一的,这取决于我们前端利用token拉取的user_info接口中所获取的信息,参数是不一样的 , 这样做到了有公司管理者对公司整体的运营情况的一个把握

        我们的有一些系统会给入驻的商家时候,他们可以添加商店 , 我们审核 ,给予相应的权限,我们前端在通过token获取roel,根据用户的roel动态算出其拥有权限的路由,之后通过router.addRouters动态挂载这些路由

        商户可以上传一些菜品 , 图片 , 价格等参数 , 我们后台人员会检测到每个商家的经营情况 , 动态的给他们推送一些活动 , 首页置顶之类的;

        这里我们使用了 element的Upload上传插件;  Transfer 穿梭框插件,  Form插件,  Table表格插件,  Pagination 分页插件

        在利用flex布局,布局: 头部固定,左边固定,右边自适应  display: flex;  

flex布局中分为主轴方向和交叉轴:
主轴方向: 我们在弹性容器上通过flex-direction修改主轴方向;如果主轴方向改变那么交叉轴也会变它有几个属性,分为row左->右 , column上->下 , row-reverse左<-右 , column-reverse上<-下

弹性布局永远沿着主轴排列,当主轴排列不下,我们可以通过flex-wrap进行换行 , nowrap,不换行 一行显示 , wrap换行下一行显示 , wrap-reverse 反向换行

复合属性: flex-flow = flex-drection + flex-wrap

复合属性flex = flex-grow + flex-shrink + flex-basis    

flex-grow: 放大比例      flex-shrink生效前的尺寸         flex-basis设置的是元素在主轴上的初始尺寸

主轴上元素的对齐方式: justify-content: flex-start默认左->右 , flex-end左<-右 , center居中 ,   space-between左右对齐中间自适应 , space-around平分剩余空间

交叉轴上的对齐方式: align-items

stretch: 默认值,会在交叉轴方向撑满        flex-start沿前端        flex-end沿交叉轴终点对齐        center沿交叉轴中点对齐        baseline沿第一行文字的基线对齐...

猜你喜欢

转载自blog.csdn.net/zyf971020/article/details/127508747
今日推荐