از طریق منوی جستجو مطلب مورد نظر خود در وبلاگ را به سرعت پیدا کنید
نحوه اجرای احراز هویت دو مرحله ای با PyOTP و Google Authenticator در برنامه Flask خود
سرفصلهای مطلب
احراز هویت دو مرحله ای یا 2FA مانند داشتن یک قفل اضافی روی درب حساب های آنلاین شما است. 2FA به جای استفاده از رمز عبور، لایه دیگری از امنیت را اضافه می کند. کمی شبیه این است که برای باز کردن یک خزانه به یک کلید و یک کد خاص نیاز دارید.
به آن به عنوان یک سپر برای حساب های خود فکر کنید. گذرواژهها را گاهی میتوان حدس زد یا دزدید، اما با 2FA، حتی اگر شخصی رمز عبور شما را دریافت کند، همچنان به آن کد یا دستگاه اضافی برای ورود نیاز دارد. این یک مرحله اضافی است که نفوذ حسابهای شما را برای هکرها سختتر میکند.
بنابراین، بیایید نحوه تنظیم این لایه حفاظتی اضافی را با استفاده از PyOTP و Google Authenticator در برنامه Flask خود بررسی کنیم.
فهرست مطالب:
- مروری بر PyOTP و Google Authenticator
- گردش کار احراز هویت دو مرحله ای در برنامه ما
- پیش نیازها
- ابزارهای خود را آماده کنید
- نحوه راه اندازی پروژه
- نحوه ایجاد طرحهای اولیه برای حسابها و هسته
- نحوه ایجاد یک مدل کاربری
- نحوه اضافه کردن Flask-Login
- نحوه اضافه کردن قالب ها و فایل های استاتیک
- نحوه ایجاد صفحه اصلی
- نحوه اجرای ثبت نام کاربر
- نحوه پیاده سازی ورود کاربر
- نحوه خروج کاربران
- نحوه اضافه کردن صفحه Setup 2FA
- چگونه یک صفحه تأیید 2FA اضافه کنیم
- نحوه اجرای برنامه تکمیل شده برای اولین بار
- بسته بندی
مروری بر PyOTP و Google Authenticator
PyOTP یک کتابخانه پایتون است که برای تولید گذرواژه های یک بار مصرف مبتنی بر زمان (TOTP) و گذرواژه های یک بار مصرف مبتنی بر HMAC (HOTP) فوق العاده مفید است. نقش اصلی آن حول محور ایجاد این کدهای منحصر به فرد و حساس به زمان است که یک لایه امنیتی اضافی را به حساب های کاربری اضافه می کند.
با ادغام PyOTP در برنامه Flask خود، می توانید به راحتی احراز هویت دو مرحله ای (2FA) را با تولید و تأیید این OTP ها پیاده سازی کنید.
اگر با PyOTP تازه کار هستید یا می خواهید در مورد عملکردهای آن اطلاعات تازه ای کسب کنید، توصیه می کنم راهنمای قبلی من در مورد PyOTP را مرور کنید. این درک مفید خواهد بود زیرا ما به یکپارچه سازی PyOTP در برنامه Flask شما برای احراز هویت دو مرحله ای (2FA) وارد می شویم.
از سوی دیگر، Google Authenticator به عنوان یکی از پرکاربردترین برنامههای تولیدکننده OTP در دسترس است. این به عنوان یک پلت فرم امن برای تولید OTP های مبتنی بر زمان، سازگار با سرویس ها و برنامه های مختلف که از 2FA پشتیبانی می کنند، عمل می کند. کاربران می توانند به راحتی Google Authenticator را در دستگاه های خود راه اندازی کنند تا این کدهای حساس به زمان را تولید کنند و سطح امنیتی بیشتری را به حساب های خود اضافه کنند.
گردش کار احراز هویت دو مرحله ای در برنامه ما
در اینجا خلاصه ای از جریان احراز هویت دو مرحله ای در برنامه ما آمده است:
- ثبت نام با 2FA Setup: هنگامی که کاربران در وب سایت ما ثبت نام می کنند، از آنها خواسته می شود که یک لایه امنیتی اضافی را تنظیم کنند – 2FA. این شامل اسکن یک کد QR با استفاده از یک برنامه احراز هویت، مانند Google Authenticator، برای پیوند دادن ایمن حساب آنها است.
- شروع ورود: هنگامی که کاربران برای ورود به سیستم باز می گردند، با وارد کردن ایمیل/نام کاربری و رمز عبور معمول خود برای دسترسی به حساب خود شروع می کنند.
- بررسی امنیتی اضافی: قبل از اعطای دسترسی، وبسایت ما با یک مانع دیگر روبرو میشود: کاربران باید یک OTP (گذرواژه یکبار مصرف) نمایش داده شده در برنامه احراز هویت خود ارائه دهند. این تضمین می کند که آنها نه تنها رمز عبور را وارد می کنند، بلکه هویت خود را با یک کد منحصر به فرد و حساس به زمان تأیید می کنند.
- اعتبار سنجی و مجوز: کاربر OTP دریافتی را به پلتفرم ما وارد می کند. سپس سیستم این OTP را در برابر کد مورد انتظار دوبار بررسی می کند و اطلاعات را تأیید می کند. اگر OTP مطابقت داشته باشد، مانند دست دادن مخفی است که به کاربر اجازه دسترسی به حساب خود را می دهد.
این رفت و آمد یکپارچه بین گذرواژهها، برنامههای احراز هویت و کدهای منحصربهفرد تضمین میکند که فقط صاحب حساب واقعی میتواند به محتوای ارزشمند پشت درهای دیجیتال وبسایت شما دسترسی داشته باشد.
اگر شما نیز از یادگیری بصری لذت می برید، در اینجا یک ویدیوی فانتزی وجود دارد که نشان می دهد برنامه چگونه کار خود را انجام می دهد.
حالا بریم سراغ کد نویسی!
پیش نیازها
قبل از شروع آموزش، مطمئن شوید که شرایط زیر را برآورده کرده اید:
- دانش کار پایتون
- پایتون 3.8+ روی سیستم شما نصب شده است
- دانش اولیه Flask و Flask Blueprints
- آشنایی با احراز هویت اولیه در Flask (اختیاری)
ابزارهای خود را آماده کنید
برای این پروژه به چند کتابخانه خارجی نیاز دارید. بیایید در مورد آنها بیشتر بدانیم و آنها را یکی یکی نصب کنیم.
اما قبل از نصب آنها، اجازه دهید یک محیط مجازی ایجاد کرده و آن را فعال کنیم.
ابتدا با ایجاد دایرکتوری پروژه و پیمایش به آن به صورت زیر شروع کنید:
mkdir flask-two-factor-auth
ccd flask-two-factor-auth
ما قصد داریم با استفاده از یک محیط مجازی ایجاد کنیم venv
. پایتون اکنون با یک نسخه از پیش نصب شده عرضه می شود venv
کتابخانه بنابراین، برای ایجاد یک محیط مجازی، می توانید از دستور زیر استفاده کنید:
python -m venv env
دستور بالا یک محیط مجازی به نام env ایجاد می کند. حال باید محیط را با استفاده از این دستور فعال کنیم:
source env/Scripts/activate
برای بررسی اینکه آیا محیط فعال شده است یا خیر، می توانید ببینید (env)
در ترمینال شما اکنون می توانیم کتابخانه ها را نصب کنیم.
- Flask یک میکروفریمورک ساده و آسان برای پایتون است که به شما کمک می کند تا برنامه های وب مقیاس پذیر و ایمن بسازید.
- Flask-Login مدیریت جلسات کاربر را برای Flask فراهم می کند. وظایف متداول ورود به سیستم، خروج از سیستم، و به خاطر سپردن جلسات کاربران شما در مدت زمان طولانی را انجام می دهد.
- Flask-Bcrypt یک افزونه Flask است که ابزارهای هش bcrypt را برای برنامه شما فراهم می کند.
- Flask-WTF یک ادغام ساده از Flask و WTForms است که به شما کمک می کند فرم هایی را در Flask ایجاد کنید.
- Flask-Migrate یک برنامه افزودنی است که انتقال پایگاه داده SQLAlchemy را برای برنامه های Flask با استفاده از Alembic انجام می دهد. عملیات پایگاه داده از طریق رابط خط فرمان Flask در دسترس است.
- Flask-SQLAlchemy یک افزونه برای Flask است که پشتیبانی از SQLAlchemy را به برنامه شما اضافه می کند. این به شما کمک می کند تا کارها را با استفاده از SQLAlchemy با Flask با ارائه پیش فرض های مفید و کمک های اضافی که انجام کارهای معمول را آسان تر می کند، ساده کنید.
- PyOTP به شما کمک می کند OTP ها را با استفاده از الگوریتم های OTP مبتنی بر زمان (TOTP) و OTP مبتنی بر HMAC (HOTP) بدون زحمت تولید کنید.
- QRCode به شما کمک می کند تا کدهای QR را در پایتون ایجاد کنید
- Python Decouple به شما کمک می کند از متغیرهای محیطی در پروژه پایتون خود استفاده کنید.
برای نصب کتابخانه های فوق به صورت یکجا، دستور زیر را اجرا کنید:
pip install Flask Flask-Login Flask-Bcrypt Flask-WTF FLask-Migrate Flask-SQLAlchemy pyotp qrcode python-decouple
نحوه راه اندازی پروژه
بیایید با ایجاد یک شروع کنیم src
فهرست راهنما:
mkdir src
فایل اول خواهد بود __init__.py
فایل برای پروژه:
from decouple import config
from flask import Flask
from flask_bcrypt import Bcrypt
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config.from_object(config("APP_SETTINGS"))
bcrypt = Bcrypt(app)
db = SQLAlchemy(app)
migrate = Migrate(app, db)
# Registering blueprints
from src.accounts.views import accounts_bp
from src.core.views import core_bp
app.register_blueprint(accounts_bp)
app.register_blueprint(core_bp)
در اسکریپت بالا یک اپلیکیشن Flask به نام ایجاد کردیم app
. ما استفاده می کنیم __name__
آرگومان برای نشان دادن ماژول یا بسته برنامه به طوری که Flask بداند فایل های دیگری مانند الگوها را کجا پیدا کند. ما همچنین پیکربندی برنامه را با استفاده از یک متغیر محیطی به نام تنظیم کردیم APP_SETTINGS
. بعدا صادرش میکنیم
برای استفاده از Flask-Bcrypt، Flask-SQLAlchemy و Flask-Migrate در برنامه خود، فقط باید اشیاء را ایجاد کنیم. Bcrypt
، SQLAlchemy
و Migrate
کلاس ها از flask_bcrypt
، flask_sqlalchemy
و flask_migrate
کتابخانه ها به ترتیب
ما همچنین طرحهایی را به نام ثبت کردهایم accounts_bp
و core_bp
در برنامه. بعداً در آموزش آنها را تعریف خواهیم کرد.
در دایرکتوری اصلی پروژه (یعنی خارج از src
دایرکتوری)، فایلی به نام ایجاد کنید config.py
. ما تنظیمات پروژه را در این فایل ذخیره می کنیم. در داخل فایل، محتوای زیر را اضافه کنید:
from decouple import config
DATABASE_URI = config("DATABASE_URL")
if DATABASE_URI.startswith("postgres://"):
DATABASE_URI = DATABASE_URI.replace("postgres://", "postgresql://", 1)
class Config(object):
DEBUG = False
TESTING = False
CSRF_ENABLED = True
SECRET_KEY = config("SECRET_KEY", default="guess-me")
SQLALCHEMY_DATABASE_URI = DATABASE_URI
SQLALCHEMY_TRACK_MODIFICATIONS = False
BCRYPT_LOG_ROUNDS = 13
WTF_CSRF_ENABLED = True
DEBUG_TB_ENABLED = False
DEBUG_TB_INTERCEPT_REDIRECTS = False
APP_NAME = config("APP_NAME")
class DevelopmentConfig(Config):
DEVELOPMENT = True
DEBUG = True
WTF_CSRF_ENABLED = False
DEBUG_TB_ENABLED = True
class TestingConfig(Config):
TESTING = True
DEBUG = True
SQLALCHEMY_DATABASE_URI = "sqlite:///testdb.sqlite"
BCRYPT_LOG_ROUNDS = 1
WTF_CSRF_ENABLED = False
class ProductionConfig(Config):
DEBUG = False
DEBUG_TB_ENABLED = False
در اسکریپت فوق، a را ایجاد کرده ایم Config
کلاس و مشخصه های مختلفی را درون آن تعریف کرد. همچنین، ما کلاس های مختلف کودک (بر اساس مراحل مختلف رشد) ایجاد کرده ایم که به ارث می برند Config
کلاس
توجه داشته باشید که ما از چند متغیر محیطی مانند SECRET_KEY
، DATABASE_URL
، و APP_NAME
. یک فایل به نام ایجاد کنید .env
در دایرکتوری ریشه و محتوای زیر را در آنجا اضافه کنید:
export SECRET_KEY=fdkjshfhjsdfdskfdsfdcbsjdkfdsdf
export DEBUG=True
export APP_SETTINGS=config.DevelopmentConfig
export DATABASE_URL=sqlite:///db.sqlite
export FLASK_APP=src
export FLASK_DEBUG=1
export APP_NAME="Flask User Authentication App"
جدای از SECRET_KEY
، DATABASE_URL
و APP_NAME
، ما نیز صادر کرده ایم APP_SETTINGS
، DEBUG
، FLASK_APP
، و FLASK_DEBUG
.
این APP_SETTINGS
به یکی از کلاس هایی که در آن ایجاد کردیم اشاره دارد config.py
فایل. ما آن را در مرحله فعلی پروژه قرار دادیم.
ارزش FLASK_APP
نام بسته ای است که ما ایجاد کرده ایم. از آنجایی که برنامه در مرحله توسعه است، می توانید مقادیر آن را تنظیم کنید DEBUG
و FLASK_DEBUG
به True
و 1
، به ترتیب.
دستور زیر را اجرا کنید تا تمام متغیرهای محیطی را از آن صادر کنید .env
فایل:
source .env
در مرحله بعد، ما یک برنامه CLI از برنامه ایجاد می کنیم تا بعداً بتوانیم در صورت نیاز دستورات سفارشی را اضافه کنیم.
ایجاد یک manage.py
فایل را در دایرکتوری اصلی برنامه قرار دهید و کد زیر را اضافه کنید:
from flask.cli import FlaskGroup
from src import app
cli = FlaskGroup(app)
if __name__ == "__main__":
cli()
اکنون، برنامه اصلی شما آماده است. با استفاده از دستور زیر می توانید آن را اجرا کنید:
python manage.py run
ساختار فایل شما از هم اکنون باید به شکل زیر باشد:
flask-two-factor-auth/
├── src/
│ └── __init__.py
├── .env
├── config.py
└── manage.py
نحوه ایجاد طرحهای اولیه برای حسابها و هسته
همانطور که قبلا ذکر شد، شما از مفاهیم طرح های اولیه در پروژه استفاده خواهید کرد. بیایید دو طرح ایجاد کنیم – accounts_bp
و core_bp
– در این بخش.
ابتدا یک دایرکتوری به نام ایجاد کنید accounts
مثل این:
mkdir accounts
cd accounts
بعد، یک خالی اضافه کنید __init__.py
فایل را برای مخفی کردن آن در یک بسته پایتون قرار دهید. حال، یک را ایجاد کنید views.py
فایل داخل بسته را که در آن تمام مسیرهای مربوط به احراز هویت کاربر را ذخیره خواهید کرد.
touch __init__.py views.py
کد زیر را داخل آن اضافه کنید views.py
فایل:
from flask import Blueprint
accounts_bp = Blueprint("accounts", __name__)
در اسکریپت بالا یک طرح اولیه به نام ایجاد کرده اید accounts_bp
برای accounts
بسته بندی
به طور مشابه، شما می توانید یک ایجاد کنید core
را در پوشه ریشه بسته بندی کنید و a را اضافه کنید views.py
فایل.
mkdir core
cd core
touch __init__.py views.py
حالا کد زیر را داخل آن اضافه کنید views.py
فایل:
from flask import Blueprint
core_bp = Blueprint("core", __name__)
توجه: اگر با Flask Blueprints تازه کار هستید، حتماً این آموزش را دنبال کنید تا در مورد نحوه عملکرد آن بیشتر بدانید.
اکنون، ساختار فایل شما باید شبیه آنچه در زیر می بینید باشد:
flask-two-factor-auth/
├── src/
│ ├── accounts/
│ │ ├── __init__.py
│ │ └── views.py
│ ├── core/
│ │ ├── __init__.py
│ │ └── views.py
│ └── __init__.py
├── .env
├── config.py
└── manage.py
نحوه ایجاد یک مدل کاربری
بیایید a ایجاد کنیم models.py
فایل داخل accounts
بسته بندی
touch src/accounts/models.py
درون models.py
فایل، کد زیر را اضافه کنید:
from datetime import datetime
import pyotp
from flask_login import UserMixin
from src import bcrypt, db
from config import Config
class User(db.Model):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String, unique=True, nullable=False)
password = db.Column(db.String, nullable=False)
created_at = db.Column(db.DateTime, nullable=False)
is_two_factor_authentication_enabled = db.Column(
db.Boolean, nullable=False, default=False)
secret_token = db.Column(db.String, unique=True)
def __init__(self, username, password):
self.username = username
self.password = bcrypt.generate_password_hash(password)
self.created_at = datetime.now()
self.secret_token = pyotp.random_base32()
def get_authentication_setup_uri(self):
return pyotp.totp.TOTP(self.secret_token).provisioning_uri(
name=self.username, issuer_name=Config.APP_NAME)
def is_otp_valid(self, user_otp):
totp = pyotp.parse_uri(self.get_authentication_setup_uri())
return totp.verify(user_otp)
def __repr__(self):
return f"<user {self.username}>"
در کد بالا یک را ایجاد کردید User
مدل با ارث بردن db.Model
کلاس این User
مدل از فیلدهای زیر تشکیل شده است:
id
: کلید اصلی را برایusers
جدولusername
: نام کاربری کاربر را ذخیره می کندpassword
: رمز عبور هش شده کاربر را ذخیره می کندcreated_at
: زمان ایجاد کاربر را ذخیره می کندis_two_factor_authentication_enabled
: پرچم بولین که ذخیره می کند آیا کاربر احراز هویت دو مرحله ای را فعال کرده است یا خیر. مقدار پیش فرض استFalse
.secret_token
: یک توکن منحصر به فرد تولید شده برای هر کاربر را ذخیره می کند که برای اجرای احراز هویت دو مرحله ای ضروری است.
سازنده مقدار را مقداردهی اولیه می کند User
اعتراض به مصداق با قبول username
و password
مولفه های. با استفاده از رمز عبور ارائه شده را هش می کند bcrypt.generate_password_hash(password)
، مهر زمانی فعلی را به عنوان علامت ثبت می کند created_at
ارزش، و منحصر به فرد ایجاد می کند secret_token
استفاده کردن pyotp.random_base32()
برای راه اندازی 2FA
این get_authentication_setup_uri()
روش یک URI راهاندازی ایجاد میکند که توسط برنامههای احراز هویت مانند Google Authenticator استفاده میشود. یک URI حاوی نام کاربری کاربر و نام برنامه (Config.APP_NAME
) برای تنظیم احراز هویت دو مرحله ای ضروری است. فرمت اصلی URI این است:
otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example
که در آن alice@google.com نام کاربری کاربر و مثال نام برنامه است.
بعدی، is_otp_valid()
متد رمز عبور یکبار مصرف (OTP) وارد شده توسط کاربر در هنگام ورود را تأیید می کند. URI راهاندازی که قبلاً تولید شده بود را تجزیه میکند، اعتبار OTP ارائه شده را بررسی میکند (user_otp
) و برمی گردد True
اگر OTP مطابقت داشته باشد، اطمینان از احراز هویت ایمن.
در نهایت، __repr__
متد یک نمایش رشته ای از User
شی، نام کاربری مرتبط را هنگامی که نمونه ای از کلاس چاپ می شود یا به عنوان یک رشته نمایش داده می شود، نمایش می دهد.
نحوه اضافه کردن Flask-Login
مهمترین بخش Flask-Login این است LoginManager
کلاسی که به برنامه شما و Flask-Login اجازه می دهد با هم کار کنند.
در src/__init__.py
فایل، کد زیر را اضافه کنید:
from decouple import config
from flask import Flask
from flask_login import LoginManager # Add this line
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config.from_object(config("APP_SETTINGS"))
login_manager = LoginManager() # Add this line
login_manager.init_app(app) # Add this line
db = SQLAlchemy(app)
migrate = Migrate(app, db)
# Registering blueprints
from src.accounts.views import accounts_bp
from src.core.views import core_bp
app.register_blueprint(accounts_bp)
app.register_blueprint(core_bp)
در اسکریپت بالا، ما لاگین منیجر را در اپلیکیشن خود ایجاد و مقداردهی اولیه کردیم.
در مرحله بعد، ما باید یک را ارائه دهیم user_loader
پاسخ به تماس این فراخوانی برای بارگیری مجدد شی کاربر از شناسه کاربری ذخیره شده در جلسه استفاده می شود. باید شناسه یک کاربر را بگیرد و شی کاربر مربوطه را برگرداند.
from src.accounts.models import User
@login_manager.user_loader
def load_user(user_id):
return User.query.filter(User.id == int(user_id)).first()
این User
مدل باید ویژگی ها و روش های زیر را پیاده سازی کند:
is_authenticated
: اگر کاربر احراز هویت شده باشد، این ویژگی True را برمی گرداند.is_active
: اگر این یک کاربر فعال باشد (اکانت فعال شده باشد) این ویژگی True را برمی گرداند.is_anonymous
: اگر کاربر ناشناس باشد، این ویژگی True را برمیگرداند (کاربران واقعی False را برمیگردانند).get_id()
: این متد رشته ای را برمی گرداند که به طور منحصر به فرد این کاربر را شناسایی می کند و می تواند برای بارگیری کاربر ازuser_loader
پاسخ به تماس
اکنون، ما نیازی به اجرای صریح اینها نداریم. در عوض، Flask-Login یک را فراهم می کند UserMixin
کلاسی که شامل پیاده سازی های پیش فرض برای همه این ویژگی ها و متدها است. ما فقط باید آن را به روش زیر به ارث ببریم:
from datetime import datetime
from flask_login import UserMixin # Add this line
from src import bcrypt, db
class User(UserMixin, db.Model): # Change this line
....
همچنین میتوانیم فرآیند ورود پیشفرض را در قسمت سفارشیسازی کنیم src/__init__.py
فایل.
نام نمای ورود به سیستم را می توان به صورت تنظیم کرد LoginManager.login_view
. مقدار به نام تابعی اشاره دارد که فرآیند ورود را مدیریت می کند.
login_manager.login_view = "accounts.login"
برای سفارشی کردن دسته پیام، تنظیم کنید LoginManager.login_message_category
:
login_manager.login_message_category = "danger"
نحوه اضافه کردن قالب ها و فایل های استاتیک
بیایید یک فایل CSS به نام ایجاد کنیم styles.css
درون src/static
پوشه:
.error {
color: red;
margin-bottom: 5px;
text-align: center;
}
a {
text-decoration: none;
}
بیایید قالب های اولیه را نیز در داخل ایجاد کنیم src/templates
پوشه ایجاد یک _base.html
فایل و کد زیر را اضافه کنید:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Two Factor Authentication</title>
<!-- meta -->
<meta name="description" content="">
<meta name="author" content="">
<meta name="viewport" content="width=device-width,initial-scale=1">
<!-- styles -->
<!-- CSS only -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
<link rel="stylesheet" href="{{url_for('static', filename="styles.css")}}">
{% block css %}{% endblock %}
</head>
<body>
{% include "navigation.html" %}
<div class="container">
<br>
<!-- messages -->
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<div class="row">
<div class="col-md-4"></div>
<div class="col-md-4">
{% for category, message in messages %}
<div class="alert alert-{{ category }} alert-dismissible fade show" role="alert">
{{message}}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endfor %}
</div>
<div class="col-md-4"></div>
</div>
{% endif %}
{% endwith %}
<!-- child template -->
{% block content %}{% endblock %}
</div>
<!-- scripts -->
<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
<!-- JavaScript Bundle with Popper -->
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js" integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.min.js" integrity="sha384-BBtl+eGJRgqQAUMxJ7pMwbEyER4l1g+O15P+16Ep7Q9Q+zqX6gSbd85u4mG4QzX+" crossorigin="anonymous"></script>
{% block js %}{% endblock %}
</body>
</html>
این _base.html
فایل HTML والد است که توسط سایر قالب ها به ارث می رسد. ما پشتیبانی از Bootstrap 5 را در فایل فوق اضافه کرده ایم. ما همچنین از Flask Flashes برای نشان دادن هشدارهای Bootstrap در برنامه استفاده می کنیم.
بیایید یک را نیز ایجاد کنیم navigation.html
فایلی که شامل نوار ناوبری برنامه است:
<!-- Navigation -->
<nav class="navbar bg-dark navbar-expand-lg bg-body-tertiary p-3" data-bs-theme="dark">
<div class="container-fluid">
<a class="navbar-brand" href="https://www.freecodecamp.org/news/how-to-implement-two-factor-authentication-in-your-flask-app/{{ url_for("core.home') }}">Two-Factor Authentication App</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
{% if current_user.is_authenticated %}
<a href="https://www.freecodecamp.org/news/how-to-implement-two-factor-authentication-in-your-flask-app/{{ url_for("accounts.logout') }}"><button type="button" class="btn btn-danger me-2">Logout</button></a>
{% endif %}
</div>
</div>
</nav>
توجه داشته باشید که ما هنوز نماهای استفاده شده در بالا را ایجاد نکرده ایم.
نحوه ایجاد صفحه اصلی
در این بخش ابتدا یک تابع view برای صفحه اصلی در داخل ایجاد می کنیم core/views.py
فایل. کد زیر را در آنجا اضافه کنید:
from flask import Blueprint, render_template
from flask_login import login_required
core_bp = Blueprint("core", __name__)
@core_bp.route("/")
@login_required
def home():
return render_template("core/index.html")
توجه داشته باشید که ما از نقشه برای اضافه کردن مسیر استفاده کرده ایم. الف را نیز اضافه کردیم @login_required
میان افزار برای جلوگیری از دسترسی کاربران احراز هویت نشده.
بعد، بیایید یک را ایجاد کنیم index.html
فایل داخل templates/core
پوشه، و کد زیر را اضافه کنید:
{% extends "_base.html" %}
{% block content %}
<h1 class="text-center">Welcome {{current_user.username}}!</h1>
{% endblock %}
صفحه HTML فقط یک پیام خوش آمدگویی برای کاربران احراز هویت شده خواهد داشت.
ساختار فایل شما در حال حاضر باید به شکل زیر باشد:
flask-two-factor-auth/
├── src/
│ ├── accounts/
│ │ ├── __init__.py
│ │ └── views.py
│ ├── core/
│ │ ├── __init__.py
│ │ └── views.py
│ ├── static/
│ │ └── styles.css
│ ├── templates/
│ │ ├── core/
│ │ │ └── index.html
│ │ ├── _base.html
│ │ └── navigation.html
│ └── __init__.py
├── .env
├── config.py
└── manage.py
نحوه اجرای ثبت نام کاربر
اول از همه، ما یک فرم ثبت نام با استفاده از Flask-WTF ایجاد می کنیم. ایجاد یک forms.py
فایل داخل accounts
بسته بندی کنید و کد زیر را اضافه کنید:
from flask_wtf import FlaskForm
from wtforms import EmailField, PasswordField
from wtforms.validators import DataRequired, Email, EqualTo, Length
from src.accounts.models import User
class RegisterForm(FlaskForm):
username = StringField(
"Username", validators=[DataRequired(), Length(min=6, max=40)]
)
password = PasswordField(
"Password", validators=[DataRequired(), Length(min=6, max=25)]
)
confirm = PasswordField(
"Repeat password",
validators=[
DataRequired(),
EqualTo("password", message="Passwords must match."),
],
)
def validate(self, extra_validators):
initial_validation = super(RegisterForm, self).validate(extra_validators)
if not initial_validation:
return False
user = User.query.filter_by(username=self.username.data).first()
if user:
self.username.errors.append("Username already registered")
return False
if self.password.data != self.confirm.data:
self.password.errors.append("Passwords must match")
return False
return True
این RegisterForm
گسترش می دهد FlaskForm
کلاس و شامل سه فیلد – username
، password
، و confirm
. ما اعتبار سنجی های مختلفی مانند DataRequired
، Length
، Email
، و EqualTo
به زمینه های مربوطه
الف را هم تعریف کردیم validate()
روشی که هنگام ارسال فرم به طور خودکار فراخوانی می شود.
در داخل متد، ابتدا اعتبار سنجی اولیه ارائه شده توسط FlaskForm را انجام می دهیم. در صورت موفقیت آمیز بودن، ما اعتبارسنجی سفارشی خود را انجام می دهیم، مانند بررسی اینکه آیا کاربر قبلاً ثبت نام کرده است یا خیر، و گذرواژه را با رمز عبور تأیید شده مطابقت می دهیم. در صورت وجود هر گونه خطایی، پیام خطا را در فیلدهای مربوطه اضافه می کنیم.
حالا بیایید از این فرم در داخل فایل HTML استفاده کنیم. ایجاد کنید accounts
دایرکتوری داخل templates
پوشه و یک فایل جدید به نام اضافه کنید register.html
درون آن. کد زیر را اضافه کنید:
{% extends "_base.html" %}
{% block content %}
<div class="row">
<div class="col-md-4"></div>
<div class="col-md-4">
<main class="form-signin w-100 m-auto">
<form role="form" method="post" action="">
{{ form.csrf_token }}
<h1 class="h3 mb-3 fw-normal text-center">Please register</h1>
<div class="form-floating">
{{ form.username(placeholder="username", class="form-control mb-2") }}
{{ form.username.label }}
{% if form.username.errors %}
{% for error in form.username.errors %}
<div class="alert alert-danger" role="alert">
{{ error }}
</div>
{% endfor %}
{% endif %}
</div>
<div class="form-floating">
{{ form.password(placeholder="password", class="form-control mb-2") }}
{{ form.password.label }}
{% if form.password.errors %}
{% for error in form.password.errors %}
<div class="alert alert-danger" role="alert">
{{ error }}
</div>
{% endfor %}
{% endif %}
</div>
<div class="form-floating">
{{ form.confirm(placeholder="Confirm Password", class="form-control mb-2") }}
{{ form.confirm.label }}
{% if form.confirm.errors %}
{% for error in form.confirm.errors %}
<div class="alert alert-danger" role="alert">
{{ error }}
</div>
{% endfor %}
{% endif %}
</div>
<button class="w-100 btn btn-lg btn-primary" type="submit">Sign up</button>
<p class="text-center mt-3">Already registered? <a href="https://www.freecodecamp.org/news/how-to-implement-two-factor-authentication-in-your-flask-app/{{ url_for("accounts.login') }}">Login now</a></p>
</form>
</main>
</div>
<div class="col-md-4"></div>
</div>
{% endblock %}
در قالب Jinja بالا، از فرمی که ایجاد کردیم استفاده میکنیم و بررسیهای منطقی رسیدگی به خطاها را برای خطاهای اعتبارسنجی در هر فیلد اضافه میکنیم. کاربران می توانند با کلیک بر روی دکمه “ثبت نام” فرم را ارسال کنند و پیوندی در زیر فرم به کاربرانی که قبلا ثبت نام کرده اند اجازه می دهد تا برای احراز هویت به صفحه ورود به سیستم بروید.
بعد، بیایید از این فرم در views.py
برای ایجاد یک تابع برای رسیدگی به فرآیند ثبت نام.
from .forms import RegisterForm
from src.accounts.models import User
from src import db, bcrypt
from flask_login import current_user
from flask import Blueprint, flash, redirect, render_template, request, url_for
accounts_bp = Blueprint("accounts", __name__)
HOME_URL = "core.home"
SETUP_2FA_URL = "accounts.setup_two_factor_auth"
VERIFY_2FA_URL = "accounts.verify_two_factor_auth"
@accounts_bp.route("/register", methods=["GET", "POST"])
def register():
if current_user.is_authenticated:
if current_user.is_two_factor_authentication_enabled:
flash("You are already registered.", "info")
return redirect(url_for(HOME_URL))
else:
flash("You have not enabled 2-Factor Authentication. Please enable first to login.", "info")
return redirect(url_for(SETUP_2FA_URL))
form = RegisterForm(request.form)
if form.validate_on_submit():
try:
user = User(username=form.username.data, password=form.password.data)
db.session.add(user)
db.session.commit()
login_user(user)
flash("You are registered. You have to enable 2-Factor Authentication first to login.", "success")
return redirect(url_for(SETUP_2FA_URL))
except Exception:
db.session.rollback()
flash("Registration failed. Please try again.", "danger")
return render_template("accounts/register.html", form=form)
مسیر با بررسی اینکه آیا کاربر فعلی قبلاً احراز هویت شده است یا خیر آغاز می شود. اگر چنین است، بررسی می کند که آیا 2FA برای کاربر فعال است یا خیر. اگر 2FA قبلاً فعال باشد، پیامی به کاربر اطلاع میدهد که قبلاً ثبتنام کردهاند و او را به URL خانه هدایت میکند. با این حال، اگر کاربر احراز هویت شده باشد اما 2FA فعال نباشد، یک پیام فلش از کاربر میخواهد تا قبل از ورود به سیستم، ابتدا 2FA را فعال کرده و آنها را به URL تنظیم 2FA هدایت کند.
اگر کاربر احراز هویت نشده باشد یا هنوز 2FA را ثبت نکرده باشد، کد یک فرم ثبت نام را راه اندازی می کند و پس از ارسال، اعتبار داده های فرم را تأیید می کند. پس از تایید موفقیت آمیز فرم، یک فرم جدید ایجاد می کنیم User
با نام کاربری و رمز عبور ارائه شده شیء کنید و آن را در پایگاه داده ذخیره کنید.
پس از ثبت نام موفقیت آمیز کاربر، کاربر تازه ثبت نام شده وارد سیستم می شود. یک پیام موفقیت آمیز چشمک می زند که به کاربر از ثبت نام موفقیت آمیز اطلاع می دهد و از او می خواهد قبل از ورود به سیستم 2FA را فعال کند. متعاقباً، کاربر به URL راه اندازی 2FA هدایت می شود تا 2FA فعال شود.
نحوه پیاده سازی ورود کاربر
ابتدا بیایید یک فرم ورود به سیستم ایجاد کنیم accounts/forms.py
فایل:
class LoginForm(FlaskForm):
username = StringField("Username", validators=[DataRequired()])
password = PasswordField("Password", validators=[DataRequired()])
فرم مشابه فرم ثبت نام است اما فقط دو فیلد دارد – username
و password
.
حالا بیایید از این فرم در داخل فایل HTML جدید به نام استفاده کنیم login.html
ایجاد شده در داخل templates/accounts
فهرست راهنما. کد زیر را اضافه کنید:
{% extends "_base.html" %}
{% block content %}
<div class="row">
<div class="col-md-4"></div>
<div class="col-md-4">
<main class="form-signin w-100 m-auto">
<form role="form" method="post" action="">
{{ form.csrf_token }}
<h1 class="h3 mb-3 fw-normal text-center">Please sign in</h1>
<div class="form-floating">
{{ form.username(placeholder="username", class="form-control mb-2") }}
{{ form.username.label }}
{% if form.username.errors %}
{% for error in form.username.errors %}
<div class="alert alert-danger" role="alert">
{{ error }}
</div>
{% endfor %}
{% endif %}
</div>
<div class="form-floating">
{{ form.password(placeholder="password", class="form-control mb-2") }}
{{ form.password.label }}
{% if form.password.errors %}
{% for error in form.password.errors %}
<div class="alert alert-danger" role="alert">
{{ error }}
</div>
{% endfor %}
{% endif %}
</div>
<button class="w-100 btn btn-lg btn-primary" type="submit">Sign in</button>
<p class="text-center mt-3">New User? <a href="https://www.freecodecamp.org/news/how-to-implement-two-factor-authentication-in-your-flask-app/{{ url_for("accounts.register') }}">Register now</a></p>
</form>
</main>
</div>
<div class="col-md-4"></div>
</div>
{% endblock %}
فایل HTML بالا نیز شبیه به register.html
فایل اما تنها با دو فیلد برای نام کاربری و رمز عبور.
در مرحله بعد، بیایید یک تابع view ایجاد کنیم تا فرآیند ورود به سیستم را انجام دهد accounts/views.py
فایل:
from .forms import LoginForm, RegisterForm
@accounts_bp.route("/login", methods=["GET", "POST"])
def login():
if current_user.is_authenticated:
if current_user.is_two_factor_authentication_enabled:
flash("You are already logged in.", "info")
return redirect(url_for(HOME_URL))
else:
flash("You have not enabled 2-Factor Authentication. Please enable first to login.", "info")
return redirect(url_for(SETUP_2FA_URL))
form = LoginForm(request.form)
if form.validate_on_submit():
user = User.query.filter_by(username=form.username.data).first()
if user and bcrypt.check_password_hash(user.password, request.form["password"]):
login_user(user)
if not current_user.is_two_factor_authentication_enabled:
flash(
"You have not enabled 2-Factor Authentication. Please enable first to login.", "info")
return redirect(url_for(SETUP_2FA_URL))
return redirect(url_for(VERIFY_2FA_URL))
elif not user:
flash("You are not registered. Please register.", "danger")
else:
flash("Invalid username and/or password.", "danger")
return render_template("accounts/login.html", form=form)
مسیر با بررسی اینکه آیا کاربر فعلی قبلاً احراز هویت شده است شروع می شود. اگر کاربر احراز هویت شده باشد و 2FA فعال باشد، پیامی به کاربر اطلاع میدهد که قبلاً وارد شده است و او را به URL خانه هدایت میکند. اگر کاربر احراز هویت شده باشد اما 2FA فعال نباشد، یک پیام فلش از کاربر میخواهد تا قبل از ورود به سیستم، 2FA را فعال کند و او را به URL تنظیم 2FA هدایت کند.
اگر کاربر احراز هویت نشده باشد، کد یک فرم ورود را راهاندازی میکند و پس از ارسال، دادههای فرم را تأیید میکند. پس از تأیید موفقیت آمیز، از پایگاه داده درخواست می کند تا کاربری مطابق با نام کاربری ارائه شده را پیدا کند. اگر کاربر وجود داشته باشد و رمز عبور با رمز عبور هش شده ذخیره شده در پایگاه داده مطابقت داشته باشد، کاربر وارد سیستم می شود.
علاوه بر این، اگر 2FA برای کاربر فعلی پس از ورود موفقیت آمیز فعال نباشد، یک پیام فلش از کاربر می خواهد قبل از ادامه، 2FA را فعال کرده و آنها را به URL راه اندازی 2FA هدایت کند. اگر ورود موفقیت آمیز باشد و 2FA فعال باشد، کاربر به URL تأیید 2FA هدایت می شود.
اگر کاربر ثبت نام نکرده باشد، یک پیام فلش به او اطلاع می دهد که ثبت نام کند. در صورت عدم تطابق در نام کاربری یا رمز عبور ارائه شده، یک پیام فلش دیگر کاربر را از اعتبار نامعتبر مطلع می کند.
نحوه خروج کاربران
خروج کاربر یک فرآیند بسیار ساده است. شما فقط باید یک تابع view برای آن در داخل ایجاد کنید accounts/views.py
فایل:
from flask_login import login_required, login_user, logout_user
@accounts_bp.route("/logout")
@login_required
def logout():
logout_user()
flash("You were logged out.", "success")
return redirect(url_for("accounts.login"))
این Flask-Login
کتابخانه حاوی الف logout_user
روشی که کاربر را از جلسه حذف می کند. ما استفاده کردیم @login_required
دکوراتور به طوری که فقط کاربران تأیید شده بتوانند از سیستم خارج شوند.
نحوه اضافه کردن صفحه Setup 2FA
تا به حال، هر زمان که 2FA در حسابهایشان فعال نشده باشد، کاربران را به صفحه setup 2FA هدایت میکردیم، اما هنوز آن را اجرا نکردهایم. بیایید این کار را در این بخش انجام دهیم.
بیایید با مسیر صفحه شروع کنیم:
from src.utils import get_b64encoded_qr_image
@accounts_bp.route("/setup-2fa")
@login_required
def setup_two_factor_auth():
secret = current_user.secret_token
uri = current_user.get_authentication_setup_uri()
base64_qr_image = get_b64encoded_qr_image(uri)
return render_template("accounts/setup-2fa.html", secret=secret, qr_image=base64_qr_image)
مسیر، ایجاد شده در داخل accounts/views.py
، تضمین می کند که فقط کاربران تأیید شده می توانند با استفاده از آن به آن دسترسی داشته باشند @login_required
دکوراتور
با دسترسی به این مسیر، تابع، کاربر فعلی را بازیابی می کند secret_token
برای راه اندازی 2FA و ایجاد یک URI از طریق current_user.get_authentication_setup_uri()
برای پیکربندی یک برنامه احراز هویت مانند Google Authenticator.
همچنین استفاده می کند get_b64encoded_qr_image(uri)
برای به دست آوردن یک تصویر کد QR با کد Base64 که نشان دهنده این URI راه اندازی است. در ادامه به تعریف آن می پردازیم.
در نهایت، آن را رندر می کند setup-2fa.html
الگو، عبور از کاربر secret_token
و تصویر QR رمزگذاری شده با Base64 را به الگو برای اسکن کردن آن توسط کاربران.
بعد، a ایجاد کنید utils.py
فایل در src
دایرکتوری و کد زیر را برای تولید QR اضافه کنید:
from io import BytesIO
import qrcode
from base64 import b64encode
def get_b64encoded_qr_image(data):
print(data)
qr = qrcode.QRCode(version=1, box_size=10, border=5)
qr.add_data(data)
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white")
buffered = BytesIO()
img.save(buffered)
return b64encode(buffered.getvalue()).decode("utf-8")
یادت باشد qrcode
کتابخانه ای که در ابتدای آموزش نصب کردیم؟ این جایی است که ما از آن استفاده خواهیم کرد.
به محض دریافت data
به عنوان ورودی، نشان دهنده محتوایی است که باید در کد QR تعبیه شود، تابع یک شی QRCode را با استفاده از qrcode
کتابخانه داده های ارائه شده را به این نمونه کد QR اضافه می کند و کد QR را تولید می کند. سپس کد این کد QR را به یک نمایش تصویر تبدیل می کند.
با استفاده از یک شی BytesIO، این تصویر را در حافظه ذخیره می کند. این تابع به کدگذاری محتوای این بافر درون حافظه، که تصویر کد QR را نشان میدهد، در قالب Base64 کد میکند. در نهایت، این رشته رمزگذاری شده با Base64 را برمی گرداند و تصویر کد QR را محصور می کند و آماده انتقال یا نمایش در برنامه های مختلف است.
بعد، بیایید ایجاد کنیم setup-2fa.html
صفحه داخل templates/accounts
پوشه، و محتوای زیر را اضافه کنید:
{% extends "_base.html" %}
{% block content %}
<div class="row">
<div class="col-md-4"></div>
<div class="col-md-4">
<main class="form-signin w-100 m-auto">
<form role="form">
<h5>Instructions!</h5>
<ul>
<li>Download <a href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=en&gl=US" target="_blank">Google Authenticator</a> on your mobile.</li>
<li>Set up a new authenticator.</li>
<li>Once you have scanned the QR, please click <a href="https://www.freecodecamp.org/news/how-to-implement-two-factor-authentication-in-your-flask-app/{{ url_for("accounts.verify_two_factor_auth') }}">here.</li>
</ul>
<div class="text-center">
<img src="data:image/png;base64, {{ qr_image }}" alt="Secret Token" style="width:200px;height:200px"/>
</div>
<div class="form-group">
<label for="secret">Secret Token</label>
<input type="text" class="form-control" id="secret" value="{{ secret }}" readonly>
</div>
<div class="text-center mt-2">
<button type="button" class="btn btn-primary" onclick="copySecret()">
Copy Secret
</button>
</div>
<p class="mt-4 text-center">
Once you have scanned the QR, please click <a href="https://www.freecodecamp.org/news/how-to-implement-two-factor-authentication-in-your-flask-app/{{ url_for("accounts.verify_two_factor_auth') }}">here</a>.
</p>
</form>
</main>
</div>
<div class="col-md-4"></div>
</div>
{% endblock %}
{% block js %}
<script>
function copySecret() {
var copyText = document.getElementById("secret");
copyText.select();
copyText.setSelectionRange(0, 99999); /*For mobile devices*/
document.execCommand("copy");
alert("Successfully copied TOTP secret token!");
}
</script>
{% endblock %}
ما دستورالعمل هایی را در صفحه اضافه می کنیم تا کاربران آن را دنبال کنند. این دستورالعملها مراحل روشنی را برای کاربران فراهم میکند تا 2FA را فعال کنند: هدایت آنها برای دانلود برنامه Google Authenticator از طریق پیوند، هدایت فرآیند راهاندازی در برنامه، و ترغیب کاربران به کلیک کردن روی پیوند پس از اسکن کد QR نمایشدادهشده.
نمایش کد QR در فرآیند راه اندازی مرکزی است. الگو با استفاده از یک تصویر کد QR را جاسازی می کند <img>
تگ با منبع تنظیم شده روی یک رشته کدگذاری شده با Base64 ({{ qr_image }}
). این تصویر نشان دهنده کلید مخفی ضروری برای راه اندازی 2FA است.
ما همچنین کلید مخفی را در حالت فقط خواندنی نشان میدهیم و به کاربران امکان میدهد کلید را بدون تغییر آن مشاهده کنند. ما یک دکمه کپی اضافه کرده ایم تا کپی کردن کلید را برای کاربران آسانتر کنیم.
علاوه بر این، ما پیوندی به صفحه تأیید 2FA اضافه کردهایم که کاربران را راهنمایی میکند تا پس از اسکن کد QR، مراحل راهاندازی را ادامه دهند. ما این قابلیت را در بخش بعدی پیاده سازی خواهیم کرد.
صفحه شما در حال حاضر چگونه به نظر می رسد:
چگونه یک صفحه تأیید 2FA اضافه کنیم
در این بخش، تأیید 2FA را پیاده سازی می کنیم. برای شروع، ما به یک فرم OTP نیاز داریم که در آن کاربران می توانند OTP خود را وارد کنند. مطالب زیر را در قسمت اضافه کنید accounts/forms.py
فایل:
class TwoFactorForm(FlaskForm):
otp = StringField('Enter OTP', validators=[
InputRequired(), Length(min=6, max=6)])
این TwoFactorForm
شامل فقط یک فیلد (otp
) برای دریافت OTP از کاربران.
حالا بیایید از این فرم در قسمت استفاده کنیم verify-2fa.html
فایل داخل templates/accounts
پوشه:
{% extends "_base.html" %}
{% block content %}
<div class="row">
<div class="col-md-4"></div>
<div class="col-md-4">
<main class="form-signin w-100 m-auto">
<form role="form" method="post" action="">
{{ form.csrf_token }}
<h1 class="h3 mb-3 fw-normal text-center">Enter OTP</h1>
<div class="form-floating">
{{ form.otp(placeholder="OTP", class="form-control mb-2") }}
{{ form.otp.label }}
{% if form.otp.errors %}
{% for error in form.otp.errors %}
<div class="alert alert-danger" role="alert">
{{ error }}
</div>
{% endfor %}
{% endif %}
</div>
<button class="w-100 btn btn-lg btn-primary" type="submit">Verify</button>
</form>
</main>
</div>
<div class="col-md-4"></div>
</div>
{% endblock %}
قالب Jinja اساساً حاوی یک فرم با یک فیلد برای OTP و یک دکمه تأیید است.
اجازه دهید مسیری را ایجاد کنیم که ارسال این فرم را در داخل انجام می دهد accounts/views.py
فایل:
@accounts_bp.route("/verify-2fa", methods=["GET", "POST"])
@login_required
def verify_two_factor_auth():
form = TwoFactorForm(request.form)
if form.validate_on_submit():
if current_user.is_otp_valid(form.otp.data):
if current_user.is_two_factor_authentication_enabled:
flash("2FA verification successful. You are logged in!", "success")
return redirect(url_for(HOME_URL))
else:
try:
current_user.is_two_factor_authentication_enabled = True
db.session.commit()
flash("2FA setup successful. You are logged in!", "success")
return redirect(url_for(HOME_URL))
except Exception:
db.session.rollback()
flash("2FA setup failed. Please try again.", "danger")
return redirect(url_for(VERIFY_2FA_URL))
else:
flash("Invalid OTP. Please try again.", "danger")
return redirect(url_for(VERIFY_2FA_URL))
else:
if not current_user.is_two_factor_authentication_enabled:
flash(
"You have not enabled 2-Factor Authentication. Please enable it first.", "info")
return render_template("accounts/verify-2fa.html", form=form)
مسیر با مقداردهی اولیه یک فرم شروع می شود (TwoFactorForm
) برای تأیید 2FA با استفاده از داده های به دست آمده از درخواست. پس از ارسال فرم، کد با چندین بررسی مشروط برای تأیید اعتبار OTP وارد شده توسط کاربر ادامه می یابد.
هنگامی که فرم با موفقیت ارسال و تأیید شد، کد صحت OTP را با استفاده از current_user.is_otp_valid(form.otp.data)
، بررسی می کند که آیا OTP وارد شده برای کاربر فعلی معتبر است یا خیر. اگر OTP معتبر باشد، کد منطق زیر را اجرا می کند:
- اگر OTP ارائه شده معتبر باشد و 2FA از قبل برای کاربر فعال باشد، یک پیام موفقیت آمیز فلش می شود که تأیید موفقیت آمیز 2FA را نشان می دهد و کاربر به URL خانه هدایت می شود.
- اگر OTP معتبر باشد اما 2FA برای کاربر فعال نباشد، سعی می کند 2FA را برای آن کاربر فعال کند. پس از فعال سازی موفقیت آمیز، یک پیام موفقیت چشمک می زند و کاربر به URL خانه هدایت می شود.
علاوه بر این، اگر OTP وارد شده توسط کاربر نامعتبر باشد، کد یک پیغام خطا نشان می دهد که OTP نامعتبر است و کاربر را به URL تأیید 2FA هدایت می کند تا دوباره فرآیند تأیید را امتحان کند.
با این کار اجرای تمامی ویژگی ها را تکمیل کردیم! 🎉
نحوه اجرای برنامه تکمیل شده برای اولین بار
اکنون که برنامه ما آماده است، می توانید ابتدا پایگاه داده را مهاجرت کرده و سپس برنامه را اجرا کنید.
برای مقداردهی اولیه پایگاه داده (ایجاد یک مخزن مهاجرت)، از دستور استفاده کنید:
flask db init
برای انتقال تغییرات پایگاه داده، از دستور زیر استفاده کنید:
flask db migrate
برای اعمال مهاجرت ها از دستور زیر استفاده کنید:
flask db upgrade
از آنجایی که این اولین بار است که برنامه خود را اجرا می کنیم، باید تمام دستورات بالا را اجرا کنید. بعداً، هر زمان که تغییراتی در پایگاه داده ایجاد کنید، فقط باید دو دستور آخر را اجرا کنید.
پس از آن، می توانید برنامه خود را با استفاده از دستور اجرا کنید:
python manage.py run
از آنجایی که ما توسعه را کامل کرده ایم، ساختار فایل شما چگونه باید باشد:
flask-two-factor-auth/
├── migrations/
├── src/
│ ├── accounts/
│ │ ├── __init__.py
│ │ ├── forms.py
│ │ ├── models.py
│ │ └── views.py
│ ├── core/
│ │ ├── __init__.py
│ │ └── views.py
│ ├── static/
│ │ └── styles.css
│ ├── templates/
│ │ ├── accounts/
│ │ │ ├── login.html
│ │ │ ├── register.html
│ │ │ ├── setup-2fa.html
│ │ │ └── verify-2fa.html
│ │ ├── core/
│ │ │ └── index.html
│ │ ├── _base.html
│ │ └── navigation.html
│ ├── __init__.py
│ └── utils.py
├── .env
├── config.py
└── manage.py
بسته شدن
در این آموزش یاد گرفتید که چگونه با استفاده از PyOTP احراز هویت دو مرحله ای را در برنامه Flask خود تنظیم کنید.
در اینجا پیوند به مخزن GitHub است. هر زمان که گیر کردید به راحتی آن را بررسی کنید.
در اینجا چند آموزش دیگر در مورد احراز هویت، تأیید ایمیل و OTP نوشته ام که ممکن است از آنها لذت ببرید:
- نحوه تنظیم احراز هویت اولیه کاربر در یک برنامه Flask
- نحوه تنظیم تأیید ایمیل در یک برنامه Flask
- نحوه تولید OTP با استفاده از PyOTP در پایتون
ممنون که خواندید. امیدوارم این مقاله برای شما مفید بوده باشد. شما می توانید من را دنبال کنید توییتر.
منتشر شده در 1402-12-26 06:49:05