在 main.js 中的beforeEach中,去校验用户是否登录

355 阅读5分钟

因为 Vuex 中保存的用户信息,在每次页面刷新的时候,都会丢失。

而且,在每次用户切换页面的时候,都要去判断用户是否登录。

所以,在每次页面切换的时候,都去请求一个后端接口,去校验用户是否登录。

如果已经登录,那么这个接口,会返回 username ,并且返回一个新的 JWT Token,然后再存到客户端,用来延长 Token 的有效时长。

1. 首先在 server/server.js 中,编写一个校验用户是否登录的接口

server.js :

// 模拟后端,提供接口
let express = require('express')

let app = express()
let bodyParser = require('body-parser')
let jwt = require('jsonwebtoken') // 引入 JWT的包

// 处理跨域
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', 'http://localhost:8080')
  res.header('Access-Control-Allow-Methods', 'GET, HEAD, OPTIONS, POST, PUT')
  res.header(
    'Access-Control-Allow-Headers',
    'Origin, X-Requested-With, Content-Type, Accept, Authorization, X-Auth-Token'
  )
  // 如果是options 请求,那么不理它
  if (req.method.toLowerCase() === 'options') {
    return res.end()
  }
  next()
})

app.use(bodyParser.json())

// 设置一个 JWT Token 的密钥, 用来给 JWT Token生成的字符串加密
let secret = 'zf'

app.get('/user', (req, res) => {
  res.json({ name: 'zf' })
})

// 登录接口
app.post('/login', (req, res) => {
  let { username, password } = req.body // 请求体里面的数据,在body里面取的
  // 登录成功
  if (username === 'admin' && password === '123456') {
    // 登录成功
    res.json({
      code: 0,
      username: 'admin',
      // 给客户端返回一个Token
      auth_token: jwt.sign({ username: 'admin' }, secret, {
        expiresIn: 120 // 过期时间 120S, 超过120S失效,即2分钟失效
      })
    })
  } else {
    res.json({
      code: 1,
      data: '用户名或者密码错误'
    })
  }
})

// 判断用户是否登录
app.get('/validate', (req, res) => {
  console.log(req.headers)
  // 判断请求头上,是否有 JWT Token
  // let token = req.headers['X-Auth-Token']
  let token = req.headers['x-auth-token'] // 取请求头中的x-auth-token时,必须小写。

  console.log('tt')

  // 没有取到请求头中的 X-Auth-Token
  console.log(token)

  // 使用jwt包提供的方法 verify() 来校验 JWT Token
  // decode 就等于之前存的 {username: 'admin'} 对象
  // JWT Token验证成功后,就可以把{username: 'admin'} 这个对象,返回给客户端
  jwt.verify(token, secret, (err, decode) => {
    if (err) {
      // err表示已过期
      return res.json({
        code: 1,
        data: 'token已失效'
      })
    } else {
      res.json({
        // 表示用户已登录,并重新把用户名,返回给客户端
        username: decode.username,
        code: 0,
        // 是用户操作期间,需要把Token的时效延长
        // 因为beforeEach方法,在用户每切换一次页面的时候,都会调用。
        // 所以,每一次用户切换页面的时候,都会请求这个方法。
        // 所以,每请求这个方法,就把Token的过期时间,重新设置一下。
        // 所以,需要再给用户传回去一个新的JWT Token,
        // 然后,用户在客户端,再把新的JWT Token存一下。
        auth_token: jwt.sign({ username: 'admin' }, secret, {
          expiresIn: 120 // 过期时间 120S, 超过120S失效,即2分钟失效
        })
      })
    }
  })
})

app.listen(3000)

2. 在 api/user.js 新建一个调取接口的方法

user.js:

// 用户登录接口

// 专门写,调取后端的接口
import axios from '../utils/ajaxRequest'

export const getUser = () => {
  // return 以后,在其他组件,调用: getUser().then()
  // import { getUser } from './api/user'
  // getUser().then(data => {})
  return axios.request({
    url: '/user',
    method: 'get'
  })
}

