前言

因为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. Vue 面试题总结

    1. Vue 框架的优点是什么? (1)轻量级框架:只关注视图层,大小只有几十Kb:     (2)简单易学:文档通顺清晰,语法简单:     (3)数据双向绑定,数据视图结构分离,仅需操作数据即可完 ...

  2. spring boot 部署到tomcat

    网上很多文章描述过,当我按步骤做时会抛各种错误,最后记录下我的做法(成功发布到本地 Tomcat9 ): 1.将项目的启动类Application.java继承SpringBootServletIni ...

  3. 这些JS技巧,看看你是否都会用?

    问题1:以下代码在浏览器控制台上会打印什么? var a = 10; function foo() { console.log(a); // ?? var a = 20; } foo(); 问题2:如 ...

  4. FFT原理及C++与MATLAB混合编程详细介绍

    一:FFT原理 1.1 DFT计算 在一个周期内的离散傅里叶级数(DFS)变换定义为离散傅里叶变换(DFT). \[\begin{cases} X(k) = \sum_{n=0}^{N-1}x(n)W ...

  5. Gradle最佳实践

    一.Gradle相比Maven的优势 配置简洁 Maven是用pom.xml管理,引入一个jar包至少5行代码,Gradle只需要一行. 构建速度快 Gradle支持daemon方式运行,启动速度快, ...

  6. JavaScript 获得当前日期+时间

    //直接从项目中copy出来的,亲测可用.function getTodayTime(){ var date = new Date(); var seperator1 = "-"; ...

  7. 利用github给国外文件下载加速

    前言 作为一名程序员,经常需要下载一些编程相关的环境,而国内的网络环境大家都知道,有的文件用浏览器是下载不动的,于是我有了利用github下载文件的想法. 我的demo项目地址:https://git ...

  8. 美业黑科技 ▏肌肤管家SkinRun V3S智能肌肤测试仪,实现“护肤”私人定制

    肌肤如同身体,也需要定时的"健康检查",但仅凭肉眼难以窥见深层的肌肤问题.而现在,肌肤管家SkinRun前沿黑科技护肤测试仪--SkinRun V3S便能帮助用户对症下药.肌肤管家 ...

  9. JavaScript入门-函数function(二)

    JavaScript入门-函数function(二) 递归函数 什么是递归函数? 递归简单理解就是,在函数体里,调用自己. //我们在求一个10的阶乘的时候,可能会这么做 //写一个循环 var to ...

  10. docker 报错: Cannot connect to the Docker daemon at unix:///var/run/docker.sock.

    最近在 Windows 子系统 WSL 上面安装了一个 ubuntu18.04, 安装完docker 跑 hello-world 的时候报错了 docker: Cannot connect to th ...