yutamugiruru
@yutamugiruru (mui)

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

エックスサーバー上でログイン保持の方法を知りたいです。

解決したいこと

エックスサーバー上でログイン保持を可能にし、HTML上でcurrent_userを機能させたいです。

例)
現在Flaskを使用してWEBアプリケーションを作成しております。
アプリケーションの内容は、管理者は店舗追加、パスワード変更ができ、一般の方は閲覧のみが可能といった内容です。
ログイン機能についてはFlask-loginを使用しております。
ローカル上ではログイン保持ができ、エックスサーバー上ではログイン保持ができずにいます。

該当するソースコード

from flask import Flask, session, app
from flask import render_template, request, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
from flask_login import UserMixin, LoginManager, login_user, logout_user, login_required, current_user
from werkzeug.security import generate_password_hash, check_password_hash
import os

#app 
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///info.db'
app.config['SECRET_KEY'] = os.urandom(24)
app.config['PERMANENT_SESSION_LIFETIME'] =  timedelta(minutes=10)
db = SQLAlchemy(app)

#flask login
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'

#user table
class User(UserMixin,db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    authority = db.Column(db.String(15), nullable=False)
    password = db.Column(db.String(15), nullable=False)

#login_manager
@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))

#login
@app.route("/login",methods=['GET','POST'])
def login():
    session.permanent = True
    if request.method == 'POST':

        password = request.form.get('password')

        user1 = User.query.filter_by(authority="管理者").one_or_none()
        user2 = User.query.filter_by(authority="一般").one_or_none()

        if check_password_hash(user1.password, password):
            login_user(user1)
            return redirect('search')
        elif check_password_hash(user2.password, password):
            login_user(user2)
            return redirect('search')
        elif password == '':
            err_msg = "パスワードを入力してください"
            return render_template('auth/login.html', err_msg = err_msg)
        else:
            err_msg = "パスワードが違います"
            return render_template('auth/login.html', err_msg = err_msg)
    else:
        return render_template('auth/login.html')

ログイン後、リダイレクト先のHTML(/search)

{% extends "base.html" %}
{% block header %}

    <div class="header_modal">
        <div class="header_right">
            <a href="{{url_for('logout')}}">Logout</a>
            <a href="{{url_for('search')}}">検索</a>
            <a href="{{url_for('info')}}">新規提携</a>
            <a href="{{url_for('favorite')}}">おすすめ</a>

            {% if current_user.id == 1 %}
                <a href="{{url_for('edit')}}">パスワード変更</a>
                <a href="{{url_for('create')}}">店舗追加</a>
            {% endif %}
        </div>
    </div>

    <div class="burger_btn">
        <span class="bar bar_top"></span>
        <span class="bar bar_mid"></span>
        <span class="bar bar_bottom"></span>
    </div>

{% endblock %}

ログインページのHTML(/login)

{% extends "base.html" %}
{% block content %}
{% if request.method == 'GET' %}

<!--login form-->
<div class="login">
    <img src="{{url_for('static', filename='outputs/storeParam(1).jpg')}}" alt="" class="login_img">
    <div class="login_wrapper">
            <h1>Login</h1>
            <div class="container">
                <form action="" method="POST">
                    <div class="input-group flex-nowrap login_form">
                        <span class="input-group-text label" id="addon-wrapping">password</span>
                        <input type="password" name="password" class="form-control form" aria-describedby="addon-wrapping">
                    </div>
                    <input type="submit" value="login" class="login_btn btn btn-dark">
                </form>
            </div>
    </div>
</div>

{% else %}

自分で試したこと(nextを使用をしましたが、変化なしでした。)

#login
@app.route("/login",methods=['GET','POST'])
def login():
    session.permanent = True
    if request.method == 'POST':

        password = request.form.get('password')

        user1 = User.query.filter_by(authority="管理者").one_or_none()
        user2 = User.query.filter_by(authority="一般").one_or_none()

        if check_password_hash(user1.password, password):
            login_user(user1)
          #nextでパラメーターを取得
            next = request.args.get('next')
            return redirect(next or url_for('search'))
        elif check_password_hash(user2.password, password):
            login_user(user2)
          #nextでパラメーターを取得
            next = request.args.get('next')
            return redirect(next or url_for('search'))
        elif password == '':
            err_msg = "パスワードを入力してください"
            return render_template('auth/login.html', err_msg = err_msg)
        else:
            err_msg = "パスワードが違います"
            return render_template('auth/login.html', err_msg = err_msg)
    else:
        return render_template('auth/login.html')

その他、試したこと
user_loaderの部分を
@login_manager.user_loader
def load_user(user_id):
user = User.query(id = user_id)
return user
と書き換えたりもしましたが特に変化はありませんでした。
また、データベースはsqlite3で接続不良かとも思いましたがデータベース自体は正常に動きました。

この解決方法をご存じの方、ご教授の程よろしくお願い致します。

0

1Answer

app.config['SECRET_KEY'] = os.urandom(24)

これではアプリケーションを起動するごとに SECRET_KEY の値が変わってしまいます。特に、もしエックスサーバー上で CGI を使って実行している場合、1リクエストごとに新しいアプリケーションが起動するので、毎回値が変わります。

SECRET_KEY はセッション情報の暗号化などに使うため、この値が変わるとセッションに保存したログイン情報も取り出せなくなってしまいます。それでログイン保持ができないのだと思います。

SECRET_KEY は適当な固定値に設定してください。以下のコマンドを使えば簡単にランダムな値を生成できます。

% python3 -c 'import secrets; print(secrets.token_urlsafe(24))'
u0sk9CiXDHr9uBAfjFexTIl7SHNRQ_TW # この値は例です。そのまま使わないでください

設定した SECRET_KEY は他人に見られることのないようにしてください。ソースコードに直接書いてもいいですが、それよりは以下のページを参考に設定ファイルか環境変数にセットすると安全です。

0Like

Comments

  1. @yutamugiruru

    Questioner

    度々ありがとうございます!
    シークレットキーに注目していませんでした。。
    とてつもなく有益なアドバイスでした、ありがとうございます。
  2. @yutamugiruru

    Questioner

    試してみたところ、問題解決いたしました。
    改めてありがとうございました。。

Your answer might help someone💌