[Vue] Connectez-vous pour voir le contrôle des autorisations (jeton)

Référence :
[1] Le projet vue stockera le jeton dans le magasin (vuex) et le stockage local
[2] À propos du contrôle des autorisations du backend (la troisième partie de l'article)

Fondamentalement modifié selon la référence ci-dessus.
Les principaux ajouts sont ces pièces :
Table des matières

1. Installez vuex pour utiliser la fonction de stockage

【citation】

  • Comprendre l'introduction du jeton (de session, de cookie) Jeton
    : le jeton est lorsque le client demande fréquemment des données au serveur, et le serveur se rend fréquemment dans la base de données pour interroger le nom d'utilisateur et le mot de passe et les compare pour déterminer si le nom d'utilisateur et le mot de passe sont correctes ou non, et faire des invites correspondantes. Dans ce contexte, Token a vu le jour.
    token est une chaîne de chaînes générée côté serveur en tant que jeton permettant au client de faire une demande. Si le frontal utilise le nom d'utilisateur/mot de passe pour demander l'authentification au serveur et que le serveur s'authentifie avec succès, le serveur renverra un jeton au frontal. Le frontal peut amener Token à prouver son statut juridique à chaque fois qu'il le demande. Si ce jeton est persistant côté serveur (comme stocké dans une base de données), il s'agit d'un jeton d'identité permanent (sauf si une date d'expiration est définie).

  • Avantages du jeton
    Le jeton est entièrement géré par l'application, il peut donc éviter la politique de même origine
    Le jeton peut éviter les attaques CSRF
    Le jeton peut être sans état et peut être partagé entre plusieurs services pour
    réduire la pression sur le serveur, réduire la base de données de requêtes fréquentes et faire le serveur plus robuste.

cnpm install vuex --save

Dans le cas d'une séparation complète des extrémités avant et arrière, l'idée générale de la vérification des jetons dans le projet Vue est la suivante :

  1. Lors de la première connexion, le frontal ajuste l'interface de connexion du back-end et envoie le numéro de téléphone (téléphone) et le mot de passe (mot de passe)
  2. Le backend reçoit la demande, vérifie le téléphone et le mot de passe, et renvoie un jeton au frontend si la vérification réussit
  3. Le frontal obtient le jeton, le stocke dans localStorage et vuex, et passe à l'index de la page d'accueil du routage
  4. Chaque fois que le frontal saute vers la route, il juge s'il y a un jeton dans localStroage, sinon, il sautera à la page de connexion, si c'est le cas, il sautera à la page de routage correspondante
  5. Chaque fois que vous ajustez l'interface backend, vous devez ajouter un jeton dans l'en-tête de la requête
  6. Le backend juge s'il y a un jeton dans l'en-tête de la requête. S'il y a un jeton, il obtiendra le jeton et le vérifiera. Si la vérification réussit, il renverra les données. Si la vérification échoue (par exemple : jeton expiré), il renverra 400. S'il n'y a pas de jeton dans l'en-tête de la requête, il renverra 400 (voir【 2】)
  7. Si le frontal reçoit un code d'état de 400, effacez les informations du jeton et passez à la page de connexion

2. modèle/stockage.js

C'est l'équivalent d'une méthode, qui définit certaines opérations sur les informations stockées.Il est également possible d'écrire directement dans store/index.js sans écrire cette méthode.

Plusieurs méthodes sont mises en œuvre ici :

  1. Définir la clé et la valeur stockées
  2. obtenir la valeur par clé
  3. Supprimer la valeur correspondant à la clé
  4. Supprimer toutes les informations mises en cache
  5. Obtenez toutes les informations de mise en scène
var storage = {
    
    
  set (key, value) {
    
    
    localStorage.setItem(key, JSON.stringify(value))
  },
  get (key) {
    
    
    return JSON.parse(localStorage.getItem(key))
  },
  remove (key) {
    
    
    localStorage.removeItem(key)
  },
  removeAll () {
    
    
    localStorage.clear()
  },
  getAll () {
    
    
    let len = localStorage.length
    // eslint-disable-next-line no-array-constructor
    let arr = []
    for (var i = 0; i < len; i++) {
    
    
      var getKey = localStorage.key(i)
      var getVal = localStorage.getItem(getKey)
      arr[i].push({
    
    
        'key': getKey,
        'val': getVal
      })
    }
    return arr
  }
}

export default storage

3. magasin/index.js

correspond à un processus spécifique.
La méthode écrite en mutations peut être appelée par store.commit( 方法名, ), par exemple pour appeler :具体数值set_token(state, token)_this.$store.commit('set_token', token)

