/ / Cách triển khai xác thực người dùng trong Flask bằng JWT

Cách triển khai xác thực người dùng trong Flask bằng JWT

Xác thực bị hỏng tiếp tục là một lỗ hổng dai dẳng trong các ứng dụng web hiện đại—nó vẫn được xếp hạng cao trong 10 rủi ro bảo mật API hàng đầu của OWASP.




Ảnh hưởng của lỗ hổng này có thể nghiêm trọng. Họ có thể cấp quyền truy cập trái phép vào dữ liệu nhạy cảm và xâm phạm tính toàn vẹn của hệ thống. Để đảm bảo hiệu quả quyền truy cập an toàn vào các ứng dụng và tài nguyên của chúng, điều quan trọng là bạn phải sử dụng các cơ chế xác thực mạnh mẽ.

Tìm hiểu cách bạn có thể triển khai xác thực người dùng trong Flask bằng JSON Web Tokens (JWT), một phương pháp dựa trên mã thông báo phổ biến và hiệu quả.



Xác thực dựa trên mã thông báo bằng mã thông báo web JSON

Xác thực dựa trên mã thông báo sử dụng một chuỗi ký tự được mã hóa để xác thực và cho phép truy cập vào hệ thống hoặc tài nguyên. Bạn có thể triển khai loại xác thực này bằng nhiều phương pháp khác nhau, bao gồm mã thông báo phiên, khóa API và Mã thông báo web JSON.

Đặc biệt, JWT cung cấp một cách tiếp cận an toàn và nhỏ gọn để truyền thông tin xác thực của người dùng cần thiết giữa các ứng dụng phía máy khách và máy chủ.

Một ví dụ về mã thông báo JWT được mã hóa ở bên trái và phiên bản đã giải mã của mã thông báo hiển thị các thành phần riêng lẻ ở bên phải.

JWT bao gồm ba thành phần chính: tiêu đề, tải trọng và chữ ký. Tiêu đề chứa siêu dữ liệu về mã thông báo, bao gồm thuật toán băm được sử dụng để mã hóa mã thông báo.

Tải trọng chứa thông tin xác thực của người dùng thực tế, chẳng hạn như ID người dùng và quyền. Cuối cùng, chữ ký đảm bảo tính hợp lệ của mã thông báo bằng cách xác minh nội dung của nó bằng khóa bí mật.

Sử dụng JWT, bạn có thể xác thực người dùng và lưu trữ tất cả dữ liệu phiên trong chính mã thông báo.

Thiết lập Dự án Flask và Cơ sở dữ liệu MongoDB

Để bắt đầu, hãy tạo một thư mục dự án mới bằng thiết bị đầu cuối:

 mkdir flask-project
cd flask-project

Tiếp theo, cài đặt virtualenvđể tạo môi trường phát triển ảo cục bộ cho dự án Flask của bạn.

 virtualenv venv 

Cuối cùng, kích hoạt môi trường ảo.

 # Unix or MacOS: 
source venv/bin/activate

# Windows:
.venvScriptsactivate

Cài đặt các gói cần thiết

Trong thư mục gốc của thư mục dự án của bạn, hãy tạo một thư mục mới yêu cầu.txt tệp và thêm các phụ thuộc này cho dự án:

 flask
pyjwt
python-dotenv
pymongo
bcrypt

Cuối cùng, chạy lệnh bên dưới để cài đặt các gói. Hãy chắc chắn rằng bạn có píp (trình quản lý gói) đã cài đặt; nếu không, hãy cài đặt nó trên hệ thống Windows, Mac hoặc Linux của bạn.

 pip install -r requirements.txt 

Tạo cơ sở dữ liệu MongoDB

Hãy tiếp tục và tạo một cơ sở dữ liệu MongoDB. Bạn có thể thiết lập cơ sở dữ liệu MongoDB cục bộ, hoặc tạo một cụm trên MongoDB Atlas, một dịch vụ MongoDB dựa trên đám mây.

Khi bạn đã tạo cơ sở dữ liệu, hãy sao chép URI kết nối, tạo một .env tệp trong thư mục gốc của dự án của bạn và thêm nó như sau:

 MONGO_URI="<insert-connection-uri>" 

