Рубрики
Без рубрики

Одностраничные приложения с Vue.js и колба: Аутентификация JWT

Автор оригинала: Adam McQuistan.

Аутентификация JWT

Добро пожаловать в шестую часть этой многоступенчатой серии учебников по полнотекстовой веб-разработке с использованием Vue.js и Фляжка. В этом посте я продемонстрирую способ использования аутентификации JSON Web Token (JWT).

Код для этого поста можно найти на моем аккаунте GitHub под веткой Шестой пост .

Содержание серии

  1. Настройка и знакомство с VueJS
  2. Навигация по маршрутизатору Vue
  3. Государственное управление с помощью Vuex
  4. RESTful API с колбой
  5. Интеграция AJAX с REST API
  6. Аутентификация JWT (вы здесь)
  7. Развертывание на виртуальном частном сервере

Базовое введение в аутентификацию JWT

Подобно некоторым другим постам в этой серии, я не буду вдаваться в существенные детали теории того, как ЭТО работает. Вместо этого я буду придерживаться прагматичного подхода и демонстрировать специфику его реализации, используя интересующие меня технологии в рамках Flask и Vue.js. Если вы заинтересованы в более глубоком понимании Jwt , я отсылаю вас к превосходному посту Скотта Робинсона здесь, на StackAbuse , где он объясняет низкоуровневые детали этой техники.

В базовом смысле JWT-это закодированный объект JSON, используемый для передачи информации между двумя системами, который состоит из заголовка, полезной нагрузки и сигнатуры в виде [HEADER].[ПОЛЕЗНАЯ НАГРУЗКА].[ПОДПИСЬ] все, что содержится в заголовке HTTP как “Authorization: Bearer [HEADER].[ПОЛЕЗНАЯ НАГРУЗКА].[ПОДПИСЬ]”. Процесс начинается с того, что клиент (запрашивающая система) аутентифицируется на сервере (сервис с требуемым ресурсом), который генерирует JWT, действительный только в течение определенного периода времени. Затем сервер возвращает его в виде подписанного и закодированного токена для хранения клиентом и использования для проверки в последующих коммуникациях.

Аутентификация JWT довольно хорошо работает для SPA-приложений, подобных тому, что строится в этой серии, и приобрела значительную популярность среди разработчиков, реализующих их.

Реализация аутентификации JWT в Flask RESTful API

Что касается колбы, то я буду использовать пакет Python PyJWT для обработки некоторых деталей, связанных с созданием, анализом и проверкой JWTS.

(venv) $ pip install PyJWT

С установленным пакетом PyJWT я могу перейти к реализации частей, необходимых для аутентификации и верификации в приложении Flask. Для начала я дам приложению возможность создавать новых зарегистрированных пользователей, которые будут представлены классом User . Как и все другие классы в этом приложении, класс User будет находиться в models.py модуль.

Первое, что нужно сделать, это импортировать пару функций, generate_password_hash и check_password_hash из модуля werkzeug пакета security , который я буду использовать для генерации и проверки хэшированных паролей. Нет необходимости устанавливать этот пакет, так как он поставляется с колбой автоматически.

"""
models.py
- Data classes for the surveyapi application
"""

from datetime import datetime
from flask_sqlalchemy import SQLAlchemy

from werkzeug.security import generate_password_hash, check_password_hash

db = SQLAlchemy()

Непосредственно под приведенным выше кодом я определяю класс User , который наследуется от класса SQLAlchemy Model , аналогичного другим, определенным в предыдущих сообщениях. Этот класс User должен содержать автоматически сгенерированное целое поле класса первичного ключа с именем id , а затем два строковых поля с именем email и password с адресом электронной почты, настроенным на уникальность. Я также даю этому классу поле relationship для связывания любых опросов, которые может создать пользователь. С другой стороны этого уравнения я добавил creator_id внешний ключ к классу Survey , чтобы связать пользователей с опросами, которые они создают.

Я переопределяю метод __init__ (...) , чтобы я мог хэшировать пароль при создании экземпляра нового объекта User . После этого я даю ему метод класса authenticate , чтобы запросить пользователя по электронной почте и проверить , что предоставленный хэш пароля совпадает с тем, который хранится в базе данных. Если они совпадают, я возвращаю аутентифицированного пользователя. И последнее, но не менее важное: я использовал метод to_dict () , чтобы помочь с сериализацией пользовательских объектов.

