Решение аутентификации в Vue с помощью Vuex

Vue.js Vuex

Традиционно многие люди используют локальное хранилище для управления токенами, сгенерированными в результате проверки на стороне клиента. Большой вопрос заключается в том, как лучше управлять токенами аутентификации, что позволит нам хранить больше информации о пользователях.

ЭтоVuexэффект. Vuex этоVue.jsСтатус управления приложением.. Он используется в качестве центрального хранилища для всех компонентов приложения, а правила используются для обеспечения того, чтобы состояние могло изменяться только предсказуемым образом.

Звучит как лучший вариант для постоянной проверки локального хранилища? Давайте исследовать вместе.

Создание модулей приложения

Для этого проекта мы хотим создать приложение с использованием vuex иvue-routerВью приложение. Мы будем использовать vue cli 3.0 для создания проекта vue и выбора маршрутизации и vuex из вариантов.

Выполните следующую команду, чтобы начать создание:

$ vue create vue-auth

Следуйте подсказкам диалогового окна, добавьте необходимую информацию и выберите нужные нам параметры для завершения установки.

Далее установитеaxios:

$ npm install axios --save

Настроить Аксиос

Нам нужны аксиомы во многих компонентах. Давайте настроим его глобально, чтобы нам не приходилось импортировать его каждый раз, когда он нам нужен.

Открыть./src/main.jsфайл и добавьте следующее:

[...]
import store from './store'
import Axios from 'axios'

Vue.prototype.$http = Axios;
const token = localStorage.getItem('token')
if (token) {
  Vue.prototype.$http.defaults.headers.common['Authorization'] = token
}
[...]

Теперь, когда мы хотим использовать аксиомы внутри компонента, мы можем использоватьthis.$http, что напрямую эквивалентно axios. Мы также можем указать собственный токен в заголовке axios, установив身份验证, так что если токен потребуется, наш запрос будет под контролем. Таким образом, нам не нужно устанавливать токен в любое время, когда мы хотим отправить запрос.

Связанные курсы:Vue для создания интернет-магазина

Как только это будет сделано, давайте использовать сервер для обработки аутентификации.

Создайте службу проверки подлинности

Я уже писал об этом, когда объяснял, как решать аутентификацию с помощью vue-router. Смотри внимательноSetup Node.js Serverэта глава.

Создание компонентов

компонент входа

СоздайтеLogin.vueсуществует./src/componentsПод содержанием. После этого добавьте шаблон на страницу входа:

<template>
 <div>
   <form class="login" @submit.prevent="login">
     <h1>Sign in</h1>
     <label>Email</label>
     <input required v-model="email" type="email" placeholder="Name"/>
     <label>Password</label>
     <input required v-model="password" type="password" placeholder="Password"/>
     <hr/>
     <button type="submit">Login</button>
   </form>
 </div>
</template>

Когда вы закончите, добавьте атрибут данных, чтобы связать его с HTML-формой:

[...]
<script>
  export default {
    data(){
      return {
        email : "",
        password : ""
      }
    },
  }
</script>

Теперь давайте добавим метод для входа в систему:

[...]
<script>
  export default {
    [...]
    methods: {
      login: function () {
        let email = this.email 
        let password = this.password
        this.$store.dispatch('login', { email, password })
       .then(() => this.$router.push('/'))
       .catch(err => console.log(err))
      }
    }
  }
</script>

Мы используем действие vuex —loginразрешить аутентификацию. Мы можем преобразовать действия в обратные вызовы, чтобы мы могли делать классные вещи в наших собственных компонентах.

Регистрация компонентов

Аналогично компоненту входа в систему, мы получаем его для зарегистрированных пользователей. Создается в каталоге компонентовRegister.vueи добавьте следующее:

