Vuex + JWT 实现授权验证登陆

admin 2024-02-11 35次阅读

image

零、今天要完成右下角粉色区块的部分

tokensoft官网

image

tokendata官网

一、如何实现权限验证的过程

大家一定还记得之前在 .net core api 系列文章中《框架之五 || 的使用 3.3 JWT权限验证【修改】》,咱们通过对 JWT 的讲解,实现了对接口的验证,大家可以去了解一下,当时因为是没有前端,所以咱们就直接用的 接口文档来手动设置的权限验证,当时群里有很多小伙伴对这个不是很明白,我也是简单说了下,通过手动在 中输入 ,变成每次 vue 的 axios 请求的 中添加 Token,这个 Token 就是咱们手动配置的那个,因为当时没有前后端搭配,所以只是比较笼统的说了下这个流程,今天呢,就重点来说下这个授权登陆验证,也为下边的管理后台铺路,这里配合 Vue 前端,再仔细讲讲是如何实现前后端同时验证的:

image

上图中说的也是很详细了,主要分为两个验证:

1、前端验证(蓝色部分),用户访问一个页面,首先判断是否需要验证登陆,比如管理后台,或者订单系统的下单页(首页和详情页自然是不需要用户登陆的,购物车和订单等必须登陆,当然有些游客也可以购买的除外),然后去验证是否存在 Token,存在就添加到 axios 的 中去请求后端 API,反之则去登陆页登陆;

2、后端验证(绿色部分),这个就是咱们之前在说 .net core api 的时候说到的 JWT 授权验证,根据当前前端 axios 请求中传来的 Token ,解析出是否被篡改,以及是否会相应的权限,这样就可以进一步返回数据了;

这个时候大家一定会有疑惑了,既然现在每一个接口都定义了权限,为什么要俩边都需要验证,只需要后端 api 一个验证不就行了,何必这么麻烦?我认为是这样的:

首先前端验证的主要目的是:通过手动配置,可以让用户去主动获取 Token ,不用每次都去获取,而且也减轻了后端请求的次数,总不能是先去发送请求,再判断当前页面是否需要登陆吧,嗯,总结来说,

前端是为了页面级登陆,后端是为了接口级验证,而且也是想把 vue 前端工程化的思想。

二、结合API设计登录页 —— 实现后端验证1、引入 样式框架

因为之后需要一个管理后台,所以考虑着增加一个框架,目前比较流行的就是 和 IView,今天咱们先说一下引用

首先,在项目中 执行 npm ,初始化以后,在 中查看是否存在 -ui 文件夹,如果没有,则执行

 npm i element-ui -S

然后就可以看到项目中已经成功安装 了

image

然后、在项目的入口配置文件 main.js 中,引用

import ElementUI from 'element-ui' import 'element-ui/lib/theme-chalk/index.css' Vue.use(ElementUI)

如果项目没有报错,到此则安装成功。

2、添加统一登陆页面

第一、在 src 的 views 文件夹内,添加 Login.vue 页面,并添加内容:

添加路由后token 权限管理·(中国)官方网站imToken钱包官网,测试页面是否可行

image

3、配合后台登陆请求

完善 .cs 页面,稍微调整了下接口,和之前的没有差别,并增加权限验证

image

 /// 
        /// 获取博客列表 /// 
        /// 
        /// 
        /// 
        /// 
 [HttpGet] public async Task Get(int id, int page = 1, string bcategory = "技术博文")
        { int intTotalCount = 6; int TotalCount = 1;
            List blogArticleList = new List(); if (redisCacheManager.Get("Redis.Blog") != null)
            {
                blogArticleList = redisCacheManager.Get>("Redis.Blog");
            } else {
                blogArticleList = await blogArticleServices.Query(a => a.bcategory == bcategory);
                redisCacheManager.Set("Redis.Blog", blogArticleList, TimeSpan.FromHours(2));
            }
            TotalCount = blogArticleList.Count() / intTotalCount;
            blogArticleList = blogArticleList.OrderByDescending(d => d.bID).Skip((page - 1) * intTotalCount).Take(intTotalCount).ToList(); foreach (var item in blogArticleList)
            { if (!string.IsNullOrEmpty(item.bcontent))
                {
                    int totalLength = 500; if (item.bcontent.Length > totalLength)
                    {
                        item.bcontent = item.bcontent.Substring(0, totalLength);
                    }
                }
            } var data = new { success = true, page = page, pageCount = TotalCount, data = blogArticleList }; return data;
        } // GET: api/Blog/5
        /// 
        /// 获取详情 /// 
        /// 
        /// 
        [HttpGet("{id}", Name = "Get")] public async Task Get(int id)
        { var model = await blogArticleServices.getBlogDetails(id); var data = new { success = true, data = model }; return data;
        }

调整 .cs 的获取 Token 方法:

  /// 
        /// 获取JWT的方法 /// 
        /// id
        /// 角色
        /// 
 [HttpGet]
        [Route("Token")] public JsonResult GetJWTStr(string name, string pass)
        { string jwtStr = string.Empty; bool suc = false; //这里就是用户登陆以后,通过数据库去调取数据,分配权限的操作 //这里直接写死了
            if (name == "admins" && pass == "admins")
            {
                TokenModelJWT tokenModel = new TokenModelJWT();
                tokenModel.Uid = 1;
                tokenModel.Role = "Admin";
                jwtStr = JwtHelper.IssueJWT(tokenModel);
                suc = true;
            } else {
                jwtStr = "login fail!!!";
            } var result = new {
                data = new { success = suc, token = jwtStr }
            }; return Json(result);
        }

4、修改 前端的 Login.vue 页面的登陆方法,获取到 Token ,并把其保存到 Vuex 中

注意:目前是用的 本地存储的方法,Vuex 暂时没有完成,因为在路由 中,取不到,有知道的小伙伴请留言

5、修改 vuex 仓库,把 token 存进store中

import Vue from "vue";import Vuex from "vuex";
Vue.use(Vuex); const store = new Vuex.Store({ // 初始化的数据
 state: {    formDatas: null,    token: ""//定义全局变量 token
 }, // 改变state里面的值得方法
 mutations: {
    getFormData(state, data) {
      state.formDatas = data;
    },
    saveToken(state, data) {//将 token 保存
      state.token = data;
    }
  }
}); // 输出模块export default store;

6、这个时候要修改下之前我们封装的 http.js 方法,因为当时我们过滤掉了失败的方法,这里要打开下,大家自行修改下

image

这个时候,我们再登陆的话,已经发生变化

image

这个时候大家可以看到,我们成功的登陆了(右上角有欢迎提示),然后 token 也成功的保存到 stroe/ 里(下边控制台输出),

因为我们在博客页增加了权限,虽然我们是用的 admin 账号,但是 中还没有添加Token,所以现在还是 401,那如何才能有效的增加请求 呢,请往下看,权限验证前端部分。

三、实现一:登陆拦截验证——路由拦截1、修改 .js 路由,实现按需登陆

在需要登陆的地方,增加登陆要求字段,

然后增加 钩子函数(这里有一个问题,只能获取到本地缓存数据,无法获取 Vuex ,正在研究中)

import Vue from "vue";import Router from "vue-router";import Home from "./views/Home.vue";import FormVuex from "./views/FormVuex.vue";import Content from "./views/content";import Login from "./views/Login";import store from "./store";
Vue.use(Router); const router = new Router({  mode: "history", base: process.env.BASE_URL,  routes: [
    {      path: "/",      name: "home",      component: Home,      meta: {        requireAuth: true // 添加该字段,表示进入这个路由是需要登录的
 }
    },
    {      path: "/Vuex",      name: "Vuex",      component: FormVuex
    },
    {      path: "/Content/:id",      name: "Content",      component: Content
    },
    {      path: "/Login",      name: "Login",      component: Login
    },
    {      path: "/about",      name: "about", // route level code-splitting // this generates a separate chunk (about.[hash].js) for this route // which is lazy-loaded when the route is visited.
      component: () => import(/* webpackChunkName: "about" */ "./views/Form.vue")
    }
  ]
});
router.beforeEach((to, from, next) => { if (to.meta.requireAuth) {  // 判断该路由是否需要登录权限
        if (window.localStorage.Token&&window.localStorage.Token.length>=128) {  // 通过vuex state获取当前的token是否存在
 next();
        } else {
            next({                path: '/login',                query: {redirect: to.fullPath} // 将跳转的路由path作为参数,登录成功后跳转到该路由
 })
        }
    } else {
        next();
    }
})export default router;