zoukankan      html  css  js  c++  java
  • 必备技能四、ajax及token

    https://segmentfault.com/a/1190000008470355?utm_source=tuicool&utm_medium=referral

    转 https://www.jianshu.com/p/576dbf44b2ae

     重要:转https://www.cnblogs.com/lingnweb/p/9887974.html

    vue axios封装以及登录token过期跳转问题  https://blog.csdn.net/H1069495874/article/details/80057107

    http://www.51testing.com/html/93/n-4458693-2.html

    1.什么是Token 机制?

    token的意思是“令牌”,是服务端生成的一串字符串,作为客户端进行请求的一个标识。 当用户第一次登录后,服务器生成一个Token并将此Token返回给客户端,在以后的请求过程中,需要将该Token作为参数或者放在header里面发送给服务器作为身份验证的一个步骤,无需再次带上用户名和密码,但是服务端每隔一段时间(比如30分钟)就会重新生成Token,这时候客户端再用本地缓存的旧Token去请求就无法通过身份认证,一般状态码都会是401。
     

    vue项目中token失效处理方法,在main.js配置如下

    //判断token失效跳转
    axios.interceptors.response.use(response => {
        if (response) {
            switch (response.data.code) {
    
                case 1001: //与后台约定登录失效的返回码,根据实际情况处理
                    sessionStorage.removeItem('uid');     //删除用户ID
                    sessionStorage.removeItem('key');     //删除用户登录验证的key值,即token值
                    router.replace({
                        path: '/Login',
                        query: {
                            redirect: router.currentRoute.fullPath
                        }
                    })
            }
        }
        return response;
    }, error => {
    
        return Promise.reject(error.response.data) //返回接口返回的错误信息
    })
    

    token失效后,需用户重新登录才可以继续访问管理后台 

    刷新token和token是否过期的操作都是由后端实现,前端只负责根据code的不同状态来做不同的操作:

    一、判断token是否过期、失效

    举例:一般响应状态码 code :0,表示请求成功。①响应状态码 code:10010表示token过期 ②响应状态码 code:10011 表示token无效。这些状态码都由你自己和后端的同学一起定义。code等于10010和10011这两种状态都会跳转到登录页,重新进行登录并获取最新的token。

    二、在一定时间内刷新token

    为什么需要刷新token?因为出于安全性的考虑,一般是一天或几个小时更新token,看项目需要。

    怎么实现?我和后端的同学是这么定义的,在发送任何一次请求时,如果需要更新token,响应体中后端的同学给我返回了token这个字段,token出现在了响应体中,说明这时候是需要刷新token的(其他非刷新token的请求时是没有token字段的),这时用localStorage保存最新token,自动覆盖掉原来旧的token,这样下次再调用新接口时用的就是最新的token了,这样用户也感知不到token更新的过程。

        

    三、具体实现

    /**
    * 全局变量 和 设置 、配置等。。。
    */
    
    import axios from 'axios' // 引入axios
    
    import Storage from '@/assets/js/util/storage.js' // storage工具类,简单的封装
    
    
    
    axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'
    
    
    
    /* 请求拦截器 */
    
    axios.interceptors.request.use(function (config) { // 每次请求时会从localStorage中获取token
    
    let token = Storage.localGet('token')
    
    if (token) {
    
    token = 'bearer' + ' ' + token.replace(/'|"/g, '') // 把token加入到默认请求参数中
    
    config.headers.common['Authorization'] = token
    
    }
    
    return config
    
    }, function (error) {
    
    return Promise.reject(error)
    
    })
    
    /* 响应拦截器 */
    
    axios.interceptors.response.use(function (response) { // ①10010 token过期(30天) ②10011 token无效
    
    if (response.data.code === 10010 || response.data.code === 10011) {
    
    Storage.localRemove('token') // 删除已经失效或过期的token(不删除也可以,因为登录后覆盖)
    
    router.replace({
    
    path: '/login' // 到登录页重新获取token
    
    })
    
    } else if (response.data.token) { // 判断token是否存在,如果存在说明需要更新token
    
    Storage.localSet('token', response.data.token) // 覆盖原来的token(默认一天刷新一次)
    
    }
    
    return response
    
    }, function (error) {
    
    return Promise.reject(error)
    
    })
    

    缓存工具类 Storage

    var Storage = {
    // ==================sessionsTorage设置缓存================
    // 设置缓存
    sessionSet: function (name, data) {
    sessionStorage.removeItem(name)
    sessionStorage.setItem(name, JSON.stringify(data))
    },
    // 获取缓存
    sessionGet: function (name) {
    return JSON.parse(sessionStorage.getItem(name))
    },
    // 清除缓存
    sessionRemove: function (name) {
    sessionStorage.removeItem(name)
    },
    // ==================localStorage设置缓存==================
    // 设置缓存
    localSet: function (name, data) {
    localStorage.removeItem(name)
    localStorage.setItem(name, JSON.stringify(data))
    },
    // 获取缓存
    localGet: function (name) {
    return JSON.parse(localStorage.getItem(name))
    },
    // 清除缓存
    localRemove: function (name) {
    localStorage.removeItem(name)
    }
    
    }
    
    export default Storage
    

      

    最后,在评论有同学提到并发请求时,因为请求的过程快慢不同,token有可能还是旧的token。所以对于并发请求的情况还是要结合promise来使用

    请求时token过期自动刷新token

    1.在开发过程中,我们都会接触到token,token的作用是什么呢?主要的作用就是为了安全,用户登陆时,服务器会随机生成一个有时效性的token,用户的每一次请求都需要携带上token,证明其请求的合法性,服务器会验证token,只有通过验证才会返回请求结果。

    2.当token失效时,现在的网站一般会做两种处理,一种是跳转到登陆页面让用户重新登陆获取新的token,另外一种就是当检测到请求失效时,网站自动去请求新的token,第二种方式在app保持登陆状态上面用得比较多。

    3.下面进入主题,我们请求用的是axios,不管用何种请求方式,刷新token的原理都是一样的。

    //封装了一个统一的请求函数,这个不是重点

    export default function request(url, options) {
        const token = localStorage.getItem('token');
        const defaultOptions = {
            headers: {
                Authorization: `Bearer ${token}`,
            },
            withCredentials: true,
            url: url,
            baseURL: BASE_URL,
        };
        const newOptions = { ...options, ...defaultOptions };
        return axios.request(newOptions)
            .then(checkStatus)
            .catch(error => console.log(error));
    }
    

      

    // 封装了一个检测返回结果的函数,与后台返回状态码code === 1002表示token失效

    let isRefreshing = true;
    function checkStatus(response) {
      if (response && response.code === 1002) {
        // 刷新token的函数,这需要添加一个开关,防止重复请求
        if(isRefreshing){
            refreshTokenRequst()
        }
        isRefreshing = false;
        // 这个Promise函数很关键
          const retryOriginalRequest = new Promise((resolve) => {
                    addSubscriber(()=> {
                        resolve(request(url, options))
                    })
                });
                return retryOriginalRequest;
      }else{
          return response;
      }
    }
    

      

    // 刷新token的请求函数

    function refreshTokenRequst(){
        let data;
        const refreshToken = localStorage.getItem('refreshToken');
        data:{
            authorization: 'YXBwYXBpczpaSWxhQUVJdsferTeweERmR1praHk=',
            refreshToken,
        }
        axios.request({
            baseURL: BASE_URL,
            url:'/app/renewal',
            method: 'POST',
            data,
        }).then((response)=>{
            localStorage.setItem('refreshToken',response.data.refreshToken);
            localStorage.setItem('token',response.data.token);
            onAccessTokenFetched();
            isRefreshing = true;
        });
    }
    

      

    // Promise函数集合

    let subscribers = [];
    function onAccessTokenFetched() {
        subscribers.forEach((callback)=>{
            callback();
        })
        subscribers = [];
    }
    
    function addSubscriber(callback) {
        subscribers.push(callback)
    }
    

      

    总结:其实token失效,自动刷新token,在页面只有一个请求的时候是比较好处理的,但是如果页面同时有多个请求,并且都会产生token失效,这就需要一些稍微复杂的处理,解决方式主要是用了Promise 函数来进行处理。每一个token失效的请求都会存到一个Promise函数集合里面,当刷新token的函数执行完毕后,才会批量执行这些Promise函数,返回请求结果。还有一点要注意一下,这儿设置一个刷新token的开关isRefreshing,这个是非常有必要的,防止重复请求。

  • 相关阅读:
    fetch函数的使用-常见问题
    React的理解-入门指导
    Navicat for mysql 11.1.20激活
    sqlserver使用job删除过期备份文件
    sqlserver删除重复的数据
    提高code效率
    Wampserver红色橙色解决思路----端口冲突是关键
    java mvc框架系列总结ssh,ssm,servlet
    正则表达式入门案例C#
    SpringBoot开发之《兼容http和https同时访问时443端口被占用》
  • 原文地址:https://www.cnblogs.com/qdwz/p/11433494.html
Copyright © 2011-2022 走看看