<template>
  <div>
    <h4>Register</h4>
    <form @submit.prevent="register">
      <label for="name">Name</label>
      <div>
          <input id="name" type="text" v-model="name" required autofocus>
      </div>

      <label for="email" >E-Mail Address</label>
      <div>
          <input id="email" type="email" v-model="email" required>
      </div>

      <label for="password">Password</label>
      <div>
          <input id="password" type="password" v-model="password" required>
      </div>

      <label for="password-confirm">Confirm Password</label>
      <div>
          <input id="password-confirm" type="password" v-model="password_confirmation" required>
      </div>

      <div>
          <button type="submit">Register</button>
      </div>
    </form>
  </div>
</template>

Давайте определим эти свойства данных, которые будут привязаны к форме:

[...]
<script>
  export default {
    data(){
      return {
        name : "",
        email : "",
        password : "",
        password_confirmation : "",
        is_admin : null
      }
    },
  }
</script>

Теперь добавим метод в:

[...]
<script>
  export default {
    [...]
    methods: {
      register: function () {
        let data = {
          name: this.name,
          email: this.email,
          password: this.password,
          is_admin: this.is_admin
        }
        this.$store.dispatch('register', data)
       .then(() => this.$router.push('/'))
       .catch(err => console.log(err))
      }
    }
  }
</script>

компоненты безопасности

Давайте создадим обычный компонент, который будет отображаться после аутентификации пользователя. файл с именемSecure.vue, и добавьте в него следующее:

<template>
  <div>
    <h1>This page is protected by auth</h1>
  </div>
</template>

Обновить компоненты приложения

Открыть./src/App.vueфайл и добавьте в него следующее:

<template>
  <div id="app">
    <div id="nav">
      <router-link to="/">Home</router-link> |
      <router-link to="/about">About</router-link><span v-if="isLoggedIn"> | <a @click="logout">Logout</a></span>
    </div>
    <router-view/>
  </div>
</template>

Если пользователь входит в систему, вы можете увидеть связанныйLogoutВсе же? отлично.

Теперь давайте добавим логику для выхода из системы.

<script>
  export default {
    computed : {
      isLoggedIn : function(){ return this.$store.getters.isLoggedIn}
    },
    methods: {
      logout: function () {
        this.$store.dispatch('logout')
        .then(() => {
          this.$router.push('/login')
        })
      }
    },
  }
</script>

Когда пользователь нажимает кнопку выхода, мы на самом деле делаем две вещи — вычисляем состояние аутентификации пользователя и отправляем событие выхода в хранилище vuex. После выхода используемthis.$router.push('/login'), переключите пользователя наloginстраница. Конечно, вы можете измениться там, где вы хотите, чтобы пользователь прыгал.

Вот и все. Давайте создадим модуль разрешений с помощью vuex.

Модуль разрешений Vuex

Если вы читали предыдущуюSetup Node.js ServerВ части ** вы должны заметить, что нам нужно хранить токен разрешения пользователя локально, и в то же время, когда пользователю предоставлено разрешение, нам нужно получить токен и информацию о пользователе в любое время.

Во-первых, давайте создадим vuexstore.jsдокумент:

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

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    status: '',
    token: localStorage.getItem('token') || '',
    user : {}
  },
  mutations: {

  },
  actions: {

  },
  getters : {

  }
})

Если вы заметили, мы представили vue, vuex и axios одновременно, а затем разрешили vue использовать vuex, потому что это важный шаг.

Мы определили свойства состояния. Теперь состояние Vuex может поддерживать статус проверки.jwtтокен и информация о пользователе.

Создать событие входа в Vuex

Действия Vuex в основном отправляют изменения в хранилище vuex. мы создадимloginдействие, которое аутентифицирует пользователя на сервере и отправит учетные данные пользователя в хранилище vuex. Открыть./src/store.jsфайл и добавьте следующее к объекту действий:

login({commit}, user){
    return new Promise((resolve, reject) => {
      commit('auth_request')
      axios({url: 'http://localhost:3000/login', data: user, method: 'POST' })
      .then(resp => {
        const token = resp.data.token
        const user = resp.data.user
        localStorage.setItem('token', token)
        axios.defaults.headers.common['Authorization'] = token
        commit('auth_success', token, user)
        resolve(resp)
      })
      .catch(err => {
        commit('auth_error')
        localStorage.removeItem('token')
        reject(err)
      })
    })
},

Действие входа через vuexcommitПодтвердите, мы будем использовать его для запуска изменений. Эти изменения можно записать в хранилище vuex.

Мы вызываем путь входа на сервер и возвращаем необходимые данные. Мы храним токен локально, а затем передаемauth_successобновить сохраненную информацию о пользователе и токен. На этом этапе мы также устанавливаем заголовокaxios.

Мы можем хранить токены в хранилище vuex, но если пользователь покинет наше приложение, все хранилище в vuex исчезнет. Чтобы гарантировать, что пользователю не придется снова входить в систему в течение действительного периода, мы можем хранить токен только локально.

Важно, чтобы вы знали, как они работают, чтобы вы могли решить, чего именно вы хотите достичь.

Мы возвращаем обещание, чтобы мы могли ответить, когда пользователь вошел в систему.

Создать Vuex注册мероприятие

картинаloginсобытиеregisterСобытия работают так же. В том же файле добавьте к объекту действий следующее:

register({commit}, user){
  return new Promise((resolve, reject) => {
    commit('auth_request')
    axios({url: 'http://localhost:3000/register', data: user, method: 'POST' })
    .then(resp => {
      const token = resp.data.token
      const user = resp.data.user
      localStorage.setItem('token', token)
      axios.defaults.headers.common['Authorization'] = token
      commit('auth_success', token, user)
      resolve(resp)
    })
    .catch(err => {
      commit('auth_error', err)
      localStorage.removeItem('token')
      reject(err)
    })
  })
},

это сloginСобытия работают во многом так. имеют общие мутаторыloginа такжеregister, с той же целью - завести пользователей в систему.

Создать Vuex退出мероприятие

Мы хотим, чтобы пользователь мог выйти из системы, и в то же время мы хотим уничтожить данные последней аутентифицированной сессии. В то же самоеactionsобъект, добавьте следующее:

logout({commit}){
  return new Promise((resolve, reject) => {
    commit('logout')
    localStorage.removeItem('token')
    delete axios.defaults.headers.common['Authorization']
    resolve()
  })
}

Теперь, когда пользователь нажмет, чтобы выйти, мы будем удалены раньшеaxiosГарнитураjwtтокен . Теперь они не смогут выполнять транзакции, для которых требуются токены.

Создать мутации

Как я упоминал ранее, мутаторы используются для изменения состояния хранилища vuex. Определим используемые мутаторы в нашем приложении. В объекте мутаторов добавьте следующее:

mutations: {
  auth_request(state){
    state.status = 'loading'
  },
  auth_success(state, token, user){
    state.status = 'success'
    state.token = token
    state.user = user
  },
  auth_error(state){
    state.status = 'error'
  },
  logout(state){
    state.status = ''
    state.token = ''
  },
},

Создать геттеры

Мы используем геттеры для получения значений свойств в состоянии vuex. В этом случае роль геттера состоит в том, чтобы отделить данные приложения от логики приложения и гарантировать, что мы не пропустим конфиденциальную информацию.

добавить следующее вgettersВ объекте:

getters : {
  isLoggedIn: state => !!state.token,
  authStatus: state => state.status,
}

Вы согласитесь со мной, что это более чистый способ доступа к сохраненным данным ☺️.

Скрыть страницу за Auth

Вся цель этой статьи — реализовать аутентификацию, чтобы определенные страницы не могли просматриваться пользователями без разрешений. Для этого нам нужно знать, какую страницу хочет посетить пользователь, и когда пользователь авторизуется, у нас есть способ это проверить. Нам также нужен какой-то способ, если определенные страницы, авторизованные или неавторизованные пользователи могут получить доступ по отдельности или одновременно. Это важные соображения, и, к счастью, мы можем сделать это с помощью vue-router.