"""
models.py
- Data classes for the surveyapi application
"""

#
# omitting imports and what not
#

class User(db.Model):
    __tablename__ = 'users'

    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(120), unique=True, nullable=False)
    password = db.Column(db.String(255), nullable=False)
    surveys = db.relationship('Survey', backref="creator", lazy=False)

    def __init__(self, email, password):
        self.email = email
        self.password = generate_password_hash(password, method='sha256')

    @classmethod
    def authenticate(cls, **kwargs):
        email = kwargs.get('email')
        password = kwargs.get('password')
        
        if not email or not password:
            return None

        user = cls.query.filter_by(email=email).first()
        if not user or not check_password_hash(user.password, password):
            return None

        return user

    def to_dict(self):
        return dict(id=self.id, email=self.email)

class Survey(db.Model):
    __tablename__ = 'surveys'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.Text)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)
    questions = db.relationship('Question', backref="survey", lazy=False)
    creator_id = db.Column(db.Integer, db.ForeignKey('users.id'))

    def to_dict(self):
      return dict(id=self.id,
                  name=self.name,
                  created_at=self.created_at.strftime('%Y-%m-%d %H:%M:%S'),
                  questions=[question.to_dict() for question in self.questions])

Далее необходимо сгенерировать новую миграцию и обновить базу данных с ее помощью, чтобы связать класс Python User с таблицей базы данных users sqlite . Для этого я запускаю следующие команды в том же каталоге, что и мой manage.py модуль.

(venv) $ python manage.py db migrate
(venv) $ python manage.py db upgrade

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

Для начала я добавляю класс User в список импорта из models.py модуль по направлению к верхней части api.py модуль. Пока я там, я пойду дальше и добавлю пару других импортных товаров, которые буду использовать позже.

"""
api.py
- provides the API endpoints for consuming and producing 
  REST requests and responses
"""

from functools import wraps
from datetime import datetime, timedelta

from flask import Blueprint, jsonify, request, current_app

import jwt

from .models import db, Survey, Question, Choice, User

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

Я начну с функции register() view, которая ожидает, что электронная почта и пароль будут отправлены в JSON в теле запроса POST. Пользователь просто создается с тем, что дается для электронной почты и пароля, и я весело возвращаю ответ JSON (что не обязательно является лучшим подходом, но на данный момент он будет работать).

"""
api.py
- provides the API endpoints for consuming and producing 
  REST requests and responses
"""

#
# omitting inputs and other view functions
#

@api.route('/register/', methods=('POST',))
def register():
    data = request.get_json()
    user = User(**data)
    db.session.add(user)
    db.session.commit()
    return jsonify(user.to_dict()), 201

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

Функция login использует метод User.authenticate(...) class для поиска и аутентификации пользователя. Если пользователь, соответствующий заданному адресу электронной почты и паролю, найден, то функция входа переходит к созданию токена JWT, в противном случае возвращается None , в результате чего функция входа возвращает сообщение “сбой аутентификации” с соответствующим кодом состояния HTTP 401.

Я создаю токен JWT с помощью PyJWT (как jwt), кодируя словарь, содержащий следующее:

  • sub – тема jwt, которая в данном случае является электронной почтой пользователя
  • в – время, когда jwt был выдан в
  • exp – это момент истечения срока действия jwt, который в данном случае составляет 30 минут после выдачи
"""
api.py
- provides the API endpoints for consuming and producing 
  REST requests and responses
"""

#
# omitting inputs and other view functions
#

@api.route('/login/', methods=('POST',))
def login():
    data = request.get_json()
    user = User.authenticate(**data)

    if not user:
        return jsonify({ 'message': 'Invalid credentials', 'authenticated': False }), 401

    token = jwt.encode({
        'sub': user.email,
        'iat':datetime.utcnow(),
        'exp': datetime.utcnow() + timedelta(minutes=30)},
        current_app.config['SECRET_KEY'])
    return jsonify({ 'token': token.decode('UTF-8') })

Процесс кодирования использует значение свойства Base Config class SECRET_KEY , определенного в config.py и удерживается в свойстве config current_app после создания приложения Flask.