Cuối cùng, cấu hình kết nối cơ sở dữ liệu từ ứng dụng Flask của bạn. Tạo một cái mới utils/db.py tệp trong thư mục gốc của dự án của bạn, với mã này:

 from pymongo import MongoClient

def connect_to_mongodb(mongo_uri):
    client = MongoClient(mongo_uri)
    db = client.get_database("users")
    return db

Hàm này thiết lập kết nối tới cơ sở dữ liệu MongoDB bằng URI kết nối được cung cấp. Sau đó nó tạo ra một cái mới người dùng bộ sưu tập nếu nó không tồn tại và trả về phiên bản cơ sở dữ liệu tương ứng.

Tạo máy chủ web Flask

Với cơ sở dữ liệu được định cấu hình, hãy tiếp tục và tạo một app.py tệp trong thư mục gốc của thư mục dự án và thêm đoạn mã sau để tạo một phiên bản của ứng dụng Flask.

 from flask import Flask
from routes.user_auth import register_routes
from utils.db import connect_to_mongodb
import os
from dotenv import load_dotenv

app = Flask(__name__)
load_dotenv()

mongo_uri = os.getenv('MONGO_URI')
db = connect_to_mongodb(mongo_uri)

register_routes(app, db)

if __name__ == '__main__':
    app.run(debug=True)

Tạo điểm cuối API xác thực

Để triển khai xác thực người dùng trong ứng dụng Flask của bạn, điều quan trọng là phải xác định các điểm cuối API cần thiết để xử lý các hoạt động liên quan đến xác thực.

Tuy nhiên, trước tiên, hãy xác định mô hình cho dữ liệu của người dùng. Để làm như vậy, tạo một mới mô hình/user_model.py tập tin trong thư mục gốc và thêm mã sau đây.

 from pymongo.collection import Collection
from bson.objectid import ObjectId

class User:
    def __init__(self, collection: Collection, username: str, password: str):
        self.collection = collection
        self.username = username
        self.password = password
    def save(self):
        user_data = {
            'username': self.username,
            'password': self.password
        }
        result = self.collection.insert_one(user_data)
        return str(result.inserted_id)

@staticmethod
    def find_by_id(collection: Collection, user_id: str):
        return collection.find_one({'_id': ObjectId(user_id)})

@staticmethod
    def find_by_username(collection: Collection, username: str):
        return collection.find_one({'username': username})

Đoạn mã trên chỉ định một Người dùng lớp đóng vai trò là mô hình dữ liệu và định nghĩa một số phương thức để tương tác với bộ sưu tập MongoDB nhằm thực hiện các thao tác liên quan đến người dùng.

  1. Các cứu phương thức lưu tài liệu người dùng mới với tên người dùng và mật khẩu được cung cấp vào bộ sưu tập MongoDB và trả về ID của tài liệu được chèn.
  2. Các find_by_idfind_by_username các phương thức truy xuất tài liệu người dùng từ bộ sưu tập dựa trên ID người dùng hoặc tên người dùng được cung cấp tương ứng.