import Vue from 'vue'
import Vuex from 'vuex'
import storage from '../model/storage'

Vue.use(Vuex)
// 用Vuex.Store对象用来记录token
// 存储需要的信息
const store = new Vuex.Store({
    
    
  state: {
    
    
    token: '',
    info: {
    
    
      phone: '',
      job: '',
      id: ''
    }
  },

  // 计算属性
  mutations: {
    
    
    // 修改token,并将token存入localStorage
    set_token (state, token) {
    
    
      state.token = token
      storage.set('token', token)
      console.log('store、localstorage保存token成功!')
    },
    del_token (state) {
    
    
      state.token = ''
      storage.remove('token')
    },
    // 可选
    setUserInfo (state, userName) {
    
    
      state.userName = userName
    }
  },
  actions: {
    
    
    // removeToken: (context) => {
    
    
    // context.commit('set_token')
    // }
  }
})
export default store

4. utils/request.js和config/index.js

request.js équivaut à réécrire une méthode de requête. Lorsque vous utilisez la méthode axios, utilisez simplement la requête réécrite directement. Dans le même temps, la vérification de l'intercepteur est définie. Si le statut renvoyé par l'arrière-plan est 400 (vous définissez le code d'état spécifique en arrière-plan), c'est-à-dire que vous n'êtes pas connecté/le jeton est invalide.

import axios from 'axios'
import store from '@/store'
import router from '@/router'

// create an axios instance
const service = axios.create({
    
    
  baseURL: '/api', // url = base url + request url
  timeout: 5000 // request timeout
})

// 添加请求拦截器,若token存在则在请求头中加token,不存在也继续请求
service.interceptors.request.use(
  config => {
    
    
    // 每次发送请求之前检测都vuex存有token,那么都要放在请求头发送给服务器,没有则不带token
    // Authorization是必须的
    if (store.state.token) {
    
    
      config.headers.Authorization = store.getters.get_token
    }
    return config
  },
  error => {
    
    
    console.log('在request拦截器显示错误:', error.response)
    return Promise.reject(error)
  }
)

// respone拦截器
service.interceptors.response.use(
  response => {
    
    
    // 在status正确的情况下,code不正确则返回对应的错误信息(后台自定义为200是正确,并且将错误信息写在message),正确则返回响应
    return response.data.status === 200 ? response : Promise.reject(response.data.message)
  },
  error => {
    
    
    // 在status不正确的情况下,判别status状态码给出对应响应
    if (error.response) {
    
    
      console.log('在respone拦截器显示错误:', error.response)
      switch (error.response.status) {
    
    
        case 401:
          store.commit('del_token')
          router.replace({
    
    
            // 跳转到登录页面
            path: '/login',
            // 将跳转的路由path作为参数,登录成功后跳转到该路由
            query: {
    
     redirect: router.currentRoute.fullPath }
          })
          break
        case 400:
          store.commit('del_token')
          alert('请先登录')
          router.replace({
    
    
            // 跳转到登录页面
            path: '/login',
            // 将跳转的路由path作为参数,登录成功后跳转到该路由,方便登陆成功后再跳转到刚刚的页面
            query: {
    
     redirect: router.currentRoute.fullPath }
          })
          break
      }
    }
    return Promise.reject(error.response.data)
  }
)

export default service

La raison pour laquelle ces deux parties sont réunies est que config/index.js définit le nom de domaine de la route de saut baseURL, ce qui permet l'accès entre domaines et facilite l'appel, c'est-à-dire que l'appel "/api" appelle la cible définie dans config, seulement Modifiez simplement le proxyTable :

	proxyTable: {
    
    
      '/api': {
    
    
        target: 'http://localhost:8090',
        changeOrigin: true,
        pathRewrite: {
    
    
          '^/api': ''
        }
      }
    },

5. routeur/index.js

Vérifiez si le jeton est invalide à chaque saut de page. En cas d'échec, reconnectez-vous.

import Vue from 'vue'
import Router from 'vue-router'
import Login from '../pages/login.vue'
import Index from '../pages/index.vue'
import store from '../store'
Vue.use(Router)

const routes = [
  {
    
    
    path: '/',
    name: 'Login',
    component: Login
  },
  {
    
    
    path: '/login',
    name: 'Login',
    component: Login
  },
  {
    
    
    path: '/index',
    name: 'Index',
    component: Index,
    meta: {
    
    
      requireAuth: true
    }
  }
]
const router = new Router({
    
     routes: routes })

router.beforeEach((to, from, next) => {
    
    
  // 判断要去的路由有没有requiresAuth
  // to.matched.some(r => r.meta.requireAuth) or to.meta.requiresAuth
  if (to.matched.some(r => r.meta.requireAuth)) {
    
    
    if (store.state.token) {
    
    
      // 有token,进行request请求,后台还会验证token
      next()
    } else {
    
    
      next({
    
    
        path: '/login',
        // 将刚刚要去的路由path(却无权限)作为参数,方便登录成功后直接跳转到该路由,这要进一步在登陆页面判断
        query: {
    
     redirect: to.fullPath }
      })
    }
  } else {
    
    
    next()
  }
})

export default router

6. Applications spécifiques

Commencez par configurer le magasin global dans main.js :

import Vue from 'vue'
import App from './App'
import 'ant-design-vue/dist/antd.css'
import router from './router'
import Antd from 'ant-design-vue'
import axios from 'axios'
import VueAxios from 'vue-axios'
import store from './store'

Vue.use(VueAxios, axios)

Vue.config.productionTip = false
Vue.use(Antd)

/* eslint-disable no-new */
new Vue({
    
    
  el: '#app',
  router,
  store,
  components: {
    
     App },
  template: '<App/>'
})

La raison de redéfinir this comme _this :

  • Lors de l'obtention des valeurs, this entre parenthèses n'est pas le total this, donc $route et $store ne peuvent pas être obtenus.
import request from '../utils/request.js'
export default {
    
    
  name: 'LoginForm',
  data () {
    
    
    return {
    
    
      form: this.$form.createForm(this, {
    
     name: 'login_form' })
    }
  },
  methods: {
    
    
    login (e) {
    
    
      e.preventDefault()
      let _this = this
      this.form.validateFields((err, values) => {
    
    
        if (!err) {
    
    
          request({
    
    
            url: '/login',
            method: 'post',
            params: {
    
    
              phone: values.phone,
              password: values.password
            }
          })
            .then(function (response) {
    
    
              console.log(response)
              if (response.status === 200) {
    
    
                const token = response.data.token
                _this.$store.commit('set_token', token)
                console.log(response.data)
                _this.$router.push('index')
              } else {
    
    
                // alert(response.data.data.message)
              }
            }).catch(function (error) {
    
    
              console.log(error)
            })
        }
      })
    }
  }
}

7. Problème : Après avoir actualisé la page, les données du magasin seront perdues et vous reviendrez à la page de connexion.

Solution : utilisez sessionStorage pour stocker les données de jeton,
vous devez modifier app.vue

<script>
export default {
    
    
  name: 'App',
  data () {
    
    
    return {
    
    
      imgUrl: require('./assets/icon_logo.png')
    }
  },
  created () {
    
    
    if (sessionStorage.getItem('store')) {
    
    
      let state = JSON.parse(sessionStorage.getItem('store'))
      this.$store.commit('set_token', state.token)
      this.$store.commit('setUserInfo', state.info)
      sessionStorage.setItem('store', '')
    }
    window.onbeforeunload = this.beforeunloadFn
  },
  methods: {
    
    
    beforeunloadFn (e) {
    
    
      // 这个事件只有在鼠标真正和浏览器有了交互,再刷新或者关闭时才会触发, 浏览器事件会弹框确认用户是否要离开页面
      e = e || window.event
      if (e) {
    
    
        sessionStorage.setItem('store', JSON.stringify(this.$store.state))
      }
    }
  }
}
</script>

Puisqu'il est nécessaire de vérifier s'il y a un jeton lors du saut vers la route, il router/index.jsfaut également modifier la vérification de la présence d'un jeton :

En fait, cela ne fait qu'augmenter sessionStoragela vérification du jeton stocké dans le

router.beforeEach((to, from, next) => {
    
    
  // 判断要去的路由有没有requiresAuth
  // to.matched.some(r => r.meta.requireAuth) or to.meta.requiresAuth
  if (to.matched.some(r => r.meta.requireAuth)) {
    
    
    if (store.state.token || JSON.parse(sessionStorage.getItem('store')).token) {
    
    
      // 有token,进行request请求,后台还会验证token
      console.log('有token')
      next()
    } else {
    
    
      next({
    
    
        path: '/login',
        // 将刚刚要去的路由path(却无权限)作为参数,方便登录成功后直接跳转到该路由,这要进一步在登陆页面判断
        query: {
    
     redirect: to.fullPath }
      })
    }
  } else {
    
    
    next()
  }
})

Je suppose que tu aimes

Origine blog.csdn.net/Kandy0125/article/details/121365444
conseillé
Classement