Далее я хотел бы разбить функциональность GET и POST, которая в настоящее время находится в плохо названной функции представления с именем fetch_survey (...) , показанной ниже в ее исходном состоянии. Вместо этого я позволю fetch_surveys(...) отвечать исключительно за извлечение всех опросов при запросе “/api/surveys/” с помощью запроса GET. С другой стороны, создание опроса, которое происходит, когда тот же URL-адрес попадает в POST-запрос, теперь будет находиться в новой функции с именем create_survey(...) .

Так вот…

"""
api.py
- provides the API endpoints for consuming and producing 
  REST requests and responses
"""

#
# omitting inputs and other view functions
#

@api.route('/surveys/', methods=('GET', 'POST'))
def fetch_surveys():
    if request.method == 'GET':
        surveys = Survey.query.all()
        return jsonify([s.to_dict() for s in surveys])
    elif request.method == 'POST':
        data = request.get_json()
        survey = Survey(name=data['name'])
        questions = []
        for q in data['questions']:
            question = Question(text=q['question'])
            question.choices = [Choice(text=c) for c in q['choices']]
            questions.append(question)
        survey.questions = questions
        db.session.add(survey)
        db.session.commit()
        return jsonify(survey.to_dict()), 201

становится этим…

"""
api.py
- provides the API endpoints for consuming and producing 
  REST requests and responses
"""

#
# omitting inputs and other view functions
#

@api.route('/surveys/', methods=('POST',))
def create_survey(current_user):
    data = request.get_json()
    survey = Survey(name=data['name'])
    questions = []
    for q in data['questions']:
        question = Question(text=q['question'])
        question.choices = [Choice(text=c) for c in q['choices']]
        questions.append(question)
    survey.questions = questions
    survey.creator = current_user
    db.session.add(survey)
    db.session.commit()
    return jsonify(survey.to_dict()), 201


@api.route('/surveys/', methods=('GET',))
def fetch_surveys():
    surveys = Survey.query.all()
    return jsonify([s.to_dict() for s in surveys])

Реальный ключ теперь заключается в защите функции create_survey(...) view, чтобы только аутентифицированные пользователи могли создавать новые опросы. Другими словами, если POST-запрос сделан против “/api/surveys”, приложение должно проверить, что он выполняется действительным и аутентифицированным пользователем.

В комплекте идет удобный Python decorator! Я буду использовать декоратор, чтобы обернуть функцию create_survey(...) view, которая проверит, что запросчик содержит допустимый токен JWT в своем заголовке, и отклонит любые запросы, которые этого не делают. Я вызову этот декоратор token_required и реализую его выше всех других функций представления в api.py вот так:

"""
api.py
- provides the API endpoints for consuming and producing 
  REST requests and responses
"""

#
# omitting inputs and other view functions
#

def token_required(f):
    @wraps(f)
    def _verify(*args, **kwargs):
        auth_headers = request.headers.get('Authorization', '').split()

        invalid_msg = {
            'message': 'Invalid token. Registeration and / or authentication required',
            'authenticated': False
        }
        expired_msg = {
            'message': 'Expired token. Reauthentication required.',
            'authenticated': False
        }

        if len(auth_headers) != 2:
            return jsonify(invalid_msg), 401

        try:
            token = auth_headers[1]
            data = jwt.decode(token, current_app.config['SECRET_KEY'])
            user = User.query.filter_by(email=data['sub']).first()
            if not user:
                raise RuntimeError('User not found')
            return f(user, *args, **kwargs)
        except jwt.ExpiredSignatureError:
            return jsonify(expired_msg), 401 # 401 is Unauthorized HTTP status code
        except (jwt.InvalidTokenError, Exception) as e:
            print(e)
            return jsonify(invalid_msg), 401

    return _verify

Основная логика этого декоратора заключается в том, чтобы:

  1. Убедитесь, что он содержит заголовок “Authorization” со строкой, которая выглядит как токен JWT
  2. Проверьте, что срок действия JWT не истек, о чем PyJWT позаботится за меня, выдав ошибку подписи Expired , если она больше не действительна
  3. Проверьте, что JWT является допустимым токеном, о котором PyJWT также заботится, бросая Недопустимая ошибка токена если она недопустима
  4. Если все допустимо, то связанный пользователь запрашивается из базы данных и возвращается в функцию, которую оборачивает декоратор

