权限系统

191 阅读1分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

1、登录?

1. 前端携带用户名,请求密码,验证码发送请求

  • 后端验证密码与数据库中是否相同,相同的话登录成功

登陆成功前发送的cookie,token:

image.png

2. 登录成功,后端返回接口,接口内容是token值

image.png

3. 前端将token值存储到cookie中,跳转路由到首页

import Cookies from 'js-cookie'
this.$http.post('/login', this.dataForm).then(({ data: res }) => {
  if (res.code !== 0) {
    // 重新获取验证码
    this.getCaptcha()
    return this.$message.error(res.msg)
  }
  // 成功登录
  Cookies.set('token', res.data.token)
  this.$router.replace({ name: 'home' })
}).catch(() => {})

image.png

4. 为什么存储在cookie中?cookie的有效期?

  • 因为每次发送请求的时候,请求头中的cookie都会发送,这样token也每次都有发送。

  • axios默认没有携带cookie,设置全局默认携带cookie,axios.defaults.withCredentials = true。

  • 前端设置withCredentials的情况下,后端要设置Access-Control-Allow-Origin为你的源地址,例如http://localhost:8080,而且还要设置header(‘Access-Control-Allow-Credentials: true’);Access-Control-Allow-Origin不能设置为*,不然cookie不会出现在http的请求头里

const http = axios.create({
  // 允许携带cookie值
  withCredentials: true
})

登录成功后的cookie,token:

image.png

  • cookie的默认有效期时间是20分钟;前后端都可以设置有效期时间,在项目中是前端设置的有效期时间,1小时

前端设置cookie失效时间:

// 10s后失效自动跳转到登录页面
import Cookies from 'js-cookie'

let seconds = 10;
let expires = new Date(new Date().getTime() + seconds * 1000);
Cookies.set('token', res.data.token, {expires: expires})

5. token失效是前端判断还是后端判断?

后端判断,返回接口,code值为401

6. cookie失效后,跳转到登录页面,前端删除token值

  • 获取到接口返回的code值,code值是401,无权限,token过期
/**
 * 清除登录信息
 */
export function clearLoginInfo () {
  Cookies.remove('token')
}
/**
 * 响应拦截
 */
http.interceptors.response.use(response => {
  if (response.data.code === 401 || response.data.code === 10001) {
    // 清除登录信息
    clearLoginInfo()
    // 跳转到登录页面
    router.replace({ name: 'login' })
    return Promise.reject(response.data.msg)
  }
  return response
}, error => {
  console.error(error)
  return Promise.reject(error)
})

image.png

2、图片验证码?

图片验证码的4种格式

3、怎么实现A用户打开看到是A页面,B用户打开看到是B页面?

1. 用户登录后,前端请求菜单接口,后端返回菜单接口,存储到全局变量

const routes = [];
const router = new Router({
  mode: 'hash',
  routes
})

router.beforeEach((to, from, next) => {
  // 添加动态(菜单)路由
  // 已添加或者当前路由为页面路由, 可直接访问
  ......

  // 获取菜单列表, 添加并全局变量保存
  http.get('/sys/menu/nav').then(({ data: res }) => {
    // 接口返回数据,存储到全局变量window.SITE_CONFIG['menuList']中
    window.SITE_CONFIG['menuList'] = res.data
    // 动态添加路由
    fnAddDynamicMenuRoutes(window.SITE_CONFIG['menuList'])
    next({ ...to, replace: true })
  }).catch(() => {
    next({ name: 'login' })
  })
})

image.png

2. 前端处理,动态添加并全局变量保存

  • 获取接口返回的菜单列表,添加到全局变量
  • 动态添加路由
  • 点击侧边栏时,this.$router.push()跳转到相应页面
/**
 * 添加动态(菜单)路由
 * @param {*} menuList 菜单列表
 * @param {*} routes 递归创建的动态(菜单)路由
 */
function fnAddDynamicMenuRoutes (menuList = [], routes = []) {
  for (var i = 0; i < menuList.length; i++) {
    .....
    // 组装路由
    var route = {
      path: '',
      component: null,
      name: '',
      meta: {
        ...window.SITE_CONFIG['contentTabDefault'],
        menuId: menuList[i].id,
        title: menuList[i].name
      }
    }

    ......
    URL = URL.replace(/^\//, '').replace(/_/g, '-')
    route['path'] = route['name'] = URL.replace(/\//g, '-')
    route['component'] = () => import(`@/views/modules/${URL}`)
    routes.push(route)
  }
  ......
}

4、如何判断是否显示按钮?

1. 前端请求权限接口,后端返回接口,接口中有对应的按钮字段,存储到全局变量

getPermissions () {
  return this.$http.get('/sys/menu/permissions').then(({ data: res }) => {
    // 存储到全局变量window.SITE_CONFIG['permissions']
    window.SITE_CONFIG['permissions'] = res.data
  }).catch(() => {})
}

image.png

2. 前端判断是否有此字段,有就显示按钮,没有不显示。

  • 组件中使用:
<el-button v-if="$hasPermission('oss:download:add')" type="primary">支持下载</el-button>
<el-button v-if="$hasPermission('0')" type="primary">新增</el-button>
  • hasPermission判断有无此字段
/**
 * 权限
 * @param {*} key
 */
export function hasPermission (key) {
  return window.SITE_CONFIG['permissions'].indexOf(key) !== -1 || false
}

// 挂载全局
Vue.prototype.$hasPermission = hasPermission