“ 现如今,在web开发中前后端分离已成为标配,而其中鉴权成为服务必不可缺少的一部分,使用token作为媒介也成为成熟的方案。作为一个后端程序员,熟悉返回给前端的参数的使用方法,也不失为一种提高接口设计能力的方式。”
01
—
数据格式
鉴权通过后,后端返回的token数据结构如下(刷新token返回的也一样):
{"token":"478f71ce-0c34-44a0-a002-d33a4d7a5451", // accessToken"expire":86400, // token过期时间"refreshToken":"9e6b24ff-34d0-4fd8-847f-4d4842a4b77c" // refreshToken}
02
—
流程图
前端使用token以及refreshToken与后端服务的流程图如下:

02
—
VUE 代码实现
具体的代码实现如下:
service/index.js
import axios from 'axios'import router from '../router'import store from '../vuex/store'import * as types from '../vuex/types'import {Loading} from 'element-ui'// 默认超时时间axios.defaults.timeout = 50000// 相对路径设置axios.defaults.baseURL = ''let loading// 开始加载function startLoading() {loading = Loading.service({lock: true,text: '加载中...',background: 'rgba(0, 0, 0, 0.2)'})}// 结束加载function endLoading() {loading.close()}let needLoadingRequestCount = 0export function showFullScreenLoading() {if (needLoadingRequestCount === 0) {startLoading()}needLoadingRequestCount++}export function tryHideFullScreenLoading() {if (needLoadingRequestCount <= 0) returnneedLoadingRequestCount--if (needLoadingRequestCount === 0) {endLoading()}}// 是否有请求在刷新tokenwindow.isRefreshing = false// 被挂起的请求数组let subscribesArr = []// push所有请求到数组中function subscribeTokenRefresh(cb) {subscribesArr.push(cb)}// 用新token发起请求function reloadSubscribesArr(token) {subscribesArr.map(cb => cb(token))}/*** 快过期了 和 已经过期了* @returns {boolean}*/function isTokenExpired() {let expiredTime = store.state.expirationTimeif (expiredTime) {let nowTime = new Date().getTime()let willExpired = (expiredTime - nowTime) / 1000 < 10 * 60return willExpired}return false}function directLogin() {router.push({path: '/home',query: {redirectUrl: window.location.href.split('#')[1] || ''}})}// http request 拦截器axios.interceptors.request.use(config => {// 设置参数if (!config.headers['Content-Type']) {config.headers = {'Content-Type': 'application/json'}}// 如果是不需要token的接口直接返回即可if (config.url.startsWith('/blog/open') || config.url === '/blog/login') {return config}// 获取tokenconst token = store.state.accessToken// 如果token存在,首先校验是否已经过期,如果已经过期,跳转到登录页面if (token) {let refreshToken = store.state.refreshTokenif (isTokenExpired() && config.url.indexOf('/admin/') > -1) {if (!window.isRefreshing) {window.isRefreshing = truepost('/blog/refresh/token', {refreshToken: refreshToken}).then((res) => {window.isRefreshing = falseif (res.code === 200) {const tokenVo = res.datastore.dispatch('setTokenInfo', tokenVo)reloadSubscribesArr(tokenVo.token)} else {subscribesArr = []window.localStorage.clear()directLogin()}}).catch((err) => {window.isRefreshing = falsesubscribesArr = []window.localStorage.clear()directLogin()})}let retry = new Promise((resolve, reject) => {subscribeTokenRefresh((newToken) => {config.headers.Authorization = `Bearer ${newToken}`resolve(config)})})return retry} else {config.headers.Authorization = `Bearer ${token}`return config}} else {directLogin()}// // 鉴权参数// if (config.method === 'get') {// // get请求下 参数在params中,其他请求在dada中// config.params = config.params || {}// // let json = JSON.parse(JSON.stringify(config.params))// // 一些参数处理// } else {// config.data = config.data || {}// // 一些参数处理// }showFullScreenLoading()return config},err => {tryHideFullScreenLoading()return Promise.reject(err)})// http response 拦截器axios.interceptors.response.use(response => {// 统一code处理if (response.status === 401) {window.localStorage.clear()router.push({name: '/home',query: {redirectUrl: window.location.href.split('#')[1] || '',is_new_user_url: 1}})} else if (response.status === 500) {this.$message.error(response.error)}tryHideFullScreenLoading()return response},error => {if (error.response) {switch (error.response.status) {case 401:store.commit(types.SET_LOGIN_STATUS, false)store.commit(types.REMOVE_TOKEN_INFO)router.replace({path: '/home',query: {redirect: router.currentRoute.fullPath}})}}tryHideFullScreenLoading()return Promise.reject(error.response.data)})export function put(url, data = {}) {return new Promise((resolve, reject) => {axios.put(url, data).then(response => {if (response.status === 200) {resolve(response.data)} else {this.$message.error(response.statusText)}}, err => {reject(err)})})}export function post(url, data = {}) {return new Promise((resolve, reject) => {axios.post(url, data).then(response => {if (response.status === 200) {resolve(response.data)} else {this.$message.error(response.statusText)}}, err => {reject(err)})})}export function get(url, data = {}) {return new Promise((resolve, reject) => {axios.get(url, data).then(response => {if (response.status === 200) {resolve(response.data)} else {this.$message.error(response.statusText)}}, err => {reject(err)})})}export function del(url, data = {}) {return new Promise((resolve, reject) => {axios.delete(url, data).then(response => {if (response.status === 200) {resolve(response.data)} else {this.$message.error(response.statusText)}}, err => {reject(err)})})}export function form(url, formData = {}) {let config = {headers: {'Content-Type': 'multipart/form-data'}}return new Promise((resolve, reject) => {axios.post(url, formData, config).then(response => {if (response.status === 200) {resolve(response.data)} else {this.$message.error(response.statusText)}}, err => {reject(err)})})}
文章转载自产品与码农,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




