用户认证(二)【使用Flask-Login认证用户】

Flask-Login扩展,记住认证状态,管理用户认证系统中的认证状态
pip install flask-login

准备用于登录的用户模型

用户认证(二)【使用Flask-Login认证用户】_第1张图片
用户认证(二)【使用Flask-Login认证用户】_第2张图片
用户认证(二)【使用Flask-Login认证用户】_第3张图片
这里写图片描述
用户认证(二)【使用Flask-Login认证用户】_第4张图片
flasky/app/models.py

from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import UserMixin
###从flask_login导入UserMixin类
###USerMixin类包含的以上四种方法的默认实现。
from . import db, login_manager
###从程序的工厂函数引入login_manager实例


class Role(db.Model):
    __tablename__ = 'roles'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    users = db.relationship('User',backref='role',lazy='dynamic')

    def __repr__(self):
        return '' % self.name


class User(UserMixin, db.Model):
###User继承UserMixin和db.Model类的功能属性
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(64), unique=True, index=True)
    ###加入email属性,用来储存用户的email
    username = db.Column(db.String(64),unique=True, index=True)
    role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
    password_hash = db.Column(db.String(128))

    @property
    def password(self):
        raise AttributeError('password is not a readable attribute')

    @password.setter
    def password(self,password):
        self.password_hash = generate_password_hash(password)

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

    def __repr__(self):
        return '' % self.username

@login_manager.user_loader
###加载用户的回调函数接收以Unicode字符串形式表示的用户标示符
###如果能找到用户,这个函数必须返回用户对象,否则返回None。
def load_user(user_id):
    return User.query.get(int(user_id))

用户认证(二)【使用Flask-Login认证用户】_第5张图片
flasky/app/_init_.py

from flask import Flask
from flask_bootstrap import Bootstrap
from flask_mail import Mail
from flask_moment import Moment
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from config import config

bootstrap = Bootstrap()
mail = Mail()
moment = Moment()
db = SQLAlchemy()

login_manager = LoginManager()
login_manager.session_protection = 'strong'
login_manager.login_view = 'auth.login'


def create_app(config_name):
    app = Flask(__name__)
    app.config.from_object(config[config_name])
    config[config_name].init_app(app)

    bootstrap.init_app(app)
    mail.init_app(app)
    moment.init_app(app)
    db.init_app(app)
    login_manager.init_app(app)

    from .main import main as main_blueprint
    app.register_blueprint(main_blueprint)

    from .auth import auth as auth_blueprint
    app.register_blueprint(auth_blueprint, url_prefix='/auth')

    return app

保护路由

用户认证(二)【使用Flask-Login认证用户】_第6张图片

添加登录表单

用户认证(二)【使用Flask-Login认证用户】_第7张图片
用户认证(二)【使用Flask-Login认证用户】_第8张图片
flasky/app/auth/forms.py登录表单
用户认证(二)【使用Flask-Login认证用户】_第9张图片

from flask_wtf import Form
###从Flask-WTF扩展导入Form基类
from wtforms import StringField, PasswordField, BooleanField, SubmitField
###从WTForms包中导入字段类
from wtforms.validators import Required, Length, Email
###从WTForms导入验证函数

class LoginForm(Form):
    email = StringField('Email', validators=[Required(), Length(1, 64), Email()])
    ###StringField构造函数中的可选参数validators指定一个有验证函数组成的列表,在接受用户提交的数据之前验证数据。
    ###电子邮件字段用到了WTForms提供的Length()和Email()验证函数。
    password = PasswordField('Password', validators=[Required()]) 
    ###PasswordField类表示属性为type="password"的元素。
    remember_me = BooleanField('Keep me logged in')
    ###BooleanField类表示复选框。
    submit = SubmitField('Log In')

用户认证(二)【使用Flask-Login认证用户】_第10张图片
用户认证(二)【使用Flask-Login认证用户】_第11张图片
flasky/app/templates/base.html

{% extends "bootstrap/base.html" %}

{% block title %}Flasky{% endblock %}

{% block head %}
{{ super() }}
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">
{% endblock %}

{% block navbar %}
<div class="navbar navbar-inverse" role="navigation">
    <div class="container">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                <span class="sr-only">Toggle navigationspan>
                <span class="icon-bar">span>
                <span class="icon-bar">span>
                <span class="icon-bar">span>
            button>
            <a class="navbar-brand" href="{{ url_for('main.index') }}">Flaskya>
        div>
        <div class="navbar-collapse collapse">
            <ul class="nav navbar-nav">
                <li><a href="{{ url_for('main.index') }}">Homea>li>
            ul>
            ########################################
            ###判断条件中的变量current_user由Flask-Login定义,且在视图函数和模板中自动调用。
            <ul class="nav navbar-nav navbar-right">
                {% if current_user.is_authenticated %}
                <li><a href="{{ url_for('auth.logout') }}">Log Outa>li>
                {% else %}
                <li><a href="{{ url_for('auth.login') }}">Log Ina>li>
                {% endif %}
            ul> 
            ########################################
        div>
    div>
div>
{% endblock %}

{% block content %}
<div class="container">
    {% for message in get_flashed_messages() %}
    <div class="alert alert-warning">
        <button type="button" class="close" data-dismiss="alert">×button>
        {{ message }}
    div>
    {% endfor %}

    {% block page_content %}{% endblock %}
div>
{% endblock %}

{% block scripts %}
{{ super() }}
{{ moment.include_moment() }}
{% endblock %}

判断条件中的变量current_user由Flask-Login定义,且在视图函数和模板中自动调用。
用户认证(二)【使用Flask-Login认证用户】_第12张图片
图中代码中Sign Out和Sign in是错的,代码应该是Log Out和Log In才能显示网页中右上角的效果。

登入用户

登录路由【视图函数login()】

用户认证(二)【使用Flask-Login认证用户】_第13张图片
用户认证(二)【使用Flask-Login认证用户】_第14张图片
用户认证(二)【使用Flask-Login认证用户】_第15张图片

登出路由【视图函数logout()】

用户认证(二)【使用Flask-Login认证用户】_第16张图片

flasky/app/auth/views.py

from flask import render_template, redirect, request, url_for, flash
from flask_login import login_user, logout_user, login_required 
###从Flask_login导入login_user, logout_user, login_required 函数
from . import auth 
###从本级目录中导入auth蓝本
from ..models import User 
###从上级目录中的models.py导入User模型
from .forms import LoginForm
从本级目录中的forms.py中导入LoginForm类

@auth.route('/login', methods=['GET', 'POST'])
###当请求为GET时,直接渲染模板,当请求是POST提交时,验证表格数据,然后尝试登入用户。
def login():
    form = LoginForm()
    if form.validate_on_submit():###表格中填入了数据,执行下面操作
        user = User.query.filter_by(email=form.email.data).first() 
        ###视图函数使用表单中填写的email加载用户
        if user is not None and user.verify_password(form.password.data):
        ###如果user不是空的,而且验证表格中的密码正确,执行下面的语句,调用Flask_Login中的login_user()函数,在用户会话中把用户标记为登录。
        ###否则直接执行flash消息和跳转到新表格中。
            login_user(user, form.remember_me.data)
            ###login_user函数的参数是要登录的用户,以及可选的‘记住我’布尔值。
            return redirect(request.args.get('next') or url_for('main.index'))
            ###用户访问未授权的URL时会显示登录表单,Flask-Login会把原地址保存在查询字符串的next参数中,这个参数可从request.args字典中读取。如果查询字符串中没有next参数,则重定向到首页。
        flash('Invalid username or password.')
    return render_template('auth/login.html', form=form)


@auth.route('/logout') 
###退出路由
@login_required 
###用户要求已经登录
def logout():
    logout_user()
    ###登出用户,这个视图函数调用logout_user()函数,删除并重设用户会话。
    flash('You have been logged out.')
    ###显示flash消息
    return redirect(url_for('main.index'))
    ###重定向到首页

login_user()函数的原型:flask.ext.login.login_user(user, remember=False, force=False, fresh=True)

登录表单时的模板

用户认证(二)【使用Flask-Login认证用户】_第17张图片
用户认证(二)【使用Flask-Login认证用户】_第18张图片

flasky/app/templates/auth/login.html

{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}

{% block title %}Flasky - Login{% endblock %}

{% block page_content %}
<div class="page-header">
    <h1>Loginh1>
div>
################################################
<div class="col-md-4">
    {{ wtf.quick_form(form) }}
div>
###加入渲染的表单
################################################
{% endblock %}

测试登录

用户认证(二)【使用Flask-Login认证用户】_第19张图片
用户认证(二)【使用Flask-Login认证用户】_第20张图片
用户认证(二)【使用Flask-Login认证用户】_第21张图片
flasky/app/templates/index.html

{% extends "base.html" %}

{% block title %}Flasky{% endblock %}

{% block page_content %}
#####################################################
<div class="page-header">
    <h1>Hello, {% if current_user.is_authenticated %}{{ current_user.username }}{% else %}Stranger{% endif %}!h1>
div>
#####################################################
###用current._user.is_authenticated测试是否是登录的用户首页
{% endblock %}


此外,还有以下改动:
把之前main/views.py的各种复杂的储存用户会话消息name,判断是否是第一次输入,以及发送邮件等等去掉,简化成直接返回index.html模板。
flasky/app/main/views.py

from flask import render_template
from . import main


@main.route('/')
def index():
    return render_template('index.html')

flasky/migrations/versions/456a945560f6_login_support.py
数据库迁移脚本版本

"""login support
Revision ID: 456a945560f6
Revises: 38c4e85512a9
Create Date: 2013-12-29 00:18:35.795259
"""

# revision identifiers, used by Alembic.
revision = '456a945560f6'
down_revision = '38c4e85512a9'

from alembic import op
import sqlalchemy as sa


def upgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.add_column('users', sa.Column('email', sa.String(length=64), nullable=True))
    op.add_column('users', sa.Column('password_hash', sa.String(length=128), nullable=True))
    op.create_index('ix_users_email', 'users', ['email'], unique=True)
    ### end Alembic commands ###


def downgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.drop_index('ix_users_email', 'users')
    op.drop_column('users', 'password_hash')
    op.drop_column('users', 'email')
    ### end Alembic commands ###

flasky/requirements.txt
所有依赖包的文件

alembic==0.8.8
blinker==1.4
click==6.6
dominate==2.2.1
Flask==0.11.1
Flask-Bootstrap==3.3.7.0
Flask-Mail==0.9.1
Flask-Migrate==2.0.0
Flask-Moment==0.5.1
Flask-Script==2.0.5
Flask-SQLAlchemy==2.1
Flask-WTF==0.12
itsdangerous==0.24
Jinja2==2.8
Mako==1.0.4
MarkupSafe==0.23
python-editor==1.0.1
SQLAlchemy==1.0.15
visitor==0.1.3
Werkzeug==0.11.11
WTForms==2.1
Flask-Login==0.3.1 
###添加了flask-login包

你可能感兴趣的:(-----flask)