Vue 登录/登出以及JWT认证

1. 后端代码概览

server/router/index.js

请求

router.get('/getUserInfo', function (req, res, next) { // 登录请求
  res.send('success')
})

router.get('/authorization', (req, res, next) => { //  获取验证请求
  const userName = req.userName // 从app.js中拿到userName
  res.send({
    code: 200,
    mes: 'success',
    data: {
      token: jwt.sign({
        name: userName
      }, 'abcd', { // 重新生成一个新的Token, 返回给前端
        expiresIn: '1d'
      }),
      rules: {
        page: {
          home: true,
          home_index: true,
          about: true,
          argu: true,
          count_to: true,
          menu_page: true,
          upload: true,
          form: true,
          folder_tree: true,
          table_page: true,
          render_page: true,
          split_pane: true,
          parent: true,
          child: true,
          named_view: true,
          store: true,
          main: true
        },
        component: {
          edit_button: true,
          publish_button: false
        }
      }
    }
  })
})

server/router/index.js

登录接口

const getPasswordByName = (name) => {
  return { password: '123' }
}

router.post('/login', function (req, res, next) {
  const { userName, password } = req.body
  if (userName) {
    const userInfo = password ? getPasswordByName(userName) : ''
    if (!userInfo || !password || userInfo.password !== password) {
      res.status(401).send({
        code: 401,
        mes: 'user name or password is wrong',
        data: {}
      })
    } else {
      res.send({
        code: 200,
        mes: 'success',
        data: {
          token: jwt.sign({ name: userName }, 'abcd', { // jwt--jsonwebtoken第三方库生成Token
            expiresIn: '1d' // 过期时间 1day/H 60 ; 一天/小时 60秒
          })
        }
      })
    }
  } else {
    res.status(401).send({
      code: 401,
      mes: 'user name is empty',
      data: {}
    })
  }
})

server/views/app.js

Token校验

const whiteListUrl = { // 白名单 不需要做校验的接口
  get: [],
  post: [
    '/index/login'
  ]
}

const hasOneOf = (str, arr) => {
  return arr.some(item => item.includes(str))
}

app.all('*', (req, res, next) => {
  let method = req.method.toLowerCase() // 获取当前请求方式
  let path = req.path // 获取当前请求路径
  if (whiteListUrl[method] && hasOneOf(path, whiteListUrl[method])) next() // 过滤 有些接口不需要做Token校验
  else { // 需要校验的接口
    const token = req.headers.authorization // 取到请求头中Token
    if (!token) res.status(401).send('there is no token, please login')
    else {
      jwt.verify(token, 'abcd', (error, decode) => { // jsonwebtoken第三方库校验Token
        if (error) {
          res.send({
            code: 401,
            mes: 'token error',
            data: {}
          })
        } else {
          req.userName = decode.name
          next()
        }
      })
    }
  }
})

2. 登录以及Token处理

  • npm i js-cookie md5 -S
  • src/views/login.vue




Vue 登录/登出以及JWT认证_第1张图片

  • src/api/user.js

export const login = ({ userName, password }) => {
  return axios.request({
    url: '/index/login',
    method: 'post',
    data: {
      userName,
      password
    }
  })
}

export const authorization = () => {
  return axios.request({
    url: '/users/authorization',
    method: 'get'
  })
}
  • src/store/module/user.js

import {
  login
} from '@/api/user'

const actions = {
  login ({
    commit
  }, {
    userName,
    password
  }) {
    login({
      userName,
      password
    }).then(res => {
      console.log(res) // {code: 200, mes: "success", data: {token: "eyJhbGciOiJIUz"}}
    }).catch(error => {
      console.log(error)
    })
  }
}

点击提交成功返回在这里插入图片描述
密码错误返回:
在这里插入图片描述

  • src/lib/util.js

import Cookies from 'js-cookie'

// 将Token放在haeder中, 每次发送请求都带有Token
export const setToken = (token, tokenName = 'token') => {
  Cookies.set(tokenName, token) // 往cookie中存值
}

export const getToken = (tokenName = 'token') => { // 获取Token
  return Cookies.get(tokenName)
}
    • store/module/user.js

const actions = {
  login({
    commit
  }, {
    userName,
    password
  }) {
    return new Promise(resolve, reject) => {
      login({
        userName,
        password
      }).then(res => {
        // console.log(res) // {code: 200, mes: "success", data: {token: "eyJhbGciOiJIUz"}}
        if (res.code === 200 && res.data.token) {
          setToken(res.data.token)
          resolve()
        } else {
          reject(new Error('错误'))
        }
      }).catch(error => {
        reject(error)
      })
    }
  }
}
    • login.vue
    handleSubmit () {
      this.login({
        userName: this.userName,
        password: this.password
      }).then(res => {
        this.$router.push({
          name: 'home'
        })
      }).catch(error => {
        console.log(error)
      })
    }
  }
  • src/router/index.js

路由导航守卫

router.beforeEach((to, from, next) => {
 const token = getToken()
 if (token) {
   // 验证token是有效的
 } else {
   if (to.name === 'login') next()
   else next({ name: 'login' })
 }
})
  • 传Token到服务端

    • api/user.js

export const authorization = () => {
  return axios.request({
    url: '/users/authorization',
    method: 'get'
  })
}
  • lib/axios.js

传入token

import { getToken } from '@/lib/util'

// 全局响应拦截器
  interceptors (instance, url) {
    instance.interceptors.request.use(config => { // 请求拦截器
      // 添加全局的loading...
      // Spin.show() ---遮罩组件
      // 队列中有请求时 显示loadong界面, 反之同理
      if (!Object.keys(this.queue).length) {
        // Spin.show()
      }
      this.queue[url] = true
      config.headers['Authorization'] = getToken() // 传入token
      return config
    }, error => {
      return Promise.reject(error)
    })
  • 获取Token

    • store/module/user.js

import {
  login,
  authorization
} from '@/api/user'
  
  authorization({
    commit
  }, token) {
    return new Promise((resolve, reject) => {
      authorization().then(res => {
        if(parseInt(res.code) === 401){
          reject(new Error('token error'))
        } else {
          resolve()
        }
      }).catch(error =>{
        reject(error)
      })
    })
  }
}
  • router/index.js

import store from '@/store'
import {
  getToken,
  setToken
} from '@/lib/util'

// 导航守卫
router.beforeEach((to, from, next) => {
  const token = getToken()
  if (token) {
    // 验证token是有效的
    store.dispatch('authorization', token).then(() => {
      if (to.name === 'login') {
        next({
          name: 'home'
        })
      } else next()
    }).catch(() => {
      setToken('')
      next({
        name: 'login'
      })
    })
  } else {
    if (to.name === 'login') next()
    else {
      next({
        name: 'login'
      })
    }
  }
})

3. Token过期处理

api/user.js

authorization ({
    commit
  }, token) {
    return new Promise((resolve, reject) => {
      authorization().then(res => {
        if (parseInt(res.code) === 401) {
          reject(new Error('token error'))
        } else {
          setToken(res.data.token) // 重置token 即重置过期时间
          resolve()
        }
      }).catch(error => {
        reject(error)
      })
    })
  }

4. 退出登录

api/user.js

  logout () {
    setToken('')
  }
  • 调用登出接口
import { mapActions } from 'vuex'
export default {
  methods: {
    ...map(['logout']),
    handleLogout() {
      this.logout()
    }
  }
}

你可能感兴趣的:(Vue,[前端][致精通]真正的大师,永远都怀着一颗学徒的心,[前端][重度学习])