聊一聊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存储服务端返回的信息 ...
随机推荐
- Linux课程知识点总结(二)
Linux课程知识点总结(二) 七.Shell实用功能 7.1 命令行自动补全 在Linux系统中,有太多的命令和文件名称需要记忆,使用命令行补全功能[Tab]可以快速的写出文件名和命令名 7.2 命 ...
- ES6中class的使用+继承
一.Class 介绍+基本语法(1).介绍通过class关键字,可以定义类.基本上,ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的 ...
- WebService 适用场合
适用场合 1.跨防火墙通信 如果应用程序有成千上万的用户,而且分布在世界各地,那么客户端和服务器之间的通信将是一个棘手的问题.因为客户端和服务器之间通常会有防火墙或者代理服 务器.在这种情况下,使用D ...
- JUC包-原子类(AtomicInteger为例)
目录 JUC包-原子类 为什么需要JUC包中的原子类 原子类原理(AtomicInteger为例) volatile CAS CAS的缺点 ABA问题 什么是ABA问题 ABA问题的解决办法 JUC包 ...
- CSS系列 (05):浮动详解
浮动的框可以向左或向右移动,直到它的外边缘碰到包含框或另一个浮动框的边框为止.由于浮动框不在文档的普通流中,所以文档的普通流中的块框表现得就像浮动框不存在一样. -- W3C 文字环绕 float可以 ...
- Hive 中的四种排序详解,再也不会混淆用法了
Hive 中的四种排序 排序操作是一个比较常见的操作,尤其是在数据分析的时候,我们往往需要对数据进行排序,hive 中和排序相关的有四个关键字,今天我们就看一下,它们都是什么作用. 数据准备 下面我们 ...
- kafka 0.8+spark offset 提交至mysql
kafka版本:<kafka.version> 0.8.2.1</kafka.version> spark版本 <artifactId>spark-streamin ...
- 分享一个的c++写的,模仿awk的框架类CAwkDoc
这是我好多年前,模仿awk写的. awk大家都比较熟悉,使用awk处理文件,读取文件,分割字段这些工作awk自己帮你实现了. 程序员只要编写业务逻辑代码,并且awk还提供了很多常用的字符串操作函数,可 ...
- Spring Boot 2.x基础教程:使用Flyway管理数据库版本
之前已经介绍了很多在Spring Boot中使用MySQL的案例,包含了Spring Boot最原始的JdbcTemplate.Spring Data JPA以及我们国内最常用的MyBatis.同时, ...
- JAVA原生mvc实现用户信息的增删查改
笔者最近学完jsp和servlet,于是心血来潮的打算写个简单的用户案例 环境准备: 开发工具eclipse jdk-1.8.0_72 tomcat-9.0.5 前端部分: 1.自己手写了一套样式 2 ...