flask-login模块官网内容整理

Posted on 2020-07-12 08:18  可乐很难瘦  阅读(460)  评论(0编辑  收藏  举报

Flask-Login

官网链接: https://flask-login.readthedocs.io/en/latest/


简介

用于处理Flask的用户会话管理,完成登录,注销,记住用户会话的常见功能。--低级库

可以完成的功能:

  1. 将活跃用户id存储的session中,方便实现登录,退出
  2. 为视图函数添加登录 or 注销状态限制
  3. 实现 记住我 功能
  4. 防止用户sessions被恶意修改

不支持的功能:

  1. 必须自己实现数据库连接,数据存储,用户登录
  2. 限制其不能使用 OpenIDs 等其他验证方式
  3. 只能处理 是否登录的判定
  4. 不能处理账号注册 账号恢复

开始使用

配置LoginManager

from flask import Flask 
from flask-login import LoginManager
app = Flask(__name__)
app.config["SECRET_KEY"] = "BENNY"

# LoginManager 使用sessions来进行身份验证,所以在配置文件中设置 secret_key
login_manager = LoginManager()
login_manager.init_app(app)

如何工作

1, 必须实现 可调的 user_loader, 用于根据用户id获取用户信息,并存入session内。

# 在定义User表的文件中实现该函数
@login_manager.user_loader
def load_user(user_id):
	return User.get(user_id)

2, User 类定义要求

# 必须实现下面方法
is_authenticated:当用户通过了身份认证后,会返回True;只有满足该验证的用户才能访问 login_required修饰的视图函数

is_active: 活跃账户,返回True; 被暂停、禁用的账号返回False

is_anonymous: 匿名用户返回True;注册登录用户返回True

get_id: 该方法必须返回一个唯一标识的用户unicode,可用于从 user_loader返回该用户;该ID必须是unicode,如果是int 或者其他类型,需要转化为 unicode

# 为了实现这样的用户类更方便,可以继承 UserMixin; 该类提供了这些属性与方法的默认实现

example:

class User(UserMixin, db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), unique=True, index=True)
    email = db.Column(db.String(64), unique=True, index=True)

    # Flask-Login integration
    def is_authenticated(self):
        return True

    def is_active(self):
        return True

    def is_anonymous(self):
        return False

    def get_id(self):
        return self.id

    def __repr__(self):
        return '<User %r>' % self.username
    def __str__(self):
        return self.username

登录实例

需要先验证 next参数的值, 否则,容易受到打开 重定向的影响

@app.route("/login", methods=["GET","POST"])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        login_manager.login_user(user)
        
        flask.flash("Logged in seccessfully")
        
        next = flask.request.args.get("next")
        
        if not is_sage_url(next):
            return flask.abort(404)
        
        return falsk.redirect(next or flask.url_for("idnex"))
  	return flask.render_template("login.html", form=from)

在模板中使用 current_user 代理访问登录用户;该代理在每个模板中都可以使用

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

使用 login_required 装饰器来限定用户登录访问的视图

@app.route("/settings")
@login_required
def settings():
    pass

# 注销登录,需要清空会话中的所有Cookie
@app.route("/logout")
@login_required
def logout():
    logout_user()
    return redirect(somewhere)

自定义登录过程

默认情况下,当未登录用户访问 login_required 装饰的页面的时候, flask-login 将flash一条消息并将其重定向到登录视图(如果没有指定登录视图,将返回401错误)

# 绑定登录视图
login_manager.login_view = "users.login"
# 自定义默认闪烁的消息
login_manager.login_messages = u"该页面需要登录后才能访问"
# 自定义消息级别
login_manager.login_message_category = "info"

# 如果想进一步自定义流程,可以使用下面的装饰器功能
@login_manager.unauthorized_handler
def unauthorized():
    return a_response

使用请求加载器自定义登录

不适用Cookie来登录用户,而是使用 header values 或者 api key 作为参数来完成登录,这需要实现 request_loader 回调函数。

