Flaskでつくる簡単ログインフォーム コピペで使えるPython

flask

今回はFlaskを使って簡単にログインフォームを作ってみたいと思います。
企業用のものではなく個人が複数のユーザーで利用する想定の簡単なウェブサイトのログインフォームを想定しています。あくまで私的利用の為で企業用等のメインページには使用しないでください。

使用方法

server.pyは使用されるwebサイトのメインコード本体です。「追加する一行」の部分を追加してください。
また、@login_requiredデコレータをログインを強制したいページに追加してください。

また、DBにユーザーを追加するときはserver.pyの途中のコードをコメントアウトして追加してください。

メインコード(server.py)

from flask import *
import os
import login_form

"""
################# 新規ユーザーを登録するときのみに実行########################
app = Flask(__name__)
login_form.add_new_user(app, "testuser", "password123")
###########################################################################
"""

app = Flask(__name__)
login_required, limiter= login_form.add_login_form(app)


@app.route("/")
@login_required
def index():
    # global task_manager
    return render_template("index.html")


if __name__ == "__main__":
    app.run("localhost", port=7776, debug=False)

ログインフォーム追加用(login_form.py)

from flask import *
import os
from flask import Flask, render_template, redirect, url_for, flash, session
from flask_sqlalchemy import SQLAlchemy
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Length
from werkzeug.security import generate_password_hash, check_password_hash
from datetime import timedelta
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
import os
from functools import wraps
from flask_cors import CORS
from flask import Flask, render_template, redirect, url_for, request, current_app
from flask_socketio import SocketIO, emit, join_room, leave_room, rooms
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address


def __create_db(flaslk_app):
    flaslk_app.config['SECRET_KEY'] = 'nnkkkoonniikkoo_sequrity555'  # 安全なキーに変更してください
    # 現在のスクリプトがあるディレクトリの絶対パスを取得
    current_directory = os.path.abspath(os.path.dirname(__file__))
    db_absolute_path = os.path.join(current_directory,  'site.db')
    flaslk_app.config['SQLALCHEMY_DATABASE_URI'] = ('sqlite:///' + db_absolute_path).replace("\\", "/")
    print(flaslk_app.config['SQLALCHEMY_DATABASE_URI'])
    flaslk_app.config['REMEMBER_COOKIE_DURATION'] = timedelta(days=30)
    db = SQLAlchemy(flaslk_app)

    class User(UserMixin, db.Model):
        __tablename__ = 'users'
        id = db.Column(db.Integer, primary_key=True)
        username = db.Column(db.String(64), unique=True, nullable=False)
        password_hash = db.Column(db.String(128), nullable=False)

        def set_password(self, password):
            self.password_hash = generate_password_hash(password)

        def check_password(self, password):
            return check_password_hash(self.password_hash, password)

        @classmethod
        def create_new_user(cls, username, password):
            """
            新規ユーザーを登録するクラスメソッド。

            :param username: 新規ユーザーのユーザー名
            :param password: 新規ユーザーのパスワード
            :return: Userオブジェクト(登録成功時)またはNone(ユーザー名が既に存在する場合)
            """
            # 既に同じユーザー名のユーザーが存在するかチェック
            existing_user = cls.query.filter_by(username=username).first()
            if existing_user is not None:
                return None  # 既に存在する場合はNoneを返して登録しない

            # 新規ユーザーの作成
            new_user = cls(username=username)
            new_user.set_password(password)  # パスワードのハッシュ化
            db.session.add(new_user)
            db.session.commit()
            return new_user  # 新規ユーザーオブジェクトを返す

    with flaslk_app.app_context():
        db.create_all()  # データベースの初期化

    return User


def add_new_user(flaslk_app, id, password):
    User = __create_db(flaslk_app)
    with flaslk_app.app_context():
        User.create_new_user(id, password)


def add_login_form(flaslk_app):
    User = __create_db(flaslk_app)

    # ログインフォームの定義
    class LoginForm(FlaskForm):
        username = StringField('Username', validators=[DataRequired()])
        password = PasswordField('Password', validators=[DataRequired()])
        submit = SubmitField('Login')

    def login_required(view_func):
        @wraps(view_func)
        def decorated(*args, **kwargs):
            if 'logged_in' not in session or not session['logged_in']:
                flash('Please log in to access the page', 'warning')
                return redirect(url_for('login'))
            return view_func(*args, **kwargs)
        return decorated

    # Flask-Limiterの初期化
    limiter = Limiter(get_remote_address, app=flaslk_app, default_limits=["5 per minutes"])

    # ログインのルート

    @flaslk_app.route('/login', methods=['GET', 'POST'])
    @limiter.limit("5 per minutes", key_func=get_remote_address)  # ここでLimiterのデコレータを使用
    def login():
        # ip_address = get_remote_address()
        form = LoginForm()
        if form.validate_on_submit():
            print("ログイン試行がありました。")
            user = User.query.filter_by(username=form.username.data).first()
            if user and user.check_password(form.password.data):
                session['logged_in'] = True
                session.permanent = True  # セッションを永続的にする
                flaslk_app.permanent_session_lifetime = timedelta(days=30)
                print("ログイン成功がありました。")
                flash('Login Successful!', 'success')
                return redirect(url_for('index'))
            else:
                flash('Login Unsuccessful. Please check username and password', 'danger')
        return render_template('login.html', title='Login', form=form)

    return login_required, limiter

View用のHTML(templates\login.html)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{ title }}</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 20px;
            padding: 20px;
        }

        h1 {
            color: #333;
        }

        form {
            max-width: 400px;
            margin: 20px auto;
        }

        label {
            display: block;
            margin-bottom: 5px;
        }

        input {
            width: 100%;
            padding: 8px;
            margin-bottom: 10px;
        }

        .flashes {
            list-style: none;
            padding: 0;
            margin: 0;
        }

        .flashes li {
            padding: 10px;
            margin-bottom: 10px;
            border: 1px solid #ddd;
        }

        .flashes .success {
            background-color: #d4edda;
            border-color: #c3e6cb;
            color: #155724;
        }

        .flashes .info {
            background-color: #d1ecf1;
            border-color: #bee5eb;
            color: #0c5460;
        }

        .flashes .warning {
            background-color: #fff3cd;
            border-color: #ffeeba;
            color: #856404;
        }

        .flashes .danger {
            background-color: #f8d7da;
            border-color: #f5c6cb;
            color: #721c24;
        }

        span {
            color: red;
        }
    </style>
</head>
<body>
    <h1>{{ title }}</h1>
    
    {% with messages = get_flashed_messages(with_categories=true) %}
        {% if messages %}
            <ul class="flashes">
                {% for category, message in messages %}
                    <li class="{{ category }}">{{ message }}</li>
                {% endfor %}
            </ul>
        {% endif %}
    {% endwith %}

    <form method="post" action="{{ url_for('login') }}">
        {{ form.csrf_token }}
        <label for="username">Username:</label>
        {{ form.username(size=20) }}
        {% for error in form.username.errors %}
            <span>{{ error }}</span>
        {% endfor %}

        <br>

        <label for="password">Password:</label>
        {{ form.password(size=20) }}
        {% for error in form.password.errors %}
            <span>{{ error }}</span>
        {% endfor %}

        <br>

        {{ form.submit }}
    </form>
</body>
</html>

以上最速ログインフォームの実装の参考になれば幸いです。

コメント

タイトルとURLをコピーしました