zoukankan      html  css  js  c++  java
  • 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

    <template>
      <div>
        <Input v-model="userName" placeholder="账号"></Input>
        <Input v-model="password" type="password" placeholder="密码"></Input>
        <Button type="primary" @click="handleSubmit">登录</Button>
      </div>
    </template>
    
    <script>
    import { mapActions } from 'vuex'
    export default {
      name: 'login_page',
      data () {
        return {
          userName: '',
          password: ''
        }
      },
      methods: {
        ...mapActions(['login']),
        handleSubmit () {
          this.login({
            userName: this.userName,
            password: this.password
          })
        }
      }
    }
    </script>
    

    在这里插入图片描述

    • 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()
        }
      }
    }
    
  • 相关阅读:
    解决nodejs使用yarn安装vue-cli提示'vue' 不是内部或外部命令
    转载——java synchronized详解
    转载——Java中this关键字和super关键字的使用
    转载—— JUnit单元测试教程(翻译自Java Code Geeks)
    Usage and Idioms——Use with Maven
    转载——JUnit 5 新特性
    Usage and Idioms——Categories
    转载——单元测试利器 JUnit 4
    Usage and Idioms——Test Fixtures
    深入JUnit源码之Rule
  • 原文地址:https://www.cnblogs.com/izhaong/p/12154279.html
Copyright © 2011-2022 走看看