Теперь все, что осталось, это добавить декоратор в метод create_survey(...) вот так:

"""
api.py
- provides the API endpoints for consuming and producing 
  REST requests and responses
"""

#
# omitting inputs and other functions
#

@api.route('/surveys/', methods=('POST',))
@token_required
def create_survey(current_user):
    data = request.get_json()
    survey = Survey(name=data['name'])
    questions = []
    for q in data['questions']:
        question = Question(text=q['question'])
        question.choices = [Choice(text=c) for c in q['choices']]
        questions.append(question)
    survey.questions = questions
    survey.creator = current_user
    db.session.add(survey)
    db.session.commit()
    return jsonify(survey.to_dict()), 201

Реализация аутентификации JWT в Vue.js СПА

С бэкендом уравнение аутентификации завершено Теперь мне нужно застегнуть клиентскую сторону, реализовав аутентификацию JWT в Vue.js. Я начинаю с создания нового модуля в приложении под названием “utils” в каталоге src и размещения index.js файл внутри папки utils. Этот модуль будет содержать две вещи:

  1. Шина событий, которую я могу использовать для отправки сообщений по всему приложению, когда происходят определенные вещи, такие как неудачная аутентификация в случае истечения срока действия JWT
  2. Функция для проверки JWT, чтобы увидеть, если он все еще действителен или нет

Эти две вещи реализуются следующим образом:

// utils/index.js

import Vue from 'vue'

export const EventBus = new Vue()

export function isValidJwt (jwt) {
  if (!jwt || jwt.split('.').length < 3) {
    return false
  }
  const data = JSON.parse(atob(jwt.split('.')[1]))
  const exp = new Date(data.exp * 1000) // JS deals with dates in milliseconds since epoch
  const now = new Date()
  return now < exp
}

Переменная EventBus – это всего лишь экземпляр объекта Vue. Я могу использовать тот факт, что объект Vue имеет как $emit , так и пару методов $on /| $off , которые используются для генерации событий, а также для регистрации и отмены регистрации событий.

Функция invalid(jwt) – это то, что я буду использовать, чтобы определить, является ли пользователь аутентифицированным на основе информации в JWT. Напомним из более раннего базового объяснения JWTs, что стандартный набор свойств находится в закодированном объекте JSON вида “[HEADER].[ПОЛЕЗНАЯ НАГРУЗКА].[ПОДПИСЬ]”. Например, скажем, у меня есть следующий JWT ‘eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJleGFtcGxlQG1haWwuY29tIiwiaWF0IjoxNTIyMzI2NzMyLCJleHAiOjE1MjIzMjg1MzJ9.1n9fx0vL9GumDGatwm2vfUqQl3yZ7Kl4t5NWMvW-pgw’. Я могу декодировать средний раздел тела, чтобы проверить его содержимое, используя следующий JavaScript:

const token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJleGFtcGxlQG1haWwuY29tIiwiaWF0IjoxNTIyMzI2NzMyLCJleHAiOjE1MjIzMjg1MzJ9.1n9fx0vL9GumDGatwm2vfUqQl3yZ7Kl4t5NWMvW-pgw'
const tokenParts = token.split('.')
const body = JSON.parse(atob(tokenParts[1]))
console.log(body)   // {sub: "[email protected]", iat: 1522326732, exp: 1522328532}

Здесь содержимым тела токена являются sub , представляющий электронную почту подписчика, iat , который выдается с отметкой времени в секундах, и exp , который является временем истечения срока действия токена в секундах от эпохи (количество секунд, прошедших с 1 января 1970 года (полночь UTC/GMT), не считая високосных секунд (в ISO 8601: 1970-01-01T00:00:00Z)). Как вы можете видеть, я использую значение exp в функции is Valid Jwt(jwt) , чтобы определить, истек ли срок действия JWT или нет.

Далее нужно добавить пару новых функций AJAX для выполнения вызовов REST API Flask для регистрации новых пользователей и входа в систему существующих, плюс мне нужно будет изменить функцию postNewSurvey (...) , чтобы включить заголовок, содержащий JWT.

// api/index.js

//
// omitting stuff ... skipping to the bottom of the file
//

export function postNewSurvey (survey, jwt) {
  return axios.post(`${API_URL}/surveys/`, survey, { headers: { Authorization: `Bearer: ${jwt}` } })
}

