鉴权之用户角色鉴权的实现基本原理,一文看懂

528 阅读6分钟

前言

      对于不同权限的用户可以查看不同的页面,这种现象是非常常见的,这就不得不提起角色鉴权,如果不清楚什么是鉴权的友友们可以移步至文章 《登录鉴权机制详解:从前端到后端的全面解析》先了解一下,那么废话不多说我们直接进入主题!

正文

准备工作

这里我们创建一个ts项目

npm init vite

image.png

语言选择ts,框架依旧是vue.

这里为大家介绍一个全新的第三方库nprogress,这个库,可以让我们在加载页面的时候,会有一个和加载进度条的效果,非常的好用,使用起来也非常的简单,大家可以自行尝试下。

然后我们在终端中下载第三库pinia,vue-router

npm i vue-router
npm i pinia

我们先来处理中央数据库pinia部分,我们再src路径下创建一个store文件夹,这里我们实现一下不同的用户可以登录哪些页面. 第一步我们依旧是引入我们需要的方法

import {defineStore} from 'pinia'
import {ref} from 'vue'

然后再是创建一个仓库,用于存放数据,只不过这个数据不是一个单纯的数据,它可以明确指出,该用户可以进入哪些页面

const defaultList = {
        admin: [
            '0',
            '1',
            '11',
            '12',
            '13',
            '2',
            '21',
            '22',
            '23',
            '24',
            '25',
            '26',
            '27',
            '28',
            '29',
            '291',
            '292',
            '3',
            '31',
            '32',
            '33',
            '34',
            '4',
            '41',
            '42',
            '5',
            '7',
            '6',
            '61',
            '62',
            '63',
            '64',
            '65',
            '66',
        ],
        user: ['0', '1', '11', '12', '13'],
    };

这里我们简单化一下,值区别于user以及admin两个用户,这里面的这些字符,就是代表着他们可以访问到哪些页面。

const username = localStorage.getItem('username')
    const key = ref<string[]>([])
    if(username){
        key.value = (username == 'admin')?defaultList.admin:defaultList.user
    }

这里我们获取到用户登录时存入到本地浏览器中的数据.我们创建一个响应式数组,且是string类型的数组,我们使用了ts的类型限制,所以我们的key中的数据必须是一个string类型的数组。我们对于我从本地浏览器获取到的字符进行判断,如果等于user那么,该用户可以访问的页面则是由defaultList.user决定,返回由defaultList.admin决定,然后通过三元运算符对key.value赋值。

函数handleSet根据不同的用户名,给key.value赋值,拿到不同的权限。

const handleSet = (val:string[])=>{
        key.value = val
    }

以后返回数据

return {
        defaultList,
        key:(username == 'admin')?defaultList.admin:defaultList.user,
        handleSet
    }

这样我们就大致写好了仓库,现在别忘了在配置文件中使用

import {createPinia} from 'pinia'

app.use(createPinia())

做完这些之后我们然后src目录下创建文件夹router来配置路由 引入

import {createRouter,createWebHistory,RouteRecordRaw} from 'vue-router';
import NProgress from 'nprogress';
import 'nprogress/nprogress.css'
import {usePermissStore} from '../store/permiss.ts'

然后写一些路由路径,这里我们给了一些路由数组中的meta一个叫permiss的key,那么这个key的值,表示,defaultList.admin或者defaultList.user这两个数组的元素谁包含这个permiss,就可以访问到这个页面,而noAuth:true值为true表示,不需要权限就可以访问,否则需要权限。

const routes :RouteRecordRaw[] = [
    {
        path:'/',
        redirect:'/dashborad'
    },
    {
        path:'/',
        name:'home',
        component:()=>import('../views/home.vue'),
        children:[
            {
                path:'/dashborad',
                name:'dashborad',
                component: ()=>import('../views/dashborad.vue'),
                meta:{
                    title:'首页',
                    noAuth:true
                }
            },
            {
                path:'/system-user',
                name:'system-user',
                component: ()=>import('../views/system-user.vue'),
                meta:{
                    title:'用户管理',
                    permiss:'11'
                }
            },
            {
                path:'/table',
                name:'basetable',
                component: ()=>import('../views/basetable.vue'),
                meta:{
                    title:'基础表格',
                    permiss:'31'
                }
            }
        ]

    },
    {
        path:'/login',
        component: ()=>import('../views/login.vue'),
        meta:{
            title:'登录',
            noAuth:true
        }
    },
    {
        path:'/403',
        component: ()=>import('../views/403.vue'),
        meta:{
            title:'403 Not Found',
            noAuth:true
        }
    },
    {
        path:'/:path(.*)',
        redirect:'/404'
    }
]
const router = createRouter({
    history:createWebHistory(),
    routes
})

