聊一聊Axios与登录机制
前言
因为HTTP是一个stateless的协议,服务器并不会保存任何关于状态数据。
所以需要登录功能让服务器在以后请求的过程中能够识别到你的身份,而不是每次发请求都要输入用户名和密码。
下面介绍一下,我比较常用的登录方案:请求头携带Token的方式。
具体步骤:
- 首次登录,将用户名密码传给后端,返回
token。 - 将
token存储在localStroage和Vuex中。 - 用
Axios将token写入请求头中。 - 前端每次请求接口都将携带
token信息。 - 后端判断
token是否过期,过期或者没有,则返回401。 - 前端根据401状态码,将页面重定向到登录页面中。
封装LocalStorage
// storage.js
const Token = 'TOKEN'
const TokenType = 'TOKEN_TYPE' export function getToken () {
return localStorage.getItem(TokenType) + ' ' + localStorage.getItem(Token)
} export function setToken (data) {
localStorage.setItem(Token, data.token)
localStorage.setItem(TokenType, data.tokenType)
} export function removeToken () {
localStorage.removeItem(Token)
localStorage.removeItem(TokenType)
}
将上述方法写到公共方法里面,统一赋值到Vue.prototype.$util中(详情请看我上篇文章vue开发中的"骚操作")
封装非常简单,封装三个方法getToken、setToken、removeToken。
- 登录时,调用setToken将token值存储到localStorage中
- 请求接口时,调用getToken将token值放在请求头中。
- token过期时,调用removeToken将localStorage中的token移除。
封装Axios
首先用axios创建一个实例,传入一些公共的配置。
// require.js
import axios from 'axios';
import qs from 'qs'; const server = axios.create({
withCredentials: true,
transformRequest: [function (data) {
return qs.stringify(data)
}]
})
这里不统一配置BaseURL,是因为登录接口与业务接口可能是不在同一个域名下的,而且很可能是跨域的,所以需要配置proxy。
我使用的是vue-cli3搭建脚手架,创建一个proxy文件,设置代理。
然后在vue.config.js中引入,并配置在devServer的proxy属性中。
// vue.proxy.config.js
export default {
'/login': { // 登录接口
target: 'http://XXX.XXX.XXX.XXX:8080',
changeOrigin: false,
ws: false,
pathRewrite: {
'^/login': '/login'
}
},
'/api': { // 业务接口
target: 'http://XXX.XXX.XXX.XXX:8899',
changeOrigin: false,
ws: false,
pathRewrite: {
'^/api': '/api'
}
}
} // vue.config.js
const proxy = require('./vue.proxy.config.js');
...
module.exports = {
deServer = {
proxy: proxy
}
}
然后配置请求拦截器,目的是为了将每个请求都写入一个Authorization的header。
// require.js
...
server.interceptors.request.use(request => {
request.headers['Authorization'] = getToken()
return request
}, error => {
return Promise.reject(error)
})
接着配置响应拦截器,目的是拦截401状态码。
如果出现401状态码,就调用removeToken删除localStorage中的token并刷新页面。
// require.js
...
server.interceptors.response.use(response => {
return response
}, error => {
if (error.response.status === 401) {
// 如果是在登录页报错的话直接显示报错信息,否则清除token
if (location.href.indexOf('login') > 0) {
Vue.prototype.$notify.error({
title: '错误',
message: error.response.data.message
})
return
}
removeToken()
location.reload()
}
return Promise.reject(error)
})
路由处理
访问登录之外的页面,都需要登录权限。比如首页,判断是否存在token,有就访问成功,没有则跳转到登录页面。
页面路由跳转过程中,会使用全局钩子router.beforeEach中拦截路由,检测到没有token就重定向至登录页面。
// router.js
import Vue from 'vue'
import Router from 'vue-router'
import routes from './routes.js' Vue.use(Router)
export default async function () {
const router = new Router({
routes
})
router.beforeEach((to, from, next) => {
if (localStorage.getItem('TOKEN') === null && to.path !== '/login') {
next({ path: '/login' })
}
next()
})
return router
}
大致上,登录功能就已经完成了。
权限
有登录必有权限,有些页面只有管理员才能访问,有些页面所有人都能访问。那怎么处理比较好呢?
路由级别的权限,我建议前端自己来配置,要不然,在开发阶段,每增加一个路由就要后端去配置,简直是噩梦。
具体实现:
- 在挂载router时,先将一些公用页面挂载,比如说登录页面。
- 当用户登录后,获取到用户角色的权限,再将其权限与路由表中每个页面所需的权限作一次比较,生成用户最终的路由表。
- 调用router.addRouter()方法将其路由表添加到vue-router中。
// router.js
export const normalRoutes = [
{
path: '/login',
name: 'login'
component: Login
},
{
path: '/',
name: '首页'
component: Index
}
] export const permissionRoutes = [
{
path: '/edit',
name: '编辑页面',
component: Edit,
role: {
role: ['admin, 'editor']
}
},
...
{
path: '*',
redirect: '/404'
}
]
注意:404页面一定要最后加载,如果放在normalRoutes中,后面的路由访问都将会被拦截到404。
接下来在router.beforeEach中
router.beforeEach((to, from, next) => {
if (localStorage.getItem('TOKEN') === null && to.path !== '/login') {
next({ path: '/login' })
} else {
// 判断是否有用户权限信息
if (store.state.loginState.roles.length === 0) {
// 请求用户权限信息
store.dispath('loginState/getUserInfo').then(res => {
// 生成可访问的路由表
store.dispath('loginState/generateRoutes', { res.data.roles }).then(() => {
// 动态添加路由表
router.addRoutes(store.state.loginState.asyncRoutes)
next({...to, replace: true})
}
}
})
} else {
next()
}
}
})
接下来瞧一瞧generateRoutes的逻辑
// store/modules/loginState.js
import {normalRoutes, permissionRoutes} from './routes.js';
function hasPermission(roles, route) {
if (route.meta && route.meta.role) {
return roles.some(role => route.meta.role.indexOf(role) >= 0)
} else {
return true
}
}
export const loginState = {
namespaced: true,
state: {
roles: [],
routes: normalRoutes,
asyncRoutes: []
},
mutations: {
SET_ROUTES: (state, routes) => {
state.asyncRoutes = routes;
state.routes = [...normalRoutes, ...routes]
}
},
actions: {
generateRoutes({commit}, data) {
return new Promise(resolve => {
const { roles } = data;
const asyncRoutes = permissionRoutes.filter(routes => {
if (roles.indexOf('admin') >= 0) return true;
if (hasPermission(roles, routes)) {
if (!!routes.children) {
routes.children = routes.children.filter(child => {
if (hasPermission(roles, child)) return child;
return false;
})
return routes;
} else {
return routes;
}
}
return false
});
commit('SET_ROUTES', asyncRoutes);
resolve();
})
}
}
}
完!
作者:zhangwinwin
链接:聊一聊Axios与登录机制
来源:github
聊一聊Axios与登录机制的更多相关文章
- 分析ECMall的注册与登录机制
ecmall的注册流程index.php?app=member&act=register. 首先app是member,act是register方法. index.php中.通过ecmall的s ...
- 单系统登录机制SSO
一.单系统登录机制 1.http无状态协议 web应用采用browser/server架构,http作为通信协议.http是无状态协议,浏览器的每一次请求,服务器会独立处理,不与之前或之后的请求产生关 ...
- SSH免密登录机制
SSH免密登录机制:(见下图) 1.A先使用ssh-keygen生成一对公钥和私钥:ssh-keygen 2.将A的公钥复制给B一份,并且将其追加到B的授权文件中:ssh-copy-id B 3.接 ...
- 聊一聊Android的消息机制
聊一聊Android的消息机制 侯 亮 1概述 在Android平台上,主要用到两种通信机制,即Binder机制和消息机制,前者用于跨进程通信,后者用于进程内部通信. 从技术实现上来说,消息机制还是比 ...
- axios token header response request http拦截器 axios实现登录、拦截、登出
axios token header response request http拦截器 axios实现登录.拦截.登出 一个项目学会前端实现登录拦截 https://github.com/superm ...
- 一次实战CTF-WEB(多重登录机制中的缺陷)
要求登录admin账号 1.登录界面 我们发现有找回密码这个易受攻击点 2.直奔找回密码 通过观察前两个阶段url(reset1.htm1 reset2.html),我们推测出了第三个阶段的url(r ...
- 剖析ECMALL的登录机制
在ecmall.php文件中实例化控制器类,每一个控制器类,必须继承(extends)upload\admin\app\backend.base.php文件.在继承中调用方法是谁先被继承谁的方法被先调 ...
- vue+vuex+axios实现登录、注册页权限拦截
1.修改config文件夹里的dev.env.js里的BASE_API,把地址改成请求后端的公共部分 1 BASE_API: '"http://192.168.xx.xx"', 2 ...
- Session和Cookie,Django的自动登录机制
什么是Cookie? Cookie是浏览器的本地存储机制,存储服务器返回的各种信息,下次发起请求时再发送给服务端,比如访问baidu 什么是Session? 刚才说道,Cookie存储服务端返回的信息 ...
随机推荐
- C#自定义TemplateImage使用模板底图,运行时根据用户或产品信息生成海报图(1)
由于经常需要基于固定的一个模板底图,生成微信小程序分享用的海报图,如果每次都调用绘图函数,手动编写每个placeholder的填充,重复而且容易出错,因此,封装一个TemplateImage,用于填充 ...
- freemarker读取session里面的值
项目背景:springMVC+freemarker模板开发web 时代和信后台管理界面 代码示例: 后台服务: HttpSession session = request.getSession(); ...
- Ubuntu+KVM显卡透传
好久没有更新微博了,最近有点忙,大家见谅啊!今天带来的是我前段时间做的东西,也就是在 Ubuntu下做KVM虚拟机显科透传.(最近人有点懒,其实有几次是有时间更新的,但是就是懒得动,唉!得保持清醒不能 ...
- linux下eclipse
最近想学习C++,所以就重新安装了linux系统,虽然这两者没有什么联系,但是我还是比较喜欢linux系统,所以在linux下安装了Eclipse,想起了当初学习Red Hat 9.0时的痛苦场景,哎 ...
- tabControl组件的吸顶效果
最开始,还没有使用better-scroll插件的时候,直接在class中设定了一定的position为sticky,设置一定的top达成了效果.但是,使用better-scroll组件后,这些属性就 ...
- 解决使用Navicat等工具进行连接登录mysql的1130错误,无法使用Ip远程连接的问题(mysql为8.0版本)
错误:ERROR 1130: Host '192.168.1.3' is not allowed to connect to thisMySQL serve 错误1130:主机192.168.1.3& ...
- three.js 中使用多线程以及性能测试
今天郭先生说一下WebWorker以及WebWorker在three.js中的应用.我们都知道Javascript是单线程的,比如执行js代码的同时UI渲染就会停止,对于多核CPU的点脑,这一点让人难 ...
- ElasticSearch教程——filter与query对比(转学习使用)
一.数据准备 PUT /company/employee/2 { "address": { "country": "china", &quo ...
- Vue自动化路由(基于Vue-Router)开篇
vue自动化路由 好久不见~ 若羽又开篇Vue的内容了. 年初的时候发布了第一版的ea-router自动化路由库,欢迎大家安装使用.[Github地址] [npm地址] 经历一年的使用.还是发现了不少 ...
- 使用Python实现的4种快速排序算法
快速排序算法,总体来说就是选一个基准值,把小于基准值的分一拨,把大于基准值的分到另一拨,然后递归. 有区别的是,分区算法有差异,最直接的是,选个基准值,定义两个列表(小值分区less和大值分区grea ...