export function authenticate (userData) {
  return axios.post(`${API_URL}/login/`, userData)
}

export function register (userData) {
  return axios.post(`${API_URL}/register/`, userData)
}

Хорошо, теперь я могу использовать эти вещи в магазине для управления состоянием, необходимым для обеспечения надлежащей функциональности аутентификации. Для начала я импортирую EventBus и допустимую функцию Jwt(...) из модуля utils, а также две новые функции AJAX из модуля api. Затем добавьте определение объекта user и строку токена jwt в объект состояния хранилища следующим образом:

// store/index.js

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

// imports of AJAX functions will go here
import { fetchSurveys, fetchSurvey, saveSurveyResponse, postNewSurvey, authenticate, register } from '@/api'
import { isValidJwt, EventBus } from '@/utils'

Vue.use(Vuex)

const state = {
  // single source of data
  surveys: [],
  currentSurvey: {},
  user: {},
  jwt: ''
}

//
// omitting all the other stuff below
//

Далее мне нужно добавить пару методов действий, которые будут вызывать либо register (...) , либо authenticate(...) AJAX-функции, которые мы только что определили. Я называю того , кто отвечает за аутентификацию пользователя login (...) , который вызывает функцию authenticate(...) AJAX, и когда он возвращает успешный ответ, содержащий новый JWT , он фиксирует мутацию, которую я назову setJwtToken , которая должна быть добавлена к объекту мутаций. В случае неудачного запроса аутентификации я связываю метод catch с цепочкой обещаний, чтобы поймать ошибку, и использую EventBus для выдачи события, уведомляющего всех подписчиков о том, что аутентификация не удалась.

Метод действия register(...) очень похож на login(...) , на самом деле он фактически использует login(...) . Я также показываю небольшую модификацию метода submit New Survey(...) action, который передает токен JWT в качестве дополнительного параметра вызову postNewSurvey(...) AJAX.

const actions = {
  // asynchronous operations

  //
  // omitting the other action methods...
  //

  login (context, userData) {
    context.commit('setUserData', { userData })
    return authenticate(userData)
      .then(response => context.commit('setJwtToken', { jwt: response.data }))
      .catch(error => {
        console.log('Error Authenticating: ', error)
        EventBus.$emit('failedAuthentication', error)
      })
  },
  register (context, userData) {
    context.commit('setUserData', { userData })
    return register(userData)
      .then(context.dispatch('login', userData))
      .catch(error => {
        console.log('Error Registering: ', error)
        EventBus.$emit('failedRegistering: ', error)
      })
  },
  submitNewSurvey (context, survey) {
    return postNewSurvey(survey, context.state.jwt.token)
  }
}

Как уже упоминалось ранее, мне нужно добавить новую мутацию, которая явно устанавливает JWT и пользовательские данные.

const mutations = {
  // isolated data mutations

  //
  // omitting the other mutation methods...
  //

  setUserData (state, payload) {
    console.log('setUserData payload = ', payload)
    state.userData = payload.userData
  },
  setJwtToken (state, payload) {
    console.log('setJwtToken payload = ', payload)
    localStorage.token = payload.jwt.token
    state.jwt = payload.jwt
  }
}

Последнее, что я хотел бы сделать в магазине, – это добавить метод getter, который будет вызван в нескольких других местах приложения, который будет указывать, аутентифицирован ли текущий пользователь или нет. Я достигаю этого, вызывая функцию is Valid Jwt(jwt) из модуля utils в геттере следующим образом:

const getters = {
  // reusable data accessors
  isAuthenticated (state) {
    return isValidJwt(state.jwt.token)
  }
}

Ладно, я уже близко. Мне нужно добавить новый Vue.js компонент для страницы входа/регистрации в приложении. Я создаю файл Login.vue в каталоге components. В разделе шаблона я даю ему два поля ввода, одно для электронной почты, которая будет служить именем пользователя, а другое для пароля. Под ними находятся две кнопки: одна для входа в систему, если вы уже являетесь зарегистрированным пользователем, и другая для регистрации.



