今天继续分享 Vue 系列,几种前端验证 token 的方法
由于我们会有很多请求,都需要验证 token 的有效性,那么把这部分逻辑抽象出来就是最好的选择了。我这里大概想到了以下两种验证的方法
- 后端验证 token 统一返回200,前端对需要验证的请求传入统一的验证函数(简单)
- 使用 Axios 的拦截功能加路由钩子 beforeEach (推荐)
方法1
对于后端的代码,直接使用“Vue + Flask 小知识(五)”里面的代码即可。
下面主要来说说 Vue 相关的前端代码
一,封装 Axios 请求
function buildServerApiRequest(params, url, type, callback, app) { setToken(); if ('get' == type) { params={params:params} } let apiUrl = buildApiUrl(url); console.log(apiUrl) let result = Axios[type](apiUrl, params); if (isFunction(callback)) {//没有回调则返回es6 promise result.then(r => { r = r.data; callback(r, app); }).catch(e => { //can do something }); } return result; }
封装的结果就是,如果需要验证 token 的请求,就把验证 token 的函数以 callback 的形式传入即可。
二,验证 token 函数
新建一个 checktoken.js 文件,编写代码如下:
function checkToken(data, app){ if(data.code == 200) { console.log("auth pass"); // can do something }else if(data.code == 401){ app.$message.error("登陆过期"); localStorage.removeItem('accessToken'); app.$router.push({path: '/login'}); }else{ console.log("unknown error"); app.$message.error("未知错误"); } } export default{ checkToken }
如果后端返回信息中的 code 为401时,则认为 token 验证是有问题的,则返回到登陆页面。注意,这里的 code 并不是 http status code 哦。
三,在需要验证 token 的请求中添加回调函数
getuser: (p,a) => getuser: (p,a) => buildApiRequest(p,'userlist','get',checkToken.checkToken,a),(p,'userlist','get',checkToken.checkToken,a), saveproject: (p,c) => buildApiRequest(p,'projectadd','post',c),
getUser 这个方法,是需要验证 token 的,saveproject 这个方法,则不需要验证 token。
方法2
该方法的大致流程为:
在路由钩子 beforeEach 中检查 token 是否存在,如果存在则继续该请求,否则重定向到登陆页面。当继续请求时,通过拦截器,在 request 拦截器中增加携带 token 的 headers,在 response 拦截器中添加对响应码的验证,如401为 token 验证失败,重定向到登陆路由。
为了区分哪些路由需要验证 token,需要给路由添加一个校验字段,如:requireAuth;对于后端 token 校验逻辑,则可以直接使用 flask_httpauth 库中的 HTTPTokenAuth 直接校验
一,重写后端代码
直接复用 HTTPTokenAuth 的相关校验规则,只需要重写它的 verify_token 即可
from flask_httpauth import HTTPTokenAuth myauth = HTTPTokenAuth(scheme='jwt') secret_key = 'hardtoguess' salt = 'hardtoguess' @myauth.verify_token def verify_token(token): s = Serializer( secret_key=secret_key, salt=salt ) try: data = s.loads(token) return True except: return False
之后,将 HTTPTokenAuth 的 login_required 装饰器装饰到需要校验 token 的函数上即可
class UserListView(Resource): @myauth.login_required def get(self): ...
二,Vue 代码编写
为路由添加字段
path: '/user_manage', name: 'UserManage', meta: { title: '用户列表', keepAlive: true, requireAuth: true # 表示需要 token 校验 },
编写 axios 拦截
//response 拦截 Axios.interceptors.response.use( response => { return response; }, error => { if (error.response){ switch (error.response.status){ case 401: router.replace({ path: '/login', query: {redirect: router.currentRoute.fullPath} }) } } return Promise.reject(error.response.data); } )
//request 拦截 axios.interceptors.request.use( config => { if (localStorage.getItem('accessToken')) { // 判断是否存在 token,如果存在的话,则每个 http header 都加上 token config.headers.Authorization = `jwt ${localStorage.getItem('accessToken')}`; } return config; }, err => { return Promise.reject(err); });
接下来编写 beforeEach
router.beforeEach((to, from, next) => { window.document.title = to.meta.title?to.meta.title+'-'+Config.siteName:Config.siteName; console.log("beforeEach", to.meta.requireAuth); if(to.meta.requireAuth) { if(localStorage.getItem('accessToken'){ next(); }else{ next({path: '/login'}) }) } else { next(); } });
这样,就完成了一个简单的 token 验证功能。