// 登录方法
// 传入用户名和密码
export const login = (username, password) => {
  return axios.request({
    method: 'POST',
    url: '/login',
    data: {
      username,
      password
    }
  })
}

// 在main.js 中的beforeEach中,去校验用户是否登录
export const validate = () => {
  return axios.request({
    method: 'get',
    url: '/validate'
  })
}

3. 然后在 actions 中,去调取 api/user.js 中的方法

store/index.js:

import Vue from 'vue'
import Vuex from 'vuex'

import { login, validate } from '../api/user'
// import * as types from './mutation-types'

import { setLocal } from '../utils/local' // 将JWT Token 存入 localStorage

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    isShowLoading: false, // 是否显示loading效果
    username: 'x' // 登录后的用户名
  },
  mutations: {
    showLoading (state) {
      state.isShowLoading = true // 显示loading
    },
    hideLoading (state) {
      state.isShowLoading = false // 隐藏loading
    },
    setUser (state, username) {
      state.username = username // 更改公共状态
    }
  },
  // 在actions中,去调取 api/user.js 中的方法
  // 然后,在组件中,去触发 actions 中的方法
  actions: {
    // 一般在actions中,写接口调用
    async doLogin ({ commit }, payload) {
      console.log(payload.username)
      console.log(payload.password)
      // store.dispatch('doLogin')
      let res = await login(payload.username, payload.password)
      // 登录成功
      if (res.code === 0) {
        // 把用户名存入 Vuex
        commit('setUser', res.username)

        // 将返回客户端的JWT Token保存到客户端上,每次请求时,请求头上加上Token,
        // 然后服务端校验 Token, 如果Token不正确,或者过期,则相当于没有登录
        setLocal('auth_token', res.auth_token)
      } else {
        // 返回的失败的promise
        return Promise.reject(res.data)
      }
    },

    // 在 main.js 中的beforeEach 去校验用户是否登录
    async doValidate ({ commit }) {
      let res = await validate()
      // 表示已登录过
      if (res.code === 0) {
        // 再给username设置一次值
        commit('setUser', res.username)

        // 再把校验后的Token,再存入客户端一次,从而延长时效。
        setLocal('auth_token', res.auth_token)
      }
      return res.code === 0 // 返回用户是否已登录, true表示已登录, false表示用户失效
    }
  }
})

4. 在 main.js 中的beforeEach方法中,去校验用户是否登录

因为,页面每次切换,都会调用 beforeEach 方法,所以,每次页面切换的时候,都会去校验用户是否登录。

main.js :

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store/index'

// 使用 ant design vue
import Antd from 'ant-design-vue'
import 'ant-design-vue/dist/antd.css'

// 全局插件
import vueComponent from './components/vue-component.js'

Vue.config.productionTip = false

// 全局注册 Antd
Vue.use(Antd)

Vue.use(vueComponent)

// 做一个白名单,哪些路径,不需要进行用户登录的校验
let whiteList = ['/order_details_tabs']

// beforeEach,对用户是否登录,进行校验
router.beforeEach(async (to, from, next) => {
  // 如果在白名单,那么就不需要经过用户登录的校验
  if (whiteList.includes(to.path)) {
    return next()
  }

  // 在服务端,有一个接口,去判断用户是否登录
  // 去请求后台接口,判断用户是否登录
  let isLogin = await store.dispatch('doValidate')
  console.log(isLogin)

  // 如果这个页面需要先登录
  let needLogin = to.matched.some(match => match.meta.needLogin)

  if (needLogin) {
    // 如果需要登录
    // 并且已经登录,就接着往下走
    if (isLogin) {
      next()
    } else {
      // 没有登录
      next('/login') // 就跳转到login页面
    }
  } else {
    // 否则
    // 如果已经登录了,并且访问的是 'login'的时候,就跳转到首页
    if (isLogin && to.name === 'login') {
      next('/')
    } else {
      next()
    }
  }
  next()
})

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

5. 注意:

必须在 server/server.js 中这样取请求头中的 'x-auth-token'
必须小写。

6. 参考:

www.jb51.net/article/123…

www.jianshu.com/p/871d480ed…

www.jianshu.com/p/6704d2a0e…