• vue存储和使用后端传递过来的token


    vue存储和使用后端node传递过来的token

    以前一直都是在postman中测试验证token,现在用在真正的项目中,记录一下在vue项目中token的存储和使用

    后端关于token的设置

    app.js中需要设置token的地方

    const expressJWT = require('express-jwt')
    const app = express();
    const config = require('./config')
    app.use(expressJWT({secret: config.jwtSecretKey}).unless({path: [/^\/api\/users/]}))
    
    const testRouter = require('./routes/testToken')
    app.use('/my', testRouter)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    config中就是一个秘钥,我单独写了一个文件,不再放上来了

    unless后面是不需要token的地址,也就是以/api/users开头的链接都不需要token验证

    用到的testToken.js如下:

    const express = require('express');
    const router = express.Router();
    
    const test = require('../router_handler/testToken')
    
    router.get('/current', test.test)
    
    module.exports = router
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    使用模块化的写法,真正的路由处理函数写在/router_handler/testToken.js中,内容如下:

    const db = require('../db/index');
    
    exports.test = (req, res) => {
        const sql = 'select * from users where id = ?'
        db.query(sql, req.user.id, (err, results) => {
            if(err) return res.send({status: 404, message: err})
            if(results.length !== 1) return res.send({status: 404, messsage: '获取用户信息失败,需重新登录'})
            res.json(results[0])
        })
        // res.send(req)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    实际上就是一个获取用户信息的处理函数,在mysql中根据id查询用户信息

    注意,重点来了,经过上述一系列设置后,当我们访问非/api/users开头的链接的时候,都需要验证,也就是在请求参数中提供token,而这个token则是在登录的时候给出的,我写在用户登录的处理函数中user.js,如下

    // 用户登录的处理函数
    exports.login = (req, res) => {
        const userinfo = req.body
        const sql = 'select * from users where name = ?'
        db.query(sql, userinfo.name, (err, results) => {
            if(err) return res.send({status: 404, message: err.message})
            if(results.length !== 1) return res.status(404).json('用户不存在!')
            const compareResults = bcrypt.compareSync(userinfo.password, results[0].password)
            if(!compareResults) return res.status(404).json('密码错误!')
            const user = {
                id: results[0].id,
                name: results[0].name,
                avatar: results[0].avatar,
                identify: results[0].identify
            }
            const tokenStr = jwt.sign(user, config.jwtSecretKey, {expiresIn: '10h'})
            res.json({
                status:200, 
                message: '登录成功', 
                // result: results[0], 
                token: 'Bearer ' + tokenStr
            })
        })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    不相关的代码我就不写了,注意看到tokenStr这个变量,就是存储的token值,通过res响应出来。

    总结一下,后端存储token的流程:

    1. 编写登录函数,向页面响应token字符串;
    2. 入口文件中(app.js)设置需要提供token的路由,并提供解析token字符串的秘钥;
    3. 编写对应的路由函数,处理请求

    至此,后端的关键要点已经写完了,进入前端token的存储

    前端页面取用token

    前端页面在登录的时候,应该把后端响应过来的token存储起来,这样就可以实现路由守护和免登陆的功能

    token存储

    通过ajax请求路由地址,并访问res.data中的token字符串,并存储在浏览器中,步骤和写法是固定的

    我写在Login.vue中,如下:

    methods: {
        submitForm(formName) {
          this.$refs[formName].validate(valid => {
            if (valid) {
              this.$axios
                .post("/users/login", this.loginUser)
                .then(res => {
                    // console.log(res)
                    //token
                    const { token } = res.data
                    localStorage.setItem("eleToken", token)
                 
                    this.$router.push("/index");
                });
            } else {
              console.log("error submit!!");
              return false;
            }
          });
        }
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    const { token } = res.data通过解构赋值的方式取出响应数据中的token

    localStorage.setItem("eleToken", token)将token存储在浏览器本地

    在这里插入图片描述

    前端使用token实现路由守卫

    其实在后端代码中写的unless就是路由守卫的意思,不过前端应该也要有这个,不然不用登录就可以访问到网站的内容了,这显然不合理

    看看前端怎么写的,写在router.js中

    import Vue from 'vue'
    import VueRouter from 'vue-router'
    import Index from './views/Index.vue'
    import Register from './views/Register.vue'
    import NotFound from './views/404.vue'
    import Login from './views/Login.vue'
    
    Vue.use(VueRouter)
    
    const router = new VueRouter({
        mode: 'history',
        base: process.env.BASE_URL,
        routes: [
            { path: '*', name: '/404', component: NotFound },
            { path: '/', redirect: '/index' },
            { path: '/register', name: 'register', component: Register },
            { path: '/index', name: 'index', component: Index },
            { path: '/login', name: 'login', component: Login },
        ]
    })
    
    // 路由守卫
    router.beforeEach((to, from, next) => {
        const isLogin = localStorage.eleToken ? true: false
        if(to.path == '/login' || to.path == '/register'){
            next()
        }else {
            isLogin ? next() : next('/login')
        }
    })
    
    export default router
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    看路由守卫后面的代码就可以了

    使用的是router中的beforeEach函数

    使用的逻辑如下:

    1. 定义一个isLogin(布尔值),如果浏览器中localStorage中存有eleToken对象,则值为true,否则为false
    2. 确定哪些路由不需要拦截:to.path == '/login' || to.path == '/register'也就是登录和注册不需要拦截,直接放行,用next()
    3. 对于拦截的路由,判断isLogin是否为true,是就说明已经登录过了,有token,直接放行,如果为false,就跳转到登录的路由。

    请求拦截和响应拦截

    还有一点没有搞定,需要判断当前的token是不是过期了的,因为后端写token的时候是给了个10小时的有效期,过期后,应该重新登录才对

    这里我写在一个单独的http.js文件中,先上代码

    import axios from "axios";
    import { Message, Loading } from 'element-ui';
    import router from "./router"
    
    let loading;
    function startLoading(){
        loading = Loading.service({
            lock: true,
            text: '数据加载中',
            background: 'rgba(0,0,0,0.7)'
        })
    }
    
    function endLoading(){
        loading.close();
    }
    
    // 请求拦截
    axios.interceptors.request.use(config => {
        startLoading();
        if(localStorage.eleToken){
            // 设置统一的请求头
            config.headers.Authorization = localStorage.eleToken
        }
        return config
    }, error => {
        return Promise.reject(error)
    })
    
    //响应拦截
    axios.interceptors.response.use(response => {
        endLoading();
        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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    看后面的请求拦截和响应拦截就可以了

    分两步:

    1. 请求拦截,当请求数据的时候,判断localStorage中是否存储了eleToken,有的话,就把它写到请求头中去
    2. 响应拦截,当token过期后,页面会响应一个401的错误状态码,这里要做的事情就是要把这个过期的token清除,并跳转到登录页面

    注意,这里坑的一笔,坑了博主一个下午,我写的

    页面会响应一个401的错误状态码

    对吗,但是经过测试,token过期后根本就没有跳转到登录页面啊,后来我打印了一下这个status,发现这个值等于0啊,坑爹呢不是吗,所以响应拦截这里正确的写法应该是:

    //响应拦截
    axios.interceptors.response.use(response => {
        endLoading();
        return response
    }, error => {
        // 错误提醒
        endLoading();
        Message.error(error.response.data)
        console.log(error.response)
    
        // 获取错误状态码
        const {status} = error.response
        console.log(status)
        if(status === 0){
            Message.error("token失效,请重新登录")
            localStorage.removeItem('eleToken')
            // console.log(error)
            router.push('/login')
        }
        return Promise.reject(error)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    把状态码改过来后就正常跳转了。
    至此,前后端token的使用都已经顺畅了~~

  • 相关阅读:
    Golang vs Rust ——服务端编程应该选择哪种语言
    Go语言学习笔记之基础语法(一)
    【Docker 内核详解】namespace 资源隔离(二):UTS namespace & IPC namespace
    kafka面试题(基础-进阶-高阶)
    开发板和电脑相互ping,电脑可以ping开发板,但是开发板ping不通电脑
    如果访问不了www.baidu.com,可能是什么原因?/为什么ping别人的机器ping不通?
    mirror算法 O(1)空间复杂度实现二叉树遍历
    PowerCLi VMware vCenter 通过自建的PXE Server一键批量部署常规New-VM
    画个心??
    抓包整理————ip 协议一[十二]
  • 原文地址:https://blog.csdn.net/u012848304/article/details/126525271