Xác định các tuyến xác thực

  1. Hãy bắt đầu bằng cách xác định lộ trình đăng ký. Lộ trình này sẽ thêm dữ liệu người dùng mới vào bộ sưu tập người dùng MongoDB. Trong thư mục gốc, tạo mới tuyến/user_auth.py tệp và mã sau đây.
     import jwt
    from functools import wraps
    from flask import jsonify, request, make_response
    from models.user_model import User
    import bcrypt
    import os

    def register_routes(app, db):
        collection = db.users
        app.config['SECRET_KEY'] = os.urandom(24)

    @app.route('/api/register', methods=['POST'])
        def register():
            
            username = request.json.get('username')
            password = request.json.get('password')
            
            existing_user = User.find_by_username(collection, username)
            if existing_user:
                return jsonify({'message': 'Username already exists!'})
          
            hashed_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())
            new_user = User(collection, username, hashed_password.decode('utf-8'))
            user_id = new_user.save()

            return jsonify({'message': 'User registered successfully!', 'user_id': user_id})

  2. Triển khai chức năng đăng nhập, để xử lý quy trình xác thực và xác minh thông tin đăng nhập của người dùng. Trong phần lộ trình đăng ký, thêm đoạn mã sau.
      @app.route('/api/login', methods=['POST'])
        def login():
            username = request.json.get('username')
            password = request.json.get('password')
            user = User.find_by_username(collection, username)
            if user:
                if bcrypt.checkpw(password.encode('utf-8'), user['password'].encode('utf-8')):
                    token = jwt.encode({'user_id': str(user['_id'])}, app.config['SECRET_KEY'], algorithm='HS256')
              
                    response = make_response(jsonify({'message': 'Login successful!'}))
                    response.set_cookie('token', token)
                    return response

            return jsonify({'message': 'Invalid username or password'})

    Điểm cuối đăng nhập thực hiện hai việc: nó xác minh thông tin đăng nhập của người dùng được cung cấp và sau khi xác thực thành công, nó sẽ tạo một JWT duy nhất cho người dùng đó. Nó đặt mã thông báo này làm cookie trong phản hồi, cùng với tải trọng JSON cho biết đăng nhập thành công. Nếu thông tin đăng nhập không hợp lệ, nó sẽ trả về phản hồi JSON để cho biết như vậy.

  3. Xác định chức năng trang trí xác minh Mã thông báo web JSON (JWT) được chuyển cùng với các yêu cầu API tiếp theo. Thêm mã bên dưới vào trong register_routes khối mã chức năng.
         def token_required(f):
    @wraps(f)
            def decorated(*args, **kwargs):
                token = request.cookies.get('token')

                if not token:
                    return jsonify({'message': 'Token is missing!'}), 401

                try:
                    data = jwt.decode(token, app.config['SECRET_KEY'], algorithms=['HS256'])
                    current_user = User.find_by_id(collection, data['user_id'])
                except jwt.ExpiredSignatureError:
                    return jsonify({'message': 'Token has expired!'}), 401
                except jwt.InvalidTokenError:
                    return jsonify({'message': 'Invalid token!'}), 401

                return f(current_user, *args, **kwargs)

            return decorated

    Chức năng trang trí này đảm bảo sự hiện diện của mã thông báo JWT hợp lệ trong các yêu cầu API tiếp theo. Nó kiểm tra xem mã thông báo có bị thiếu, hết hạn hay hợp lệ hay không và trả về phản hồi JSON thích hợp nếu có.

  4. Cuối cùng, tạo một tuyến đường được bảo vệ.
      @app.route('/api/users', methods=['GET'])
    @token_required
        def get_users(current_user):
            users = list(collection.find({}, {'_id': 0}))
            return jsonify(users)

Điểm cuối này xử lý logic để truy xuất dữ liệu người dùng từ cơ sở dữ liệu nhưng nó yêu cầu khách hàng gửi yêu cầu bao gồm mã thông báo hợp lệ để truy cập dữ liệu.

Cuối cùng, hãy chạy lệnh bên dưới để khởi động máy chủ phát triển.

 flask run 

Để kiểm tra đăng ký, đăng nhập và điểm cuối của người dùng được bảo vệ, bạn có thể sử dụng Postman hoặc bất kỳ ứng dụng API nào khác. Gửi yêu cầu đến http://localhost:5000/api/ và quan sát các phản hồi để xác minh chức năng của các điểm cuối API này.

Xác thực mã thông báo có phải là một biện pháp bảo mật hoàn hảo không?

Mã thông báo web JSON cung cấp một cách mạnh mẽ và hiệu quả để xác thực người dùng cho ứng dụng web của bạn. Tuy nhiên, điều quan trọng là phải hiểu rằng xác thực mã thông báo không phải là hoàn hảo; nó chỉ là một phần của một câu đố bảo mật lớn hơn.

Kết hợp xác thực mã thông báo với các phương pháp hay nhất về bảo mật khác. Hãy nhớ giám sát liên tục và áp dụng các biện pháp bảo mật nhất quán; bạn sẽ tăng cường đáng kể tính bảo mật tổng thể cho các ứng dụng Flask của mình.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *