前言

因为HTTP是一个stateless的协议,服务器并不会保存任何关于状态数据。

所以需要登录功能让服务器在以后请求的过程中能够识别到你的身份,而不是每次发请求都要输入用户名和密码。

下面介绍一下,我比较常用的登录方案:请求头携带Token的方式。

具体步骤:

  • 首次登录,将用户名密码传给后端,返回token
  • token存储在localStroageVuex中。
  • Axiostoken写入请求头中。
  • 前端每次请求接口都将携带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开发中的"骚操作"

封装非常简单,封装三个方法getTokensetTokenremoveToken

  • 登录时,调用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中引入,并配置在devServerproxy属性中。

// 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
}
}

然后配置请求拦截器,目的是为了将每个请求都写入一个Authorizationheader

// 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与登录机制的更多相关文章

  1. 分析ECMall的注册与登录机制

    ecmall的注册流程index.php?app=member&act=register. 首先app是member,act是register方法. index.php中.通过ecmall的s ...

  2. 单系统登录机制SSO

    一.单系统登录机制 1.http无状态协议 web应用采用browser/server架构,http作为通信协议.http是无状态协议,浏览器的每一次请求,服务器会独立处理,不与之前或之后的请求产生关 ...

  3. SSH免密登录机制

     SSH免密登录机制:(见下图) 1.A先使用ssh-keygen生成一对公钥和私钥:ssh-keygen 2.将A的公钥复制给B一份,并且将其追加到B的授权文件中:ssh-copy-id B 3.接 ...

  4. 聊一聊Android的消息机制

    聊一聊Android的消息机制 侯 亮 1概述 在Android平台上,主要用到两种通信机制,即Binder机制和消息机制,前者用于跨进程通信,后者用于进程内部通信. 从技术实现上来说,消息机制还是比 ...

  5. axios token header response request http拦截器 axios实现登录、拦截、登出

    axios token header response request http拦截器 axios实现登录.拦截.登出 一个项目学会前端实现登录拦截 https://github.com/superm ...

  6. 一次实战CTF-WEB(多重登录机制中的缺陷)

    要求登录admin账号 1.登录界面 我们发现有找回密码这个易受攻击点 2.直奔找回密码 通过观察前两个阶段url(reset1.htm1 reset2.html),我们推测出了第三个阶段的url(r ...

  7. 剖析ECMALL的登录机制

    在ecmall.php文件中实例化控制器类,每一个控制器类,必须继承(extends)upload\admin\app\backend.base.php文件.在继承中调用方法是谁先被继承谁的方法被先调 ...

  8. vue+vuex+axios实现登录、注册页权限拦截

    1.修改config文件夹里的dev.env.js里的BASE_API,把地址改成请求后端的公共部分 1 BASE_API: '"http://192.168.xx.xx"', 2 ...

  9. Session和Cookie,Django的自动登录机制

    什么是Cookie? Cookie是浏览器的本地存储机制,存储服务器返回的各种信息,下次发起请求时再发送给服务端,比如访问baidu 什么是Session? 刚才说道,Cookie存储服务端返回的信息 ...

随机推荐

  1. 关于django的坑(一)

    关于django orm 的坑: 关于设置数据库表自动更新 django的orm关于更新数据库的方法有update和save两种方法.想要表中自动更新需要一下几个条件: 使用 DateTimeFiel ...

  2. Qt学习笔记-设计简易的截图工具软件

    现在利用Qt来实现一个截图软件. 首先,设计一个界面出来. 最上面有一个label用来显示图片. 然后使用QPixmap中的静态函数grabWindow来获取图片.这里需要一个winID.可以使用 Q ...

  3. MySQL中的排序

    在编写SQL 语句时常常会用到 order by 进行排序,那么排序过程是什么样的?为什么有些排序执行比较快,有些排序执行很慢?又该如何去优化? 索引排序 索引排序指的是在通过索引查询时就完成了排序, ...

  4. redis加锁的几种实现

    redis加锁的几种实现 2017/09/21 1. redis加锁分类 redis能用的的加锁命令分表是INCR.SETNX.SET 2. 第一种锁命令INCR 这种加锁的思路是, key 不存在, ...

  5. 如何组织一场JAVA技能大练兵

    近期,公司为了锻炼开发人员技能,举办了一场涵盖多个技术线的技能大练兵,我有幸受邀负责java技术方向的出题和评审工作.下面从以下几个方面回顾下整个过程: 题目设计 程序要求 测试方法 题目设计 题目设 ...

  6. 对接口报错404 发现url多了一些不可描述的代码%E2%80%8B

    接口url出现了空格,复制的url可能出现空格肉眼看不出来,手动输入一遍URL

  7. 初识 D3.js :打造专属可视化

    一.前言 随着现在自定义可视化的需求日益增长,Highcharts.echarts等高度封装的可视化框架已经无法满足用户各种强定制性的可视化需求了,这个时候D3的无限定制的能力就脱颖而出. 如果想要通 ...

  8. 记一次Goroutine与wg导致的问题

    前言 今天发现了一个问题是之前一直没有注意到的,这里记一下 正文 Send Closed Chan 问题概述 代码逻辑是启动时启动多个 channel, channel1 获取数据监听数据处理后发送给 ...

  9. .net core 和 WPF 开发升讯威在线客服与营销系统:使用 WebSocket 实现访客端通信

    本系列文章详细介绍使用 .net core 和 WPF 开发 升讯威在线客服与营销系统 的过程.本产品已经成熟稳定并投入商用. 在线演示环境:https://kf.shengxunwei.com 注意 ...

  10. idea中文注释出现乱码,我靠自己解决了

    如果你像我一样️,查遍google百度,半天下来还是找不到解决方案,说不定这篇博客能帮助你顺利解决呢 好了,那么开始说说我是怎么解决麻烦的. 首先,我想打开一份java文稿.光预览,它是没有任何问题的 ...