这里我们要看一下这个写法,同样是使用了ts中的类型限制,所以在routes中的元素必须是路由数组

const routes :RouteRecordRaw[] = []

在前面我提到了nprogress,它的使用方法时在前置路由中开始,在后置路由中结束,这里会为大家演示。

router.beforeEach((to,from,next)=>{
    NProgress.start();
    const role = localStorage.getItem('username');
    const permissStore = usePermissStore()
    if(to.meta.title){
        document.title = to.meta.title as string
    }
    if(!role&&to.meta.noAuth !==true){
        next('/login')
    }
    else if(
        typeof to.meta.permiss =='string' 
        && !permissStore.key.includes(to.meta.permiss)){
            next('/403')
    }else{
        next()
    }
    next();
})

router.afterEach(()=>{
    NProgress.done();
})

const role = localStorage.getItem('username');:表示用户是否登录了,如果登录了就可以在本地浏览器中找到username的值。
const permissStore = usePermissStore();实例化一个仓库。

if(!role&&to.meta.noAuth !==true){
        next('/login')
    }

这段代码表示,你当前没有登录到页面,且该页面是需要登录权限的,然后给你跳转到登录页面. 否则就是,你登陆了,然后检查您访问的页面是否有permiss这个key,且这个key的值是不是string !permissStore.key.includes(to.meta.permiss)则表示,如果你的permiss正确的话,就会再判断,你访问的这个页面的permiss在你用户的权限中是否有,否则跳转。最后就是未登录且不需要登录的,要鉴权且有权限的。

nprogress在后置路由中结束

router.afterEach(()=>{
    NProgress.done();
})

现在我们来看看login.vue中的逻辑实现,html部分我们就不展示了

const lgStr = localStorage.getItem('login-param');
const defParam = lgStr? JSON.parse(lgStr) : null
const param = reactive<LoginInfo>({
    username: defParam? defParam.username : '',
    password: defParam? defParam.password : ''
})

这里我们获取一下本地浏览器中的userinfo的数据,如果存在defParam的值就是本地浏览器中存入的数据,这表示用户已经登录过了,通过v-model绑定在表单的输入框中,表示,我们第一次登录成功后,不清除本地浏览器中的数据,当我们之后登录时,就不需要输入数据了。前提是我们要选中'记住密码'

当我们提交表单后,逻辑设定又是什么呢?

const submitForm = () => {
    if (!login.value) return
    // 严谨
    login.value.validate(valid => {
        console.log(valid);
        if (valid) {
            ElMessage.success('登录成功')
            localStorage.setItem('username',param.username)
            const keys = permissStore.defaultList[
                param.username == 'admin'? 'admin' : 'user'
            ]
            permissStore.handleSet(keys)
            if (checked.value) {
                localStorage.setItem(
                    'login-param',
                    JSON.stringify(param)
                )
            } else {
                localStorage.removeItem('login-param')
            }
            router.push('/')
        } else {
            ElMessage.error('登录失败')
        }
    })
}

这里的validate 方法通常与表单验证相关联,特别是在使用了表单验证库的情况下。validate 方法用于验证表单中的输入字段是否符合预期的规则。 我们来看看这段代码

image.png

param.username == 'admin'? 'admin' : 'user'通过三元运算符,判断用户名是什么,如果是user就拿到permissStore.defaultList[value]中的值,也就是拿到了user的权限,并通过handleSet设置权限

那也就是意味着在路由中的

image.png

中的permissStore.key.includes(to.meta.permiss)的值,就是通过用户登录,调用函数handleSet设置的。

if (checked.value) {
        localStorage.setItem(
            'login-param',
             JSON.stringify(param))
  } else {
         localStorage.removeItem('login-param')
       }

这里实现的逻辑是当我们点击'记住密码'这个勾选框,就会把用户信息存入本利浏览器中,如果之前用户选择了'记住密码',但是现在不想选择'记住密码'了,点击勾选框,就会将该用户的信息从本地浏览器中移除。

现在我们来看看用户权限 user用户是不含31这个页面的权限的

image.png image.png

那么换成admin就可以正常访问了

image.png

本文到此,用户角色鉴权的实现基本原理就介绍完了,感谢大家阅读!!若有不足恳请指出,谢谢大家!