از طریق منوی جستجو مطلب مورد نظر خود در وبلاگ را به سرعت پیدا کنید
برنامه های تک صفحه ای با Vue.js و Flask: JWT Authentication به قسمت ششم این مجموعه آموزشی چند قسمتی خوش آمدید روی توسعه وب تمام پشته با استفاده از Vue.js و Flask. در این پست من روشی برای استفاده از JSON Web Token (JWT) احراز هویت را نشان خواهم داد. کد این پست را می توانید پیدا کنید روی حساب GitHub من در زیر شاخه …
سرفصلهای مطلب
احراز هویت JWT
به قسمت ششم این مجموعه آموزشی چند قسمتی خوش آمدید روی توسعه وب تمام پشته با استفاده از Vue.js و Flask. در این پست من روشی برای استفاده از JSON Web Token (JWT) احراز هویت را نشان خواهم داد.
کد این پست را می توانید پیدا کنید روی حساب GitHub من در زیر شاخه پست ششم.
محتوای سری
- راه اندازی و آشنایی با VueJS
- مسیریاب Vue
- مدیریت دولتی با Vuex
- RESTful API با Flask
- ادغام AJAX با REST API
- احراز هویت JWT (تو اینجایی)
- استقرار در یک سرور خصوصی مجازی
مقدمه اولیه احراز هویت JWT
مشابه برخی از پست های دیگر در این مجموعه، من وارد جزئیات مهم نمی شوم روی نظریه چگونگی JWT آثار. در عوض، من یک رویکرد عملگرایانه را در پیش خواهم گرفت و ویژگی های اجرای آن را با استفاده از فناوری های مورد علاقه در Flask و Vue.js نشان خواهم داد. اگر علاقه مند به درک عمیق تر از JWT هستید، شما را به پست عالی اسکات رابینسون در اینجا ارجاع می دهم. روی rasanegar، جایی که او جزئیات سطح پایین این تکنیک را توضیح می دهد.
در مفهوم اساسی یک JWT یک شی JSON کدگذاری شده است که برای انتقال اطلاعات بین دو سیستم استفاده می شود که از یک هدر، یک بار و یک امضا به شکل زیر تشکیل شده است. (HEADER).(PAYLOAD).(SIGNATURE)
همه در هدر HTTP به عنوان “Authorization: Bearer (HEADER).(PAYLOAD).(SIGNATURE)” موجود است. این process با تأیید اعتبار مشتری (سیستم درخواست کننده) با سرور (سرویس با منبع مورد نظر) شروع می شود که یک JWT تولید می کند که فقط برای مدت زمان مشخصی معتبر است. سپس سرور این را به عنوان یک رمز امضا شده و رمزگذاری شده برای مشتری برمی گرداند تا آن را ذخیره کرده و برای تأیید در ارتباطات بعدی استفاده کند.
احراز هویت JWT برای برنامههای SPA مانند برنامهای که در این سری ساخته شده است، بسیار خوب عمل میکند و محبوبیت قابل توجهی در میان توسعهدهندگانی که آنها را پیادهسازی میکنند، به دست آورده است.
پیاده سازی JWT Authentication در Flask RESTful API
در سمت Flask چیزها، من از بسته پایتون استفاده خواهم کرد PyJWT برای رسیدگی به برخی از جزئیات پیرامون ایجاد، تجزیه و اعتبارسنجی JWT ها.
(venv) $ pip install PyJWT
با نصب بسته PyJWT می توانم حرکت کنم روی برای پیاده سازی قطعات لازم برای احراز هویت و تأیید در برنامه Flask. برای شروع، من به برنامه این امکان را می دهم که کاربران ثبت نام شده جدیدی ایجاد کند که با a نمایش داده می شوند User
کلاس مانند تمام کلاس های دیگر در این برنامه User
کلاس ساکن خواهد شد models.py
مدول.
اولین موردی که باید انجام داد این است که import چند تابع، generate_password_hash
و check_password_hash
از werkzeug بسته ها security
ماژولی که من از آن برای تولید و تأیید رمزهای عبور هش شده استفاده خواهم کرد. نیازی به نصب این بسته نیست زیرا با Flask به صورت خودکار ارائه می شود.
"""
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
با ایمیلی که برای منحصر به فرد بودن پیکربندی شده است. من هم به این کلاس a می دهم relationship
زمینه برای مرتبط کردن هر نظرسنجی که کاربر ممکن است ایجاد کند. در طرف دیگر این معادله a را اضافه کردم 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))
مرحله بعدی ایجاد یک مهاجرت جدید و به روز رسانی پایگاه داده با آن برای جفت شدن است User
کلاس پایتون با جدول پایگاه داده کاربران 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()
عملکرد را مشاهده کنید که انتظار دارد ایمیل و رمز عبور همراه با 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
سرد. پشتیبان قادر است کاربران جدیدی ایجاد کند که مشتاق به ایجاد گودهای نظرسنجی هستند، بنابراین، بهتر است برخی از عملکردها را برای احراز هویت آنها اضافه کنم و به آنها اجازه بدهم که آنها را دریافت کنند. روی با ایجاد نظرسنجی های خود
تابع ورود به سیستم از User.authenticate(...)
متد کلاس برای یافتن و احراز هویت یک کاربر. اگر کاربر مطابق با ایمیل و رمز عبور داده شده پیدا شود، عملکرد ورود به سیستم برای ایجاد یک نشانه JWT پیشرفت می کند، در غیر این صورت None
برگردانده میشود، و در نتیجه تابع ورود به سیستم، پیامی را با کد وضعیت HTTP 401 برمیگرداند.
من توکن JWT را با استفاده از PyJWT ایجاد می کنم (به عنوان jwt
) با رمزگذاری یک فرهنگ لغت حاوی موارد زیر:
- فرعی – موضوع از
jwt
، که در این مورد ایمیل کاربر است - iat – زمان
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') })
رمزگذاری process از ارزش استفاده می کند BaseConfig
کلاس SECRET_KEY
دارایی تعریف شده در config.py
و در current_app
ویژگی پیکربندی ‘s پس از ایجاد برنامه Flask.
در مرحله بعد، من می خواهم عملکرد GET و POST را که در حال حاضر در یک تابع view با نام ضعیف به نام وجود دارد، تجزیه کنم. 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(...)
عملکرد را مشاهده کنید تا فقط کاربران تأیید شده بتوانند نظرسنجی جدید ایجاد کنند. به روشی دیگر، اگر یک درخواست POST علیه “/api/surveys” انجام شود، برنامه باید بررسی کند که توسط یک کاربر معتبر و تأیید شده انجام شده باشد.
دکوراتور مفید پایتون وارد می شود! من از دکوراتور برای بسته بندی استفاده خواهم کرد create_survey(...)
عملکرد را مشاهده کنید که بررسی میکند درخواستکننده دارای یک نشانه JWT معتبر در سربرگ خود است و درخواستهایی که فاقد آن هستند را رد میکند. من به این دکوراتور زنگ می زنم token_required
و آن را بالاتر از همه توابع view دیگر در آن پیاده سازی کنید 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
منطق اصلی این دکوراتور این است که:
- اطمینان حاصل کنید که حاوی سرصفحه “Authorization” با رشته ای است که شبیه یک توکن JWT است.
- تأیید کنید که JWT منقضی نشده است، که PyJWT با پرتاب کردن از من مراقبت می کند
ExpiredSignatureError
اگر دیگر معتبر نیست - تأیید کنید که JWT یک توکن معتبر است، که PyJWT نیز با پرتاب a از آن مراقبت می کند
InvalidTokenError
اگر معتبر نیست - اگر همه معتبر باشند، کاربر مرتبط از پایگاه داده درخواست می شود و به عملکردی که تزئین کننده در حال بسته بندی است بازگردانده می شود.
اکنون تنها چیزی که باقی مانده است اضافه کردن دکوراتور به آن است 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 Authentication در Vue.js SPA
با تکمیل قسمت پشتی معادله احراز هویت، اکنون باید دکمه سمت کلاینت را با اجرای احراز هویت JWT در Vue.js اضافه کنم. من با ایجاد یک ماژول جدید در برنامه به نام “utils” در داخل برنامه شروع می کنم src
دایرکتوری و قرار دادن یک index.js
فایل داخل پوشه utils. این ماژول شامل دو چیز خواهد بود:
- گذرگاه رویدادی که میتوانم از آن برای ارسال پیامها در اطراف برنامه زمانی که موارد خاصی اتفاق میافتد استفاده کنم، مانند احراز هویت ناموفق در صورت منقضی شدن JWT
- تابعی برای بررسی یک 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
و یک جفت $روی
/ $off
روش هایی که برای انتشار رویدادها و همچنین ثبت و لغو ثبت رویدادها استفاده می شوند.
این isValid(jwt)
تابع چیزی است که من برای تعیین اینکه آیا یک کاربر مبتنی بر احراز هویت است یا خیر استفاده خواهم کرد روی اطلاعات موجود در JWT از توضیح اولیه اولیه JWT ها به یاد بیاورید که مجموعه استانداردی از ویژگی ها در یک شی JSON کدگذاری شده به شکل “(HEADER).(PAYLOAD).(SIGNATURE)” قرار دارند. به عنوان مثال، بگویید من JWT زیر را دارم:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJleGFtcGxlQG1haWwuY29tIiwiaWF0IjoxNTIyMzI2NzMyLCJleHAiOjE1MjIzMjg1MzJ9.1n9fx0vL9GumDGatwm2vfUqQl3yZ7Kl4t5NWMvW-pgw
میتوانم بخش میانی بدنه را رمزگشایی کنم تا محتوای آن را با استفاده از جاوا اسکریپت زیر بررسی کنم:
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:00 Z)). همانطور که می بینید من از آن استفاده می کنم exp
ارزش در isValidJwt(jwt)
برای تعیین اینکه آیا JWT منقضی شده است یا خیر.
مرحله بعدی اضافه کردن چند توابع جدید AJAX برای برقراری تماس با Flask REST API برای ثبت نام کاربران جدید و لاگین کردن کاربران موجود است، به علاوه من باید آن را تغییر دهم. 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)
}
خوب، اکنون می توانم از این موارد در فروشگاه استفاده کنم تا وضعیت مورد نیاز برای ارائه عملکرد صحیح احراز هویت را مدیریت کنم. برای شروع من import EventBus
و isValidJwt(...)
عملکرد از ماژول utils و همچنین دو عملکرد جدید AJAX از ماژول api. سپس یک تعریف از a اضافه کنید 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
، که باید به شی mutations اضافه شود. در صورت درخواست احراز هویت ناموفق، a را زنجیره می کنم catch
روش به زنجیره وعده برای گرفتن خطا و استفاده از EventBus
برای انتشار رویدادی که به مشترکین اطلاع می دهد که احراز هویت ناموفق است.
این register(...)
روش عمل کاملا مشابه است login(...)
، در واقع، در واقع از آن استفاده می کند login(...)
. من همچنین یک اصلاح کوچک در آن نشان می دهم submitNewSurvey(...)
متد عمل که توکن 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
}
}
آخرین کاری که می خواهم در فروشگاه انجام دهم این است که یک متد دریافت کننده اضافه کنم که در چند مکان دیگر در برنامه فراخوانی می شود که نشان می دهد کاربر فعلی احراز هویت شده است یا خیر. من این کار را با تماس گرفتن انجام می دهم isValidJwt(jwt)
عملکرد از ماژول utils در گیرنده به شرح زیر است:
const getters = {
// reusable data accessors
isAuthenticated (state) {
return isValidJwt(state.jwt.token)
}
}
باشه نزدیک میشم من باید یک جزء Vue.js جدید برای ورود / ثبت نام اضافه کنم page در برنامه. من یک فایل به نام ایجاد می کنم Login.vue
در فهرست اجزای سازنده در بخش قالب دو فیلد ورودی به آن میدهم، یکی برای ایمیل، که به عنوان نام کاربری و دیگری برای رمز عبور است. در زیر آنها دو دکمه وجود دارد، یکی برای ورود به سیستم اگر قبلاً کاربر ثبت نام کرده اید و دیگری برای ثبت نام.
<!-- components/Login.vue -->
<template>
<div>
<section class="hero is-primary">
<div class="hero-body">
<div class="container has-text-centered">
<h2 class="title">Login or Register</h2>
<p class="subtitle error-msg">{{ errorMsg }}</p>
</div>
</div>
</section>
<section class="section">
<div class="container">
<div class="field">
<label class="label is-large" for="email">Email:</label>
<div class="control">
<input type="email" class="input is-large" id="email" v-model="email">
</div>
</div>
<div class="field">
<label class="label is-large" for="password">Password:</label>
<div class="control">
<input type="password" class="input is-large" id="password" v-model="password">
</div>
</div>
<div class="control">
<a class="button is-large is-primary" @click="authenticate">Login</a>
<a class="button is-large is-success" @click="register">Register</a>
</div>
</div>
</section>
</div>
</template>
بدیهی است که این کامپوننت به برخی از وضعیت های محلی مرتبط با یک کاربر نیاز دارد که با استفاده من از آن نشان داده شده است v-model
در فیلدهای ورودی، بنابراین من آن را در ویژگی داده کامپوننت بعدی اضافه می کنم. من همچنین اضافه می کنم errorMsg
ویژگی داده که هر پیامی را که توسط EventBus
در صورت عدم موفقیت ثبت نام یا احراز هویت. برای استفاده از EventBus
من مشترک هستم failedRegistering
و failedAuthentication
حوادث در mounted
مرحله چرخه حیات کامپوننت Vue.js، و آنها را در آن لغو ثبت کنید beforeDestroy
صحنه. نکته دیگری که باید به آن توجه کرد، استفاده از آن است @click
کنترلکنندههای رویداد با کلیک کردن روی دکمههای ورود و ثبت نام فراخوانی میشوند. آنها باید به عنوان متدهای جزء پیاده سازی شوند، authenticate()
و register()
.
<!-- components/Login.vue -->
<script>
export default {
data () {
return {
email: '',
password: '',
errorMsg: ''
}
},
methods: {
authenticate () {
this.$store.dispatch('login', { email: this.email, password: this.password })
.then(() => this.$router.push('/'))
},
register () {
this.$store.dispatch('register', { email: this.email, password: this.password })
.then(() => this.$router.push('/'))
}
},
mounted () {
EventBus.$روی('failedRegistering', (msg) => {
this.errorMsg = msg
})
EventBus.$روی('failedAuthentication', (msg) => {
this.errorMsg = msg
})
},
beforeDestroy () {
EventBus.$off('failedRegistering')
EventBus.$off('failedAuthentication')
}
}
</script>
خوب، اکنون فقط باید به بقیه برنامه اطلاع دهم که مؤلفه Login وجود دارد. من این کار را با وارد کردن آن در ماژول روتر و تعریف مسیر آن انجام می دهم. در حالی که من در ماژول روتر هستم، باید یک تغییر اضافی در آن ایجاد کنم NewSurvey
مسیر کامپوننت برای محافظت از دسترسی آن فقط به کاربران تأیید شده مانند شکل زیر:
// 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
}
)
})
در اینجا قابل ذکر است که من در حال استفاده هستم vue-router
نگهبان مسیر beforeEnter
برای بررسی اینکه آیا کاربر فعلی از طریق آن احراز هویت شده است یا خیر isAuthenticated
دریافت کننده از فروشگاه اگر isAuthenticated
false را برمی گرداند سپس برنامه را به لاگین هدایت می کنم page.
با کدگذاری مؤلفه Login و تعیین مسیر آن، میتوانم از طریق یک مؤلفه پیوند مسیریاب در مؤلفه «هدر» به آن دسترسی داشته باشم. components/Header.vue
. من به صورت مشروط یا پیوند به را نشان می دهم NewSurvey
جزء یا Login
جزء با استفاده از isAuthenticated
ذخیره گیرنده یک بار دیگر در یک ویژگی محاسبه شده در Header
جزء ارجاع شده توسط v-if
بخشنامه هایی از این قبیل:
<!-- components/Header.vue -->
<template>
<nav class="navbar is-light" role="navigation" aria-label="main navigation">
<div class="navbar-menu">
<div class="navbar-start">
<router-link to="/" class="navbar-item">
Home
</router-link>
<router-link v-if="isAuthenticated" to="/surveys" class="navbar-item">
Create Survey
</router-link>
<router-link v-if="!isAuthenticated" to="/login" class="navbar-item">
Login / Register
</router-link>
</div>
</div>
</nav>
</template>
<script>
export default {
computed: {
isAuthenticated () {
return this.$store.getters.isAuthenticated
}
}
}
</script>
<style>
</style>
عالی! حالا بالاخره میتوانم سرورهای توسعهدهنده را برای برنامه Flask و برنامه Vue.js فعال کنم و آزمایش کنم تا ببینم میتوانم یک کاربر ثبت نام کرده و لاگین کنم.
ابتدا سرور توسعه دهنده Flask را راه اندازی می کنم.
(venv) $ python appserver.py
سپس سرور توسعه دهنده webpack برنامه Vue.js را کامپایل و ارائه می کند.
$ npm run dev
در مرورگر من بازدید می کنم http://localhost:8080
(یا هر پورتی که سرور توسعه دهنده وب پک نشان می دهد) و مطمئن شوید که نوار ناوبری اکنون “ورود / ثبت نام” را در محل “ایجاد نظرسنجی” مانند شکل زیر نمایش می دهد:
بعد کلیک می کنم روی پیوند “ورود / ثبت نام” را وارد کنید و ورودی های ایمیل و رمز عبور را پر کنید، سپس روی ثبت کلیک کنید تا مطمئن شوید مطابق انتظار عمل می کند و من به خانه هدایت می شوم. page و لینک “ایجاد نظرسنجی” را ببینید که به جای پیوند “ورود / ثبت نام” که قبل از ثبت نام وجود داشت، نمایش داده شده است.
خوب، کار من تا حد زیادی انجام شده است. تنها کاری که باید انجام دهید این است که کمی رسیدگی به خطا را به آن اضافه کنید submitSurvey(...)
روش Vue.js از NewSurvey
مؤلفه ای برای رسیدگی به رویدادی که در آن توکن اتفاقی منقضی می شود در حالی که کاربر در حال ایجاد یک نظرسنجی جدید مانند این است:
<script>
import NewQuestion from '@/components/NewQuestion'
export default {
components: { NewQuestion },
data () {
return {
step: 'name',
name: '',
questions: ()
}
},
methods: {
//
// omitting other methods
//
submitSurvey () {
this.$store.dispatch('submitNewSurvey', {
name: this.name,
questions: this.questions
})
.then(() => this.$router.push('/'))
.catch((error) => {
console.log('Error creating survey', error)
this.$router.push('/')
})
}
}
}
</script>
منابع
آیا می خواهید در مورد چارچوب های مختلف استفاده شده در این مقاله بیشتر بدانید؟ برخی از منابع زیر را برای بررسی عمیق تر در استفاده از Vue.js یا ساختن APIهای بکاند در پایتون بررسی کنید:
نتیجه
در این پست روش پیاده سازی احراز هویت JWT را در برنامه نظرسنجی با استفاده از Vue.js و Flask نشان دادم. JWT یک روش محبوب و قوی برای ارائه احراز هویت در برنامه های SPA است، و امیدوارم پس از خواندن این پست احساس راحتی کنید که از این فناوری ها برای ایمن سازی برنامه های خود استفاده کنید. با این حال، توصیه می کنم برای درک عمیق تر از چگونگی و چرایی کار JWT، از مقاله rasanegar اسکات دیدن کنید.
مثل همیشه، ممنون که خواندید و از نظر دادن یا انتقاد در زیر خجالت نکشید.
(برچسبها به ترجمه)# python
منتشر شده در 1403-01-26 19:42:06