建立用户认证系统需要经过以下几步:
- app/__init__.py中声明并初始化数据库扩展
- app/models.py中定义模型
- app/auth/__init__.py:创建认证蓝本
- 使用flask-login认证用户
- 修改模型
- 添加登录表单
- 用户登入登出
- 测试
逐步说明:
第一步:数据库初始化
from flask.ext.sqlalchemy import SQLAlchemy
db = SQLAlchemy()
def create_app():
...
db.init_app(app)
...
第二步: 用户模型
from . import db #导入声明的数据库
from werkzeug.security import generate_password_hash, check_password_hash #导入hash加密
#创建用户数据模型
class User(db.Model):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), unique=True, index=True)
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
前两步用于建立数据库,用于第三步的表单验证
第三步: 认证蓝本
app/ auth/__ init__. py: 创建蓝本
from flask import Blueprint
auth = Blueprint
from . import viewsapp/auth/views.py:路由和视图函数
from flask import render_template
from . import auth
@auth.route('/')
def login():
return render_template('auth/login.html')-
app/__ init__. py: 添加认证蓝本
from .auth import auth as auth_blueprint app.register_blueprint(auth_blueprint, url_prefix='/auth')
第四步
1.修改用户模型,支持用户登录
Flask-Login扩展要求程序的User模型需要具备以下几个方法:
- is_authenticated() 是否登录
- is_active() 是否允许登录
- is_anoymous() 普通用户返回False
- get_id() 返回用户的唯一标示符
可以利用flask-login的UserMixin类实现以上方法,即class User(UserMixin, db.Model):多重继承
- app/__init__.py:初始化Flask-Login
from flask.ext.login import LoginManager
login_manager=LoginManger()
login_manager.session_pretection='strong' #登录保护
login_manager.login_view = 'auth.login' #设置登录页面的端点
def create_app():
...
login_manager.init_app(app)
Flask-Login需要程序用一个回调函数使用指定的标示符加载用户
** app/models.py: 加载用户的回调函数**
from . import login_manager
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
2.app/auth/forms.py: 登录表单
from flask.ext.wtf import Form #导入表单
from wtforms import StringField, PasswordField, BooleanField, SubmitField #表单输入格式
from wtforms.validators import DataRequired, Length, Email #表单验证模块
class LoginForm(Form):
email = StringField('Email', validators=[DataRequired(), Length(1,64), Email()])
password = PasswordField('password', validators=[DataRequired()]) remember_me = BooleanField('Keep me Logged in ')
submit = SubmitField('Log In')
修改base.html,根据登录状态改变链接
{% if current_user.is_authenticated %}
<li id="signin_icon"><a href="{{ url_for('auth.logout') }}">SIGNOUT</a></li>
{% else %}
<li><a href="{{ url_for('auth.login') }}">SIGNIN</a></li>
{% endif %}
判断条件中的变量 current_user 由 Flask-Login 定义,且在视图函数和模板中自动可用。这个变量的值是当前登录的用户,如果用户尚未登录,则是一个匿名用户。如果是匿名用户,is_authenticated() 方法返回 False。所以这个方法可用来判断当前用户是否已经登录。
3.用户登入登出
- app/auth/views.py: 添加登入登出路由
from flask import render_template, redirect, request, url_for, flash
from flask.ext.login import login_required, login_user, logout_user
from . import auth
from ..models import User
from .forms import LoginForm
@auth.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(email=form.email.data).first()
if user is not None and user.verify_password(form.password.data):
login_user(user, form.remember_me.data)
return redirect(request.args.get('next') or url_for('main.index'))
flash('Invalid username or password.')
return render_template('auth/login.html', form=form)
@auth.route('/logout')
@login_required
def logout():
logout_user()
flash('You have been logged out. ')
return redirect(url_for('main.index'))
- app/templates/auth/login.html: 渲染登录表单
{% extends 'base.html' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block title %}HOPTOP | Login {% endblock %}
{% block content %}
<div class="container">
<div class="page-header">
<h1>Login</h1>
</div>
<div class="col-md-4">
{{ wtf.quick_form(form) }}
</div>
</div>
{% endblock %}
第五步:测试
-
在数据中添加用户
db.create_all() # 如果没有创建数据库,执行这一步 u = User(email='admin@admin.com', username='admin', password='admin') db.session.add(u) db.session.commit() #提交
打开网页测试
中间会出现很多问题,细心的解决