Определение маршрутов к авторизованным и неавторизованным страницам

Открыть./src/router.jsфайл и импортируем нужные нам:

import Vue from 'vue'
import Router from 'vue-router'
import store from './store.js'
import Home from './views/Home.vue'
import About from './views/About.vue'
import Login from './components/Login.vue'
import Secure from './components/Secure.vue'
import Register from './components/Register.vue'

Vue.use(Router)

Как видите, мы представили созданные нами vue, vue-router и vuex. Мы также представили все определенные компоненты и настроили маршруты, используемые в vue.

Определим маршрут:

[...]
let router = new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      name: 'home',
      component: Home
    },
    {
      path: '/login',
      name: 'login',
      component: Login
    },
    {
      path: '/register',
      name: 'register',
      component: Register
    },
    {
      path: '/secure',
      name: 'secure',
      component: Secure,
      meta: { 
        requiresAuth: true
      }
    },
    {
      path: '/about',
      name: 'about',
      component: About
    }
  ]
})

export default router

Определение нашего маршрута очень общее. Для маршрутов, требующих проверки авторизации, нам нужно добавить дополнительные данные, чтобы гарантировать, что когда пользователь обращается к ним, мы можем их идентифицировать. В этом суть мета-свойств, добавляемых к определениям маршрутов. если хочешь спросить_" Я могу добавить больше данных вметаданныеи использовать его?»Я скажу вам твердо, это абсолютно 😁.

Решить несанкционированный пример доступа

У нас есть собственные определения маршрутов. Теперь давайте проверим наличие несанкционированного доступа и примем меры. существуетrouter.jsфайл, добавьте следующее вexport default routerДо:

router.beforeEach((to, from, next) => {
  if(to.matched.some(record => record.meta.requiresAuth)) {
    if (store.getters.isLoggedIn) {
      next()
      return
    }
    next('/login') 
  } else {
    next() 
  }
})

Из этого поста, используя vue router для аутентификации, вы можете вспомнить, что здесь у нас очень сложный механизм, и он становится очень большим и очень запутанным. vuex упростил его для нас, мы можем добавить любое условие к маршруту. В нашем хранилище vuex мы можем определить действия для проверки этих условий и получения значений, которые их возвращают.

Пример решения проблемы с истечением срока действия токена

Поскольку мы храним токен локально, его можно хранить вечно. Это означает, что всякий раз, когда мы открываем наше приложение, оно может автоматически аутентифицировать пользователя, даже если срок действия токена истек. В большинстве случаев наши запросы будут завершаться ошибкой с недопустимыми токенами. Это плохой опыт для пользователя.

Теперь откройте./src/App.vueфайл и в скрипте добавьте следующее:

export default {
  [...]
  created: function () {
    this.$http.interceptors.response.use(undefined, function (err) {
      return new Promise(function (resolve, reject) {
        if (err.status === 401 && err.config && !err.config.__isRetryRequest) {
          this.$store.dispatch(logout)
        }
        throw err;
      });
    });
  }
}

Мы перехватили запрос axios и определили, получили ли мы его401未授权отклик. Если да, то мы распространяемlogoutсобытие, то пользователь выходит из приложения. Это заставит пользователя перейти к ранее разработанномуloginстраницу, чтобы они могли снова войти в систему.

Я согласен, что это улучшит пользовательский опыт ☺️.

конец

Из предыдущих статей вы можете видеть, что наше текущее приложение значительно изменилось благодаря внедрению vuex. Теперь, вместо того, чтобы полагаться на постоянную проверку токена, повсюду царят беспорядочные условия. Мы можем просто использовать хранилище vuex для управления разрешениями и проверки состояния в приложении всего несколькими строками кода.

Надеюсь, это поможет вам создавать лучшие приложения.