Um so etwas wie einen Django-Administratorbildschirm mit Flask zu implementieren, ist es bequem, eine Bibliothek namens Flask-Admin zu verwenden. Wenn Sie jedoch nur Flask-Admin verwenden, können Sie den Administratorbildschirm aufrufen, ohne ein Kennwort einzugeben (ohne sich anzumelden), was aus Sicherheitsgründen sehr anfällig ist. In diesem Artikel verwenden wir Flask-Admin und Flask-Login, um ** einen DB-Administratorbildschirm mit einer Anmeldefunktion zu implementieren **.
Da die Informationsmenge etwas gering ist, ist dies ein Artikel, in dem ich versucht habe, die Informationen der Erklärung auf Japanisch zu erweitern. Diejenigen, die Englisch sprechen können oder nicht gut in redundanten Formulierungen sind, können die Referenzseite über den obigen Link sehen.
Model-View-Controller Sie müssen ein wenig über das Modell wissen. Denn in diesem Artikel werde ich Wörter wie Modell und Controller verwenden. Ich denke, der folgende Artikel wird hilfreich sein. Informationen zum MVC-Modell
Ubuntu20.04LTS
MySQL 8.0.21
Python3.8.5
Flask==1.1.2
Flask-Admin==1.5.6
Flask-Login==0.5.0
Flask-SQLAlchemy==2.4.4
mysqlclient==2.0.1
Plötzlich zeige ich Ihnen den gesamten endgültigen Quellcode für diejenigen, die nicht die Zeit haben. Eine ausführliche Erklärung finden Sie unten.
from flask import Flask, abort, jsonify, render_template, request, redirect, url_for
from wtforms import form, fields, validators
import flask_admin as admin
import flask_login as login
from flask_admin.contrib import sqla
from flask_admin import helpers, expose
from flask_admin.contrib.sqla import ModelView
from werkzeug.security import generate_password_hash, check_password_hash
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = "mysql://{user}:{password}@{host}/{db_name}".format(**{
'user': os.environ['RDS_USER'],
'password': os.environ['RDS_PASS'],
'host': os.environ['RDS_HOST'],
'db_name': os.environ['RDS_DB_NAME']
})
app.config['SECRET_KEY'] = os.environ['FLASK_SECRET_KEY']
db = SQLAlchemy(app)
class AdminUser(db.Model):
id = db.Column(db.Integer, primary_key=True)
login = db.Column(db.String(50), unique=True)
password = db.Column(db.String(250))
@property
def is_authenticated(self):
return True
@property
def is_active(self):
return True
@property
def is_anonymous(self):
return False
def get_id(self):
return self.id
def __unicode__(self):
return self.username
class LoginForm(form.Form):
login = fields.StringField(validators=[validators.required()])
password = fields.PasswordField(validators=[validators.required()])
def validate_login(self, field):
user = self.get_user()
if user is None:
raise validators.ValidationError('Der Benutzername oder das Passwort ist unterschiedlich.')
if not check_password_hash(user.password, self.password.data):
raise validators.ValidationError('Der Benutzername oder das Passwort ist unterschiedlich.')
def get_user(self):
return db.session.query(AdminUser).filter_by(login=self.login.data).first()
class RegistrationForm(form.Form):
login = fields.StringField(validators=[validators.required()])
password = fields.PasswordField(validators=[validators.required()])
def validate_login(self, field):
if db.session.query(AdminUser).filter_by(login=self.login.data).count() > 0:
raise validators.ValidationError('Der gleiche Benutzername existiert.')
def init_login():
login_manager = login.LoginManager()
login_manager.init_app(app)
@login_manager.user_loader
def load_user(user_id):
return db.session.query(AdminUser).get(user_id)
class MyModelView(sqla.ModelView):
def is_accessible(self):
return login.current_user.is_authenticated
class MyAdminIndexView(admin.AdminIndexView):
@expose('/')
def index(self):
if not login.current_user.is_authenticated:
return redirect(url_for('.login_view'))
return super(MyAdminIndexView, self).index()
@expose('/login/', methods=('GET', 'POST'))
def login_view(self):
form = LoginForm(request.form)
if helpers.validate_form_on_submit(form):
user = form.get_user()
login.login_user(user)
if login.current_user.is_authenticated:
return redirect(url_for('.index'))
link = '<p>Um kein Konto zu erstellen<a href="' + url_for('.register_view') + '">Klicke hier</a></p>'
self._template_args['form'] = form
self._template_args['link'] = link
return super(MyAdminIndexView, self).index()
@expose('/register/', methods=('GET', 'POST'))
def register_view(self):
form = RegistrationForm(request.form)
if helpers.validate_form_on_submit(form):
user = AdminUser()
form.populate_obj(user)
user.password = generate_password_hash(form.password.data)
db.session.add(user)
db.session.commit()
login.login_user(user)
return redirect(url_for('.index'))
link = '<p>Wenn Sie bereits ein Konto haben<a href="' + url_for('.login_view') + '">Klicken Sie hier, um sich einzuloggen</a></p>'
self._template_args['form'] = form
self._template_args['link'] = link
return super(MyAdminIndexView, self).index()
@expose('/logout/')
def logout_view(self):
login.logout_user()
return redirect(url_for('.index'))
init_login()
admin = admin.Admin(app, 'Administratorbildschirm', index_view=MyAdminIndexView(), base_template='my_master.html')
admin.add_view(MyModelView(AdminUser, db.session))
@app.route("/", methods=['GET'])
def index():
return "Hello, World!"
if __name__ == "__main__":
app.run()
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = "mysql://{user}:{password}@{host}/{db_name}".format(**{
'user': os.environ['RDS_USER'],
'password': os.environ['RDS_PASS'],
'host': os.environ['RDS_HOST'],
'db_name': os.environ['RDS_DB_NAME']
})
app.config['SECRET_KEY'] = os.environ['FLASK_SECRET_KEY']
db = SQLAlchemy(app)
Da es im Internet viele japanische Artikel gibt, wird die Erklärung hier weggelassen.
class AdminUser(db.Model):
id = db.Column(db.Integer, primary_key=True)
login = db.Column(db.String(50), unique=True)
password = db.Column(db.String(250))
@property
def is_authenticated(self):
return True
@property
def is_active(self):
return True
@property
def is_anonymous(self):
return False
def get_id(self):
return self.id
def __unicode__(self):
return self.username
Login ist Ihr Benutzername. Es definiert den Benutzernamen und das Kennwort für die Anmeldung am Administratorbildschirm. Der Grund, warum jede Methode über einen Eigenschaftsdekorator verfügt, besteht darin, Informationen abzurufen, z. B. ob Sie sich bereits angemeldet haben, wenn Sie den Anmeldevorgang später schreiben. Wenn Sie mehr über Property Decorator erfahren möchten ↓ Eigenschaften
class LoginForm(form.Form):
login = fields.StringField(validators=[validators.required()])
password = fields.PasswordField(validators=[validators.required()])
def validate_login(self, field):
user = self.get_user()
if user is None:
raise validators.ValidationError('Der Benutzername oder das Passwort ist unterschiedlich.')
if not check_password_hash(user.password, self.password.data):
raise validators.ValidationError('Der Benutzername oder das Passwort ist unterschiedlich.')
def get_user(self):
return db.session.query(AdminUser).filter_by(login=self.login.data).first()
class RegistrationForm(form.Form):
login = fields.StringField(validators=[validators.required()])
password = fields.PasswordField(validators=[validators.required()])
def validate_login(self, field):
if db.session.query(AdminUser).filter_by(login=self.login.data).count() > 0:
raise validators.ValidationError('Der gleiche Benutzername existiert.')
Es ist ein Controller, der die Verarbeitung beschreibt, wenn Eingaben aus der Form der Ansicht empfangen werden (Anmeldebildschirm oder Registrierungsbildschirm für Administratorkonten). Was Sie hier beachten sollten, ist die LoginForm-Klasse.
check_password_hash(user.password, self.password.data)
ist. Dies ist eine praktische Methode, die das gehashte und gespeicherte echte Kennwort und den vom Anmeldebildschirm eingegebenen Hash-Wert vergleicht und True zurückgibt, wenn sie übereinstimmen.
Obwohl dies nicht empfohlen wird, sollte der bedingte Ausdruck verwendet werden, wenn das Kennwort in der Datenbank im Klartext gespeichert ist.
if user.password != self.password.data:
Ich denke, Sie sollten es ändern.
def init_login():
login_manager = login.LoginManager()
login_manager.init_app(app)
@login_manager.user_loader
def load_user(user_id):
return db.session.query(AdminUser).get(user_id)
class MyModelView(sqla.ModelView):
def is_accessible(self):
return login.current_user.is_authenticated
class MyAdminIndexView(admin.AdminIndexView):
@expose('/')
def index(self):
if not login.current_user.is_authenticated:
return redirect(url_for('.login_view'))
return super(MyAdminIndexView, self).index()
@expose('/login/', methods=('GET', 'POST'))
def login_view(self):
form = LoginForm(request.form)
if helpers.validate_form_on_submit(form):
user = form.get_user()
login.login_user(user)
if login.current_user.is_authenticated:
return redirect(url_for('.index'))
link = '<p>Um kein Konto zu erstellen<a href="' + url_for('.register_view') + '">Klicke hier</a></p>'
self._template_args['form'] = form
self._template_args['link'] = link
return super(MyAdminIndexView, self).index()
@expose('/register/', methods=('GET', 'POST'))
def register_view(self):
form = RegistrationForm(request.form)
if helpers.validate_form_on_submit(form):
user = AdminUser()
form.populate_obj(user)
user.password = generate_password_hash(form.password.data)
db.session.add(user)
db.session.commit()
login.login_user(user)
return redirect(url_for('.index'))
link = '<p>Wenn Sie bereits ein Konto haben<a href="' + url_for('.login_view') + '">Klicken Sie hier, um sich einzuloggen</a></p>'
self._template_args['form'] = form
self._template_args['link'] = link
return super(MyAdminIndexView, self).index()
@expose('/logout/')
def logout_view(self):
login.logout_user()
return redirect(url_for('.index'))
init_login()
admin = admin.Admin(app, 'Administratorbildschirm', index_view=MyAdminIndexView(), base_template='my_master.html')
admin.add_view(MyModelView(AdminUser, db.session))
Es ist ein bisschen so, als würde man es normalerweise mit Flask machen.
Was Sie hier beachten sollten, ist die MyModelView-Klasse. MyModelView erbt von sqla.ModelView und überschreibt die Methode is_accessible. (Ich muss einfach) Die Methode is_accessible gibt zurück, ob der Benutzer bereits angemeldet ist. Durch einfaches Überschreiben der Methode is_accessible können Sie Zugriffssteuerungsregeln in einer späteren Ansichtsklasse (hier die MyAdminIndexView-Klasse) definieren.
init_login()
admin = admin.Admin(app, 'Administratorbildschirm', index_view=MyAdminIndexView(), base_template='my_master.html')
admin.add_view(MyModelView(AdminUser, db.session))
Definiert, welche Ansichtsklasse in welchem Modell tatsächlich verwendet wird.
Ohne HTML ist es bedeutungslos. Erstellen Sie ein Vorlagenverzeichnis im Projektstammverzeichnis und erstellen Sie Dateien und Verzeichnisse mit der folgenden Struktur.
templates/
admin/
index.html
my_master.html
index.html
my_master.html
{% extends 'admin/base.html' %}
{% block access_control %}
{% if current_user.is_authenticated %}
<div class="btn-group pull-right">
<a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
<i class="icon-user"></i> {{ current_user.login }} <span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li><a href="{{ url_for('admin.logout_view') }}">Ausloggen</a></li>
</ul>
</div>
{% endif %}
{% endblock %}
Wenn Sie nach der Anmeldung die Benutzer-ID drücken, wird auf dem Bildschirm eine Dropdown-Schaltfläche angezeigt.
templates/index.html
<html>
<body>
<div>
<a href="{{ url_for('admin.index') }}">Go to admin!</a>
</div>
</body>
</html>
Jede Indexseite ist in Ordnung. Ich werde es angemessen schreiben.
templates/admin/index.html
{% extends 'admin/master.html' %}
{% block body %}
{{ super() }}
<div class="row-fluid">
<div>
{% if current_user.is_authenticated %}
<h1>Civichat Admin-Bildschirm</h1>
<p class="lead">
Zertifiziert
</p>
<p>
Sie können die Daten von diesem Bildschirm aus verwalten. Wenn Sie sich abmelden möchten/admin/Bitte greifen Sie auf die Abmeldung zu.
</p>
{% else %}
<form method="POST" action="">
{{ form.hidden_tag() if form.hidden_tag }}
{% for f in form if f.type != 'CSRFTokenField' %}
<div>
{{ f.label }}
{{ f }}
{% if f.errors %}
<ul>
{% for e in f.errors %}
<li>{{ e }}</li>
{% endfor %}
</ul>
{% endif %}
</div>
{% endfor %}
<button class="btn" type="submit">Erledigt</button>
</form>
{{ link | safe }}
{% endif %}
</div>
<a class="btn btn-primary" href="/"><i class="icon-arrow-left icon-white"></i>Rückkehr</a>
</div>
{% endblock body %}
Es ist wie eine Indexseite auf dem Admin-Bildschirm nach dem Anmelden.
Erstens denke ich, dass der Zugriff nach IP-Adresse eingeschränkt werden muss, bevor das Anmeldeformular erreicht wird. Ich denke, der folgende Artikel wird hilfreich sein.
Wenn Sie Fehler haben, teilen Sie uns dies bitte in den Kommentaren mit.
Recommended Posts