Очевидно, что этот компонент будет нуждаться в некотором локальном состоянии, связанном с пользователем, как указано моим использованием v-model в полях ввода, поэтому я добавляю это в свойство data компонента далее. Я также добавляю свойство error Msg data, которое будет содержать любые сообщения, испускаемые EventBus в случае неудачной регистрации или аутентификации. Чтобы использовать EventBus Я подписываюсь на события “неудачная регистрация” и “неудачная аутентификация” в mounted Vue.js этап жизненного цикла компонентов и отмените их регистрацию на этапе beforeDestroy . Еще одна вещь, которую следует отметить, – это использование обработчиков событий @click , вызываемых при нажатии кнопок Входа и регистрации. Они должны быть реализованы как компонентные методы, authenticate() и register() .



Хорошо, теперь мне просто нужно, чтобы остальная часть приложения знала, что компонент входа существует. Я делаю это, импортируя его в модуль маршрутизатора и определяя его маршрут. Пока я нахожусь в модуле маршрутизатора, мне нужно внести дополнительные изменения в маршрут компонента New Survey , чтобы защитить его доступ только для аутентифицированных пользователей, как показано ниже:

// router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/components/Home'
import Survey from '@/components/Survey'
import NewSurvey from '@/components/NewSurvey'
import Login from '@/components/Login'
import store from '@/store'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'Home',
      component: Home
    }, {
      path: '/surveys/:id',
      name: 'Survey',
      component: Survey
    }, {
      path: '/surveys',
      name: 'NewSurvey',
      component: NewSurvey,
      beforeEnter (to, from, next) {
        if (!store.getters.isAuthenticated) {
          next('/login')
        } else {
          next()
        }
      }
    }, {
      path: '/login',
      name: 'Login',
      component: Login
    }
  ]
})

Здесь стоит упомянуть, что я использую router guard vue-router перед входом , чтобы проверить, аутентифицирован ли текущий пользователь через IsAuthenticated getter из магазина. Если IsAuthenticated возвращает false, то я перенаправляю приложение на страницу входа.

Закодировав компонент входа в систему и определив его маршрут, я могу обеспечить доступ к нему через компонент router-link в компоненте заголовка в components/Header.vue. Я условно показываю либо ссылку на компонент New Survey , либо компонент Login , используя геттер IsAuthenticated store еще раз в вычисляемом свойстве в компоненте Header , на который ссылаются директивы v-if , например:







Отлично! Теперь я наконец могу запустить серверы dev для приложения Flask и Vue.js приложение и тест, чтобы увидеть, могу ли я зарегистрировать и войти в систему пользователя.

Сначала я запускаю сервер разработки Flask.

(venv) $ python appserver.py

Затем сервер webpack dev для компиляции и обслуживания Vue.js приложение.

$ npm run dev

В моем браузере я посещаю http://localhost:8080 (или любой другой порт, указанный сервером webpack dev) и убедитесь, что на навигационной панели теперь отображается “Login/Register” вместо “Create Survey”, как показано ниже:

Вход в навигационную панель

Затем я нажимаю на ссылку “Войти/Зарегистрироваться” и заполняю входные данные для электронной почты и пароля, затем нажимаю “Зарегистрироваться”, чтобы убедиться, что он работает должным образом, и я перенаправляюсь обратно на домашнюю страницу и вижу ссылку “Создать опрос”, отображаемую вместо ссылки “Войти/Зарегистрироваться”, которая была там до регистрации.

Страница входа в систему

Ладно, моя работа в основном закончена. Единственное, что осталось сделать, это добавить небольшую обработку ошибок в submit Survey(...) Vue.js метод компонента New Survey для обработки события, когда токен истекает, когда пользователь создает новый опрос, например:


Ресурсы

Хотите узнать больше о различных фреймворках, используемых в этой статье? Попробуйте проверить некоторые из следующих ресурсов для более глубокого погружения в использование Vue.js или создание внутренних API-интерфейсов на Python:

Вывод

В этом посте я продемонстрировал, как реализовать аутентификацию JWT в приложении опроса с помощью Vue.js и Фляжка. JWT-это популярный и надежный метод обеспечения аутентификации в SPA-приложениях, и я надеюсь, что после прочтения этого поста вы почувствуете себя комфортно, используя эти технологии для защиты ваших приложений. Тем не менее, я рекомендую посетить статью Скотта StackAbuse для более глубокого понимания того, как и почему работает JWT.

Как всегда, спасибо за чтение и не стесняйтесь комментировать или критиковать ниже.