@login_manager.request_loader
def load_user_from_request(request):
    # 首先,尝试从url里提取api_key
    api_key = request.args.get("api_key")
    if api_key:
    	user = User.query.filter_by(api_key=api_key).first()
        if user:
            return user
    # 从请求头中获取参数 Authorization
    api_key = request.headers.get("Authorization")
    if api_key:
        api_key = api_key.replace("Basic", "", 1)
        try:
            api_key = base654.b64decode(api_key)
        except TyperError:
            pass
        user = user.query.filter_by(api_key=api_key).first()
        if user:
            return user
    return None

匿名用户

未登录用户需要设置为 AnonymousUserMixin 对象,该对象需要实现的属性与方法

  1. is_active 、is_authenticated 返回False
  2. is_anonymous: return True
  3. get_id: return None

自定义匿名用户的类

class AnonymousUser(AnonymousUserMixin):
    def can(self, permissions):
        return False

    def is_administrator(self):
        return False

    
login_manager.anonymous_user = AnonymousUser

remember Me 功能

只需要将 remember=True 传给login_user 调用即可。

通过配置文件设置Cookie过期时间。

REMEMBER_COOKIE_DURATION

Alternative Tokens 备选tokens

当使用用户id作为token,意味着必须改变用户ID才能使其登录session 无效。一个改进的方法是,通过使用一个替代ID。

该替代ID也必须是唯一的,作为第二个用户ID。

该方法优点: 当用户修改密码后,可以直接更新该替代ID,此时,可以保证旧的ID的登录会话无效(已经查找不到该ID)

@login_manager.user_loader
def load_user(user_id):
    return User.query.filter_by(alternative_id=user_id).first()
    
def get_id(self):
    return unicode(self.alternative_id)

修改密码,重新登录功能(todo ??)

Cookie设置

REMEMBER_COOKIE_NAME 存储“记住我”信息的cookie的名称。默认值: remember_token
REMEMBER_COOKIE_DURATION Cookie过期之前的时间,以datetime.timedelta对象或整数秒为单位。 默认值: 365天(非non历公历年)
REMEMBER_COOKIE_DOMAIN 如果“记住我” cookie应该跨域,请在此处设置域值(即.example.com 允许cookie在的所有子域上使用example.com)。 默认: None
REMEMBER_COOKIE_PATH 将“记住我” cookie限制为特定路径。 默认: /
REMEMBER_COOKIE_SECURE 将“记住我” cookie的范围限制为安全通道(通常为HTTPS)。 默认: None
REMEMBER_COOKIE_HTTPONLY 防止客户端脚本访问“记住我” cookie。 默认: False
REMEMBER_COOKIE_REFRESH_EACH_REQUEST 如果设置为Truecookie,则每次请求都会刷新它,这会增加生命周期。像Flask一样SESSION_REFRESH_EACH_REQUEST默认: False

session 保护

保护session避免被修改。可以设置的保护级别为 None, Basic , Strong。设置的路径,config文件级别优先于login_manager

# 默认情况下,basic被激活
logign_manager.session_protection = "strong"
# 设置None,可以禁用该功能
login_manager.session_protection = None

当使用authentication去验证API的时候,需要去禁用Flask Sesion Cookie。为了实现这个,需要自定义custom session interface,使用一个标识去跳过会话保存功能。

from flask import g
from flask.sessions import SecureCookieSessionInterface
from flask_login import user_loaded_from_header

class CustomSessionInterface(SecureCookieSessionInterface):
    """Prevent creating session from API requests."""
    def save_session(self, *args, **kwargs):
        if g.get('login_via_header'):
            return
        return super(CustomSessionInterface, self).save_session(*args,
                                                                **kwargs)

app.session_interface = CustomSessionInterface()

@user_loaded_from_header.connect
def user_loaded_from_header(self, user=None):
    g.login_via_header = True