本篇博客主要介绍 flask-login 会话管理方面的内容,完成用户合法的登录和登出,以及一些页面视图的保护功能。
1.相关库安装
pip install flask-login
2.相关代码
在 extensions.py 中添加 flask-login 的扩展实例和其需要的加载用户函数,以及相关的登录控制:
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_bootstrap import Bootstrap
from flask_login import LoginManager
db = SQLAlchemy()
migrate = Migrate()
bootstrap = Bootstrap()
login_manager = LoginManager()
@login_manager.user_loader
def load_user(user_id):
from myblog.models import Admin
user = Admin.query.get(int(user_id))
return user
login_manager.login_view = 'admin.login'
login_manager.login_message = '你必须登陆后才能访问该页面!'
login_manager.login_message_category = "info"
在 __init__.py 中添加初始化 flask-login 的代码:
from flask import Flask
from myblog.home.blog import blog_bp
from myblog.home.admin import admin_bp
from myblog.extensions import db, migrate, bootstrap, login_manager
from myblog.config import Config
def create_app():
app = Flask(__name__)
app.config.from_object(Config)
register_blueprints(app)
register_extensions(app)
return app
def register_blueprints(app):
app.register_blueprint(blog_bp)
app.register_blueprint(admin_bp, url_prefix='/admin')
def register_extensions(app):
db.init_app(app)
db.create_all(app=app)
migrate.init_app(app, db)
bootstrap.init_app(app)
login_manager.init_app(app)
修改 models.py 中的 Admin 类,使其继承自 UserMixin 类:
from datetime import datetime
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import UserMixin
from myblog.extensions import db
class Admin(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20))
password_hash = db.Column(db.String(128))
def set_password(self, password):
self.password_hash = generate_password_hash(password)
def validate_password(self, password):
return check_password_hash(self.password_hash, password)
修改 home/admin.py 中的视图函数,添加登录登出、视图保护的代码:
from flask import request, Blueprint, render_template, redirect, url_for, flash
from flask_login import login_user, login_required, current_user, logout_user
from myblog.models import Article, Category, Comment, Admin
from myblog.forms import LoginForm, AddForm
from myblog.extensions import db
admin_bp = Blueprint('admin', __name__)
@admin_bp.route('/login', methods=['POST', 'GET'])
def login():
form = LoginForm()
if form.validate_on_submit():
username = form.username.data
password = form.password.data
remember = form.remember.data
admin = Admin.query.first()
if username == admin.username and admin.validate_password(password):
login_user(admin)
flash('登录成功', category='info')
return redirect(url_for('admin.edit'))
else:
flash('登录失败', category='warning')
return render_template('admin/login.html', form=form)
@admin_bp.route('/logout')
@login_required
def logout():
logout_user()
flash('再见!')
return redirect(url_for('admin.login'))
@admin_bp.route('/edit')
@login_required
def edit():
page = request.args.get('page', 1, type=int)
articles = Article.query.order_by(Article.timestamp.desc()).paginate(page, 10, False)
return render_template('admin/edit.html', articles=articles)
@admin_bp.route('/delete/<int:article_id>')
@login_required
def delete(article_id):
article = Article.query.filter(Article.id == article_id).first()
db.session.delete(article)
db.session.commit()
flash('删除成功', category='info')
return redirect(url_for('admin.edit'))
@admin_bp.route('/add', methods=['POST', 'GET'])
@login_required
def add():
form = AddForm()
if form.validate_on_submit():
title = form.title.data
body = form.body.data
category = form.category.data
article = Article(title=title, body=body, category_id=category)
db.session.add(article)
db.session.commit()
flash('新建博客成功', category='info')
return redirect(url_for('admin.edit'))
return render_template('admin/add.html', form=form)
在 base.html 中修改导航栏页面,视登录情况显示登录或登出按钮:
<!DOCTYPE html>
<html lang="en">
<head>
{% block head %}
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
{% block styles %}
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}"> {{ bootstrap.load_css() }}
{% endblock %}
<title>{% block title %}{% endblock %}</title>
{% endblock %}
</head>
<body>
{% for message in get_flashed_messages() %}
<div class="alert alert-primary" role="alert">
{{ message }}
</div>
{% endfor %}
{% block nav %}
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container">
<a class="navbar-brand" href="#">我的博客</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-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">
<ul class="navbar-nav mr-auto">
<li class="nav-item {% if request.endpoint == 'blog.index' %} active {% endif %}">
<a class="nav-link" href="{{ url_for('blog.index') }}">首页<span class="sr-only">(current)</span></a>
</li>
</ul>
<ul class="navbar-nav">
{% if current_user.is_authenticated %}
<li class="nav-item {% if request.endpoint == 'admin.logout' %} active {% endif %}">
<a class="nav-link" href="{{ url_for('admin.logout') }}">登出</a>
</li>
{% else %}
<li class="nav-item {% if request.endpoint == 'admin.login' %} active {% endif %}">
<a class="nav-link" href="{{ url_for('admin.login') }}">登录</a>
</li>
{% endif %}
</ul>
</div>
</div>
</nav>
{% endblock nav %}
<div class="container">
{% block content %}{% endblock%}
</div>
{% block scripts %}
{{ bootstrap.load_js() }}
{% endblock %}
</body>
</html>
3.相关页面展示
登录后的后台页面:
未登录时,后台页面的 edit 视图需要登录才能查看: