• vuex刷新页面丢失登录的token信息的解决方案


    vuex刷新页面丢失登录的token信息的解决方案

    最近真的是和这个token给干上了,被这个token验证搞的非常无语了

    先记录几个问题:

    1. 后端设置了token的有效期,前端也设置了请求拦截和响应拦截,响应拦截中也对状态码为401的token返回错误状态码进行了token清除,但是localstorage中一直没有清除token,还是可以保持登录,让人抓狂;
    2. 每次刷新页面,就会丢失登录用户的信息,让人抓狂+1;
    3. 每次关闭页面,再重新进入页面,用户登录信息再次丢失,抓狂,,,;

    抓狂了一整天,还是解决了,貌似解决了,记录一下

    前后端生成使用token的流程

    后端怎么写token,前端怎么用token,我不想再说了,前面两篇博客都写了,我直接上代码

    后端生成token的代码:

    // 登录
    exports.login = (req, res) => {
      const userinfo = req.body;
      //   console.log(userinfo)
      const sql = "select * from users where username = ?";
      db.query(sql, userinfo.username, (err, results) => {
        if (err) return res.status(404).json(err);
        if (results.length !== 1) return res.status(400).json("用户不存在");
        const compareResult = bcrypt.compareSync(
          userinfo.password,
          results[0].password
        );
        if (!compareResult) return res.status(400).json("用户名或密码错误");
    
        const user = {
          id: results[0].id,
          username: results[0].username,
          email: results[0].email,
          identify: results[0].identify
        };
    
        const tokenStr = jwt.sign(user, config.jwtSecretKey, { expiresIn: 36000 });
        // if (jwt.verify(tokenStr, config.jwtSecretKey, (err, decode) => {
        //   if (err) return res.status(401).json(err)
        // }))
    
        res.json({
          status: 200,
          message: "登录成功",
          token: "Bearer " + tokenStr,
        });
      });
    };
    '
    运行

    前端登录的时候用把token写到localstorage中:

    submitForm(formName) {
                this.$refs[formName].validate(valid => {
                    if (valid) {
                        this.$axios.post('/user/login', this.loginUser)
                            .then(res => {
                                const { token } = res.data
                                // console.log(token)
                                localStorage.setItem('mytoken', token)
    
                                // 解析token
                                const decode = jwt_decode(token)
                                console.log(decode)
    
                                // 将解析后的token存入vuex中
                                this.$store.dispatch('setAuthenticated', !this.isEmpty(decode))
                                this.$store.dispatch("setUser", decode)
    
                                this.$router.push('/')
                            })
                            .catch(err => {
                                // console.log(err)
                                this.$message.error('登录失败,请检查用户名及密码是否正确!')
                            })
                    } else {
                        this.$message({
                            type: "error",
                            message: '错误提交申请'    
                        })
                        return false
                    }
                })
            },
    

    逻辑就是,后端生成了一个token,并设定了token的有效期,然后前端请求接口时,获取到这个token,并将这个token命名为mytoken存到本地,并将解码后的token存放到vuex的state.user中(this.$store.dispatch("setUser", decode))。空口无凭,我放一下我的store/index.js文件中的setUser

    const actions = {
      setAuthenticated: ({ commit }, isAuthenticated) => {
        commit(types.SET_AUTHENTICATED, isAuthenticated);
      },
      setUser: ({ commit }, user) => {
        commit(types.SET_USER, user);
      },
      clearCurrentState: ({ commit }) => {
        commit(types.SET_AUTHENTICATED, false);
        commit(types.SET_USER, null);
      },
    };
    '
    运行

    好了,token放在localstorage中了,至于要不要将token存放到vuex的state.user中,不影响token的过期删除使用

    正常的逻辑是对axios进行二次封装,在请求拦截和响应拦截中对token进行处理,正常的逻辑代码如下:

    import axios from "axios";
    import { Message, Loading } from 'element-ui';
    import router from "./router"
    
    // 请求拦截
    axios.interceptors.request.use(config => {
        if(localStorage.eleToken){
            // 设置统一的请求头
            config.headers.Authorization = localStorage.eleToken
        }
        return config
    }, error => {
        return Promise.reject(error)
    })
    
    //响应拦截
    axios.interceptors.response.use(response => {
        return response
    }, error => {
        // 错误提醒
        endLoading();
        Message.error(error.response.data)
    
        // 获取错误状态码
        const {status} = error.response
        if(status == 401){
            Message.error("token失效,请重新登录")
            localStorage.removeItem('eleToken')
            router.push('/login')
        }
        return Promise.reject(error)
    })
    
    export default axios
    

    逻辑不解释了,就是请求头设置验证,响应时发现状态码为401就表示token过期了应该删除,可是让我抓狂的时,设置了10s的过期时间,页面还是一直保持着登录状态,,面向百度和CSDN编程了大半天没发现错在哪里,于是放弃了用这个方案。

    解决方法:

    其实已经在我上一篇博客中提到了,我今天又完善了一下,记录一下代码:

    // 路由守卫
    import jwt_decode from 'jwt-decode'
    router.beforeEach((to, from, next) => {
      
      // const EXPIRESTIME = 36000  
      const isLogin = localStorage.mytoken ? true : false;
      if (isLogin){
        const decode = jwt_decode(localStorage.mytoken)
       
        const date = parseInt(new Date().getTime() / 1000)
       
        if ((date - decode.iat) > (decode.exp - decode.iat)){
          localStorage.removeItem('mytoken')
          next("/login");
        }
      }
      
    
      if (to.path == "/login" || to.path == "/register") {
        next();
      } else {
        isLogin ? next() : next("/login");
      }
    });
    

    上面这段代码写在我的路由处理文件router/index.js文件中

    可以看出,我并没有另外再设置过期时间,而是根据后端返回的token的创建时间(decode.iat)和到期时间(decode.exp)的差与当前时间(date)和创建时间之差进行比较,这样更科学

    好了,第一个问题解决了,进入下一个问题。

    vuex刷新页面丢失登录信息

    这个问题非常好解释,因为我的用户信息存储在store中(也就是vuex中),每次刷新页面的时候,store的数据就会被释放,所以就丢失了用户信息,怎么处理呢,每次重载页面的时候,将用户信息写在页面的sessionstorage中,代码如下:

    created() {
        // 在页面加载时读取sessionStorage里的状态信息
       if (sessionStorage.getItem('store')) {
           this.$store.replaceState(
             Object.assign(
               {},
               this.$store.state,
               JSON.parse(sessionStorage.getItem('store'))
             )
          )
         }
         // 在页面刷新时将vuex里的信息保存到sessionStorage里,beforeunload事件在页面刷新时先触发    
         window.addEventListener('beforeunload', () => {
           sessionStorage.setItem('store', JSON.stringify(this.$store.state))      
         })
        // console.log(this.$store.state)
    }
    

    代码写在主组件App.vue中,逻辑也很好解释,就是每次刷新页面之前,将vuex里的信息保存到sessionStorage里,具体方法是设置一个名为store的item存进去,然后每次重新加载页面的时候,只要发现页面的sessionStorage中存了’store’,就将已经释放掉了的store数据(注意,这个不是刚才命名的store)中写入这个sessionStorage中存的’store’,这样,每次刷新页面,都会重新把数据写到store中,算是解决问题了

    但是,有个问题是,只要一关闭页面,sessionstorage中的数据就没有了,,,

    也就是说,关闭页面后,再打开页面,localstorage中有token,可以避开设置好的路由守卫进入页面,但是却没有用户信息,页面上有需要显示用户信息的,或者需要用户权限的功能全部没有了,真是操蛋,,

    因此进入下一个问题

    vuex关闭页面丢失登录信息的解决方案

    我直接上代码吧

     created() {
        // 在页面加载时读取sessionStorage里的状态信息
        // if (sessionStorage.getItem('store')) {
        //   this.$store.replaceState(
        //     Object.assign(
        //       {},
        //       this.$store.state,
        //       JSON.parse(sessionStorage.getItem('store'))
        //     )
        //   )
        // }
        // // 在页面刷新时将vuex里的信息保存到sessionStorage里,beforeunload事件在页面刷新时先触发    
        // window.addEventListener('beforeunload', () => {
        //   sessionStorage.setItem('store', JSON.stringify(this.$store.state))      
        // })
        // console.log(this.$store.state)
        
        
    
        if (localStorage.getItem('mytoken')) {
          const decode = jwt_decode(localStorage.mytoken)
          // console.log(decode)
          this.$store.dispatch("setUser", decode)
          // this.$store.replaceState(Object.assign({}, this.$store.state, JSON.parse(decode)))
        }
        // window.addEventListener('beforeunload', () => {
        //   localStorage.setItem('mytoken', localStorage.mytoken)
        // })
    
    
      },
    

    依然写在App.vue中,看到没,我把sessionstorage相关的代码全部注释起来了

    然后实际上就是把localstorage中的mytoken解析出来,然后传递给store中的user,就这样,每次加载页面的时候,总能保证store中是有用户信息的,除非token失效。

    大功告成,就是不知道明天上班会报什么稀奇古怪的错来,,,

  • 相关阅读:
    MathJax公式编辑示例
    【创建型模式】建造者模式
    vue源码分析(二)——vue的入口发生了什么
    Java中mybatis的Mpper代理开发的详细使用步骤
    从一个webpack loader中学习
    【问题记录 与 解决】PIL.Image.open(str(roses[0])) 无法在 PyCharm 中打开查看 windows 文件夹中的图片
    【大数据毕设】基于Hadoop的音乐管理系统论文(三)
    Photoshop套索工具使用指南:解锁自由选区的艺术
    巨杉数据库登榜互联网周刊“2022中国软件150强”及“2022大数据服务TOP150”
    Babylonjs学习笔记(九)——第一人称控制器
  • 原文地址:https://blog.csdn.net/u012848304/article/details/126939061