您的位置:首页 > 其它

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

2016-09-24 17:09 323 查看
Flask-Login扩展,记住认证状态,管理用户认证系统中的认证状态

pip install flask-login

准备用于登录的用户模型











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 '<Role %r>' % 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 '<User %r>' % self.username

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




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


保护路由



添加登录表单





flasky/app/auth/forms.py登录表单



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"的<input>元素。
remember_me = BooleanField('Keep me logged in')
###BooleanField类表示复选框。
submit = SubmitField('Log In')






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 navigation</span>
<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') }}">Flasky</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li><a href="{{ url_for('main.index') }}">Home</a></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 Out</a></li>
{% else %}
<li><a href="{{ url_for('auth.login') }}">Log In</a></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定义,且在视图函数和模板中自动调用。



图中代码中Sign Out和Sign in是错的,代码应该是Log Out和Log In才能显示网页中右上角的效果。

登入用户

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







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



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)


登录表单时的模板





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>Login</h1>
</div>
################################################
<div class="col-md-4">
{{ wtf.quick_form(form) }}
</div>
###加入渲染的表单
################################################
{% endblock %}


测试登录







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包
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: