16.flask-login

Flask-Login

概念

Flask-Login 为 Flask 提供了用户会话管理。处理了日常的登入,登出并且长时间记住用户的会话。

会:

  • 在会话中存储当前活跃的用户 ID,让你能够自由地登入和登出。
  • 能够限制登入(或者登出)用户可以访问的视图。
  • 处理让人棘手的 “记住我” 功能。
  • 能够帮助保护用户会话免遭 cookie 被盗的牵连。
  • 可以与以后可能使用的 Flask-Principal 或其它认证扩展集成。

但是,不会:

  • 限制使用特定的数据库或其它存储方法。如何加载用户完全由你决定。
  • 限制使用用户名和密码,OpenIDs,或者其它的认证方法。
  • 处理超越 “登入或者登出” 之外的权限。
  • 处理用户注册或者账号恢复。

安装

pip install flask-login

配置

from flask import Flask
from flask_login import LoginManager

app = Flask(__name__)

# 实例化对象
login_manager = LoginManager()
# 懒加载
login_manager.init_app(app)
# 设置登录视图函数
login_manager.login_view = "login"

if __name__ == "__main__":
    app.run()

登录管理对象 login_managerlogin_view 属性,指定登录页面的视图函数 (登录页面的 endpoint),即验证失败时要跳转的页面,设置为登录页。

用户模块

构造数据

要做用户验证,需要维护用户记录,为了方便演示,使用一个全局列表 USERS 来记录用户信息,并且初始化了两个用户信息:

# 用户信息
USERS = [
    {
        "id": 1,
        "name": '123456',
        "password": generate_password_hash('123456')
    },
    {
        "id": 2,
        "name": '654321',
        "password": generate_password_hash('654321')
    }
]

password 为登录密码,通过模块 werkzeug.security 提供了 generate_password_hash 方法,使用 sha256 加密算法将字符串变为密文。

不要在系统中存放用户密码的明文

基于用户信息,定义两方法,用来创建( create_user )和获取( get_user )用户信息:

# 创建一个用户
def create_user(username, password):
    user = {
        "id": uuid.uuid4()
        "name": username,
        "password": generate_password_hash(password),
    }
    USERS.append(user)

# 根据用户名获得用户记录
def get_user(username):
    for user in USERS:
        if user.get("name") == username:
            return user
    return None
用户类

下面创建一个用户类,类维护用户的登录状态,是生成 Session 的基础,Flask-Login 提供了用户基类 UserMixin,方便定义自己的用户类,定义一个 User

from flask_login import LoginManager,UserMixin
from werkzeug.security import generate_password_hash,check_password_hash
...
class User(UserMixin):
    """用户类"""
    def __init__(self, user):
        self.username = user.get("name")
        self.password_hash = user.get("password")
        self.id = user.get("id")

    def verify_password(self, password):
        """密码验证"""
        if self.password_hash is None:
            return False
        return check_password_hash(self.password_hash, password)

    def get_id(self):
        """获取用户ID"""
        return self.id

    @staticmethod
    def get(user_id):
        """根据用户ID获取用户实体,为 login_user 方法提供支持"""
        if not user_id:
            return None
        for user in USERS:
            if user.get('id') == user_id:
                return User(user)
        return None
加载登录用户

有了用户类,并且实现了 get 方法,就可以实现 login_manageruser_loader 回调函数了,user_loader 的作用是根据 Session 信息加载登录用户,它根据用户 ID,返回一个用户实例:

@login_manager.user_loader  # 定义获取登录用户的方法
def load_user(user_id):
    return User.get(user_id)

登录页面

定义表单类
class UserForm(FlaskForm):
    username = StringField("用户名",validators=[Length(min=1,max=20,message="请输入的字符长度在1~20个字符之间")])
    password = PasswordField("密码",validators=[Length(min=1,max=20,message="请输入的字符长度在1~20个字符之间")])
视图函数
...
from flask_login import login_user
...

@app.route('/login/', methods=["GET", "POST"])
def login():
    form = UserForm(request.form)
    msg = None
    # 如果提交通过验证则返回True
    if request.method == "POST" and form.validate():
    # request.method == "POST" and form.validate() 
    # <==> form.validate_on_submit()
        username = form.username.data
        password = form.password.data
        user_info = get_user(username)  # 从用户数据中查找用户记录
        if user_info is None:
            msg = "该用户不存在"
        else:
            # 创建用户实体
            user = User(user_info)
            # 验证密码
            if user.verify_password(password):
                # 创建用户session
                login_user(user)
                print("重定向url:{}".format(url_for("index")))
                return redirect(url_for("index"))
            else:
                msg = "用户名或密码密码有误"
    return render_template("login.html", form=form, msg=msg)

  • 视图函数同时支持 GETPOST 方法
  • form.validate_on_submit() 可以判断用户是否完整的提交了表单,只对 POST 有效,所以可以用来判断请求方式
  • 如果是 POST 请求,获取提交数据,通过 get_user 方法查找是否存在该用户
  • 如果用户存在,则创建用户实体,并校验登录密码
  • 校验通过后,调用 login_user 方法创建用户 Session,然后跳转到请求参数中 next 所指定的地址或者首页 (不用担心如何设置 next,还记得上面设置的 login_manager.login_view = 'login' 吗? 对,未登录访问时,会跳转到 login,并且带上 next 查询参数)
  • POST 请求,或者未经过验证,会显示 login.html 模板渲染后的结果
模板
<form action="{{ url_for("login") }}" method="post">
    {{ form.csrf_token }}
    <table>
        <tbody>
        <tr>
            <td>{{ form.username.label }}</td>
            <td>{{ form.username() }}</td>
        </tr>
        <tr>
            <td>{{ form.password.label }}</td>
            <td>{{ form.password() }}</td>
        </tr>
        <tr>
            <td></td>
            <td><input id="submit" type="submit" value="提交"/></td>
        </tr>
        </tbody>
    </table>
</form>

需要验证的页面

为了方便演示,将首页作为需要验证的页面,通过验证将看到登录者欢迎信息,页面上还有个登出链接

首页视图函数 index:

from flask_login import login_required,current_user

@app.route('/')  # 首页
@login_required  # 需要登录才能访问
def index():
    return render_template("index.html",username=current_user.username)
  • 注解 @login_required 会做用户登录检测,如果没有登录要方法此视图函数,就被跳转到 login 接入点( endpoint )。
  • current_user 是当前登录者,是 User 的实例,是 Flask-Login 提供全局变量( 类似于全局变量 g )。
  • username 是模板中的变量,可以将当前登录者的用户名传入 index.html 模板。

登出视图函数 logout:

from flask_login import logout_user

@app.route('/logout')  # 登出
@login_required
def logout():
    logout_user()
    return redirect(url_for('login'))
  • 只有登录了才有必要登出,所以加上注解 @login_required。
  • logout_user 方法和 login_user 相反,由于注销用户的 Session
  • 登出视图不需要模板,直接跳转到登录页,实际项目中可以增加一个登出页,展示些有趣的东西。

用户注册

上面的演示了,已存在用户登录的情况,不存在用户需要完成注册才能登录。

注册功能和登录很类似,页面上多了密码确认字段,并且需要验证两次输入的密码是否一致,后台逻辑是:如果用户不存在,且通过检验,将用户数据保存到 USERS 列表中,跳转到 login 页面。

配合sqlalchemy使用

示范

直接让用户模型继承 flask.ext.login.UserMixin类,类中有上面4个方法的默认实现 如:

flask-sqlachemy和sqlchemy这里都一样:

from flask.ext.login import UserMixinclass User(db.Model, UserMixin):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64),unique=True)

Flask-Login要求实现一个回调函数,使用 get_id()方法返回的唯一标识用户的Unicode字符串 作为参数 返回这个用户对象.

如果是继承的UserMixin类, get_id()方法默认返回的用户的id. 如果用户不存在,应该返回None.

此外如果需要定制数据库的nid时,可在上方添加:

def get_id(self):
    return self.nid

默认情况下是查看id的:如果数据库的user表的主键为id,则可不定制get_id()

flask-sqlachemy的语法如下:

from . import loginManager

@loginManager.user_loader
def load_user(user_id):    
    return User.query.get(int(user_id))

获取当前登陆的用户,

from flask.ext.login import current_user
#判断当前用户是否是匿名用户
current_user.is_anonymous()

也可以在模版中使用 {% if current_user.is_authenticated %} 判断

在模版中使用,如果用户已认证就显示他的名字

{% if current_user.is_authenticated %}
 Hi {{ current_user.name }}! 
{% endif %}

current_user.name的name是user表的name

如何控制Flask-Login的session过期时间

在 Flask-Login 中,如果不特殊处理的话,session 是在你关闭浏览器之后就失效的。也就是说每次重新打开页面都是需要重新登录的。

如果需要自己控制 session 的过期时间的话,

  • 首先需要设置 login_manager 的 session类型为永久的,
  • 然后再设置 session 的过期时间
from flask import session
...
session.permanent = True
app.permanent_session_lifetime = timedelta(minutes=5)

还需要注意的是 cookie 的默认有效期:

login_manager.remember_cookie_duration=timedelta(days=1)

使用redis存储session

跳转

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值