若依框架-token的获取(二)
实用框架-Ruoyi(token的获取)
token验证以及权限渲染需要结合前端vue进行分析查看,加上ruoyi前后端分离版本是基于vue-element-admin为前端基础进行改造,所以建议理解这块内容之前看一遍这套框架的详细说明文档。
Promise
前端代码中出现了大量以下代码
new Promise((resolve, reject) => { ... ... resolve() }).catch(error => { reject(error) })
所有有必要先解释下这块代码是什么意思,拿来做什么用: Promise是es6里的,属于一种构造函数,前端人员在编写代码的时候会遇到这种情况:
({某个异步操作,比如访问异步接口}
,function (ret,err){
对ret和err进行操作和判断
});
因为是异步,所以需要在方法内部写处理,如果处理简单少还好,一旦在回调函数中又不断增加异步操作就会形成一个回调地狱,类似:
//假设有以下三个用来处理回调的封装方法,都是异步 //succesFunc是成功回调,errFunc是失败回调 function asyncA(params,succesFunc,errFunc){ ...(执行异步方法) succesFunc(ret)//如果成功 errFunc(err)//如果失败 } function asyncB(params,succesFunc,errFunc){ ...(执行异步方法) succesFunc(ret)//如果成功 errFunc(err)//如果失败 } function asyncC(params,succesFunc,errFunc){ ...(执行异步方法) succesFunc(ret)//如果成功 errFunc(err)//如果失败 } //那么平常我们的写法大概是这样的 asyncA('参数',function(ret1){ asyncB(ret1,function(ret2){ asyncC(ret2,function(ret3){ 最终执行某段处理方法 },function(err3){ ... }) },function(err2){ ... }) },function(err1){ ... });
对这个回调地狱接受程度因人而异,但确实有很多人因为接受不了这种操作从而提出了解决方案,Promise就是其中一种:
//三个异步处理方法可以通过promise封装成这样 let asyncA = function(){ return new Promise((resolve,reject) => { //...(这里正常执行异步) resolve('这是成功1');//如果成功 reject('这是失败1');//如果失败 }); } let asyncB = function(){ return new Promise((resolve,reject) => { //...(这里正常执行异步) resolve('这是成功2');//如果成功 reject('这是失败2');//如果失败 }); } let asyncC = function(){ return new Promise((resolve,reject) => { //...(这里正常执行异步) resolve('这是成功3');//如果成功 reject('这是失败3');//如果失败 }); } //方法不怕多,可以无限then下去 asyncA().then(function(ret){ //你既可以return一个确定的“值”,也可以再次返回一个Promise实例 //当返回的是一个确切的值的时候,then会将这个确切的值传入一个默认的Promise实例 //then会将这个确切的值传入一个默认的Promise实例,并且这个Promise实例会立即置为fulfilled状态,以供接下来的then方法里使用 //这里传的是一个Promise实例 return asyncB(ret) }).then(function(ret){ return asyncC(ret) }).then(function(ret){ console.log('最终执行某段处理方法'); }).catch(function(err){ console.log(err);//只要有一个异常,可以抛出对应方法内的异常 })
main.js
main.js作为vue项目的入口文件,加载了各种公共组件(需要引用和初始化组件实例)。
路由router注册
router目录下的index.js配合permission.js是整套vue前端项目的权限判断核心 index.js里面的path配置都是一些不会与权限挂钩的路由,比如404、登录页面路径等 permission.js中的 router.beforeEach
是路由拦截,在访问某个链接之前会进行权限判断,但是如果你注掉这部分拦截代码,然后再直接去访问某些路径(假设该路径没有被注入到router中),那么也是不能访问的
路由拦截是对访问路径的第一道校验,控制着整个前端页面的访问权限,把用户当前登录状态、权限和能访问该项目的路径绑定在一起,把握得死死的。
登录的重定向
假设在A页面登录超时,然后重新登录,会自动跳转到上次退出登录的那个页面,非常的银杏,这个做法是这样子的: 首先在用来拦截请求地址的permission.js中有这样一行代码:
next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页
所以在退出登录的时候会看到浏览器中的网址会带有对应的值:
等重新输入完账号密码点击登录时,会重定向某一个地址:
this.$router.push({ path: this.redirect || "/" }).catch(()=>{});
这个 this.redirect
来自:
query
的内容从url中自动获取形成一个键值对存储
去登录时走的流程
在执行 handleLogin()
方法时,会调用state中的自定义方法 Login
来实现登录验证和token值的记录,具体流程如下:
然后在store/modules/user.js中,调用封装好的网络请求方法进行登录验证:
注意,上面这一步是在state中进行的,vue-element-admin中对各个层级有比较严格的规定,比如缓存层就做记录缓存和取缓存的操作,api层就做接口层的操作,顶多层级之间相互调用
上面的代码你可能看着还有点疑问,为什么 this.$store.
可以直接调用user.js中的Login()方法 首先main.js中已经声明并使用了store:
import store from './store' new Vue({ el: '#app', router, store, render: h => h(App) })
store文件夹中的index.js是默认调用的,这是在index.js中的代码:
OK,回到调用接口api这一步,上面说到了登录动作先是调用了store的Login方法,先走store而不是直接走api是为了对登录成功后返回的token直接记录到缓存中,不在api层额外操作缓存。
我们再来看一下api层,非常简单的api方法封装
这一层只需要会用就行,重点关注的是已经封装好的request.js,因为之后的不管什么接口方法都需要调用该request.js,之后如果对接口入参和出参有调整都需要去修改该文件,所以理解这个文件的内容很有必要:
其实request.js就干了一件事,配置了下vue的axios参数,对入参和出参进行判断、封装和处理,对于一些共通错误进行统一抛出 相信到这里,很多人注意到了一个方法: getToken()
,这个方法干了什么:
直接从浏览器cookie中取token值,简单粗暴
有取,那必有存,什么时候存的? 答案就是登录成功后的处理
登录成功时处理
回到一开始的登录页面login.vue以及对应store的modules模块,user.js,看下登录成功后做了什么事情: user.js:
login.vue
OK,登录成功并跳转了,是不是又是一个全新的url,所以又会走一遍permission.js的路由拦截:
这其中有非常关键的一步骤就会执行了:
拿到了token,就会去获取该用户权限信息,根据该权限信息生成动态路由,最后通过 addRoutes
方法注册路由 rewriteRoutes
有些人看代码会比较懵逼,甚至对走的流程完全理解不了,不慌,看这张图就够了:
整个前端的登录拦截和判断权限控制访问的流程大概就如上图所述一样
后台token如何验证
看token获取获取,只要看一下登录接口中的代码怎么走就行了,定位接口:/login
看下 login()
这个方法:
核心方法是最后一步的 return tokenService.createToken(loginUser)
,继续展开往下看:
这样返回的JWT令牌包含了该用户的所有信息,因为有据可查:Json Web Token