Flask-Login

2019/06/03 20:47
阅读数 12

一、安装和配置

1、安装

pip install flask-login

 

2、配置flask-login插件

使用Flask-Login的应用程序中最重要的部分是LoginManager类(登录管理器),然后对app进行配置。

from flask import Flask
from flask_login import LoginManager


app = Flask(__name__)  # 实例化一个flask对象
app.config["SECRET_KEY"] = '雅蠛蝶'  # 设置session的secret key

login_manager = LoginManager()  # 实例化一个登录的管理实例
login_manager.init_app(app)

默认情况下,Flask-Login使用 sessions 进行身份验证。也就是数说你必须在app上设置 secret key,否则Flask会向您显示一条错误消息。

 

3、user_loader 回调函数

user session 记录的是用户 ID (user_id),回调函数的作用就是通过 user_id 返回对应的 User 对象。 把返回的user对象存储到session中。 user_loader 回调函数在 user_id 非法的时候不应该抛出异常,而要返回 None。 没有这个回调函数的话,Flask-Login 将无法工作。
@login_manager.user_loader
def user_loader(user_id):
    from user.models import User  # 导入User表
    user = User.query.get(int(user_id))
    return user

 

4、User类的实现

使用flask-login插件需要User类的实现以下几个属性和方法:

  • is_authenticated:属性,用来判断是否是已经授权了,如果通过授权就会返回true
  • is_active: 属性,判断是否已经激活
  • is_anonymous: 属性,判断是否是匿名用户
  • get_id(): 方法,返回用户的唯一标识
当然,这些属性和方法也可以直接继承于userMixin的默认方法和属性,不用自己去实现。
from flask_login import UserMixin


class User(UserMixin,db.Model):  # db是flask-sqlalchemy的实例
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
    password= db.Column(db.String(128))
    avatar_hash = db.Column(db.String(32))

    def __init__(self,id,username,email):
        self.id = id
        self.username = username
        self.email = email

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

 

二、flask-login的应用

1、登录Demo

# 用户进行身份验证后,你可以使用该login_user 功能登录。

from flask import request
from test.models import User  # 导入User类


@app.route('/login', methods=['GET', 'POST'])
def login():
    # Here we use a class of some kind to represent and validate our
    # client-side form data. For example, WTForms is a library that will
    # handle this for us, and we use a custom LoginForm to validate.
    form = LoginForm()
    if form.validate():
        # Login and validate the user.
        # user should be an instance of your `User` class
        username = request.form.get('username')
        password = request.form.get('password')
        user = User.query.filter_by(User.username==username, User.password==password).first()
        login_user(user)  # 登录
        flask.flash('Logged in successfully.')

        next = flask.request.args.get('next')
        # is_safe_url should check if the url is safe for redirects.
        # See http://flask.pocoo.org/snippets/62/ for an example.
        if not is_safe_url(next):
            return flask.abort(400)

        return flask.redirect(next or flask.url_for('index'))
    return flask.render_template('login.html', form=form)

这里的是登陆的请求操作,通过用户查询,判断是否有权限,然后通过login_user(user),其实也是调用user_loader()把用户设置到session中。

这里的next参数可能有安全问题而不能直接跳转,可以考虑使用is_safe_url去过滤。

 

2、使用flask-login登陆后的一些参数和方法

1.  登陆后的用户,默认提供了current_user的用户代理方法,在每个模板中都可以直接使用:

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

 

2. 需要用户登录的视图可以使用 @login_required 来装饰

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

 

3. 注销用户,logout_user(),这样会清除cookie和session

@app.route("/logout")
@login_required
def logout():
    logout_user()
    return redirect(somewhere)

 

3、自定义登录操作

# 设置登陆视图,用于未授权操作的跳转(即用户未登录时,默认跳转到哪里)
login_manager.login_view = "auth.login"


# 设置快闪消息,用于提示用户
login_manager.login_message = _("Please login to access this page.")

# 可以设置None,'basic','strong'以提供不同的安全等级
login_manager.session_protection = "basic"

# 自定义消息类别
login_manager.login_message_category = "info"
"""
就是在登陆页面的重定向时候,可以携带到登陆请求的参数,例如在一个用户页面,session过期,这是要跳转到登陆页面,那么会把当前的链接当参数存放到next里面传递到登陆请求中。当然了,这里还可以设置USE_SESSION_FOR_NEXT =True 这样就把链接放session里面了;而这些可携带参数链接必须在info的路径下。
"""

 

4、使用Request Loader自定义登录

有时候你想要不使用cookies来登录用户,比如使用头部值或者作为查询参数传递的api键值。 在这些情况下,您应该使用request_loader回调。 这个回调应该和你的user_loader回调一样,只是它接受Flask请求而不是user_id。
举个例子,你可以通过路径的参数或者请求头里携带的Authorzation消息进行验证用户:

@login_manager.request_loader
def load_user_from_request(request):

    # first, try to login using the api_key url arg
    api_key = request.args.get('api_key')
    if api_key:
        user = User.query.filter_by(api_key=api_key).first()
        if user:
            return user

    # next, try to login using Basic Auth
    api_key = request.headers.get('Authorization')
    if api_key:
        api_key = api_key.replace('Basic ', '', 1)
        try:
            api_key = base64.b64decode(api_key)
        except TypeError:
            pass
        user = User.query.filter_by(api_key=api_key).first()
        if user:
            return user

    # finally, return None if both methods did not login the user
    return None

 

5、匿名用户

默认情况下,当用户实际未登录时,current_user 将设置为 AnonymousUserMixin 对象。它具有以下属性和方法:

  • is_active并且is_authenticated是 False
  • is_anonymous 是 True
  • get_id() 回报  None

如果您对匿名用户有自定义要求(例如,他们需要具有权限字段),您可以提供一个可调用的(类或工厂函数),用于创建匿名用户 LoginManager

login_manager.anonymous_user = MyAnonymousUser

6、记住我(remember)

默认情况下,当用户关闭浏览器时,Flask会话将被删除,用户将被注销。“记住我”可防止用户在关闭浏览器时意外退出。但这不意味着记忆或预填充用户的用户名和密码在登录表单的用户注销之后。

“记住我”功能可能很难实现。但是,Flask-Login使它几乎透明 - 只需  remember=True 转到 login_user 呼叫。Cookie将保存在用户的计算机上,然后Flask-Login将自动从该cookie恢复用户ID(如果该cookie不在会话中)。cookie过期前的时间可以使用  REMEMBER_COOKIE_DURATION 配置进行设置, 也可以传递给它 login_user
cookie是防篡改的,因此如果用户篡改它(即插入别人的用户ID代替他们自己的用户ID),cookie就会被拒绝,就好像它不存在一样。

该级别的功能将自动处理。但是,你可以(并且应该,如果你的应用程序处理任何类型的敏感数据)提供额外的基础结构,以提高您记住的cookie的安全性。

 

7、替代Tokens

使用用户ID作为记忆令牌的值意味着您必须更改用户的ID以使其登录会话无效。一种改进方法是使用备用用户ID而不是用户ID。例如:

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

然后,User类下的 get_id 方法将返回替代ID而不是用户的主ID:

def get_id(self): return unicode(self.alternative_id) 

这样,当用户更改密码时,您可以自由地将用户的替代ID更改为新的随机生成的值,这将确保其旧的身份验证会话将不再有效。请注意,替代ID仍必须唯一标识用户...将其视为第二个用户ID。

 

8、Fresh Logins

当用户登录时,他们的会话被标记为“新鲜”,这表示他们实际上在该会话上进行了身份验证。当他们的会话被销毁并且他们用“记住我”cookie重新登录时,它被标记为“非新鲜”login_required 不区分新鲜度,这对大多数页面来说都很好。但是,更改个人信息等敏感操作应该需要重新登录(无论如何,更改密码等操作都需要重新输入密码。)

fresh_login_required,除了验证用户是否已登录外,还将确保其登录是新鲜的如果没有,它会将它们发送到可以重新输入凭据的页面。您可以自定义其行为在相同的方式,你可以自定义login_required,通过设置 LoginManager.refresh_viewneeds_refresh_message以及 needs_refresh_message_category

login_manager.refresh_view = "accounts.reauthenticate"
login_manager.needs_refresh_message = (
    u"To protect your account, please reauthenticate to access this page."
)
login_manager.needs_refresh_message_category = "info"

 

或者通过提供自己的回调来处理刷新

@login_manager.needs_refresh_handler
def refresh():
    # do stuff
    return a_response

要将会话再次标记为新鲜,请调用  flask_login.confirm_login 函数。

 

9、Cookie设置

REMEMBER_COOKIE_NAME

存储“记住我”信息的 cookie 名。 默认值: remember_token

REMEMBER_COOKIE_DURATION

cookie过期时间,为一个 datetime.timedelta 对象。 默认值: 365 天 (1 非闰阳历年)

REMEMBER_COOKIE_DOMAIN

如果“记住我” cookie 应跨域,在此处设置域名值 (即 .example.com 会允许 example 下所有子域 名)。 默认值: None

REMEMBER_COOKIE_PATH

限制”记住我“ cookie 存储到某一路径下。 默认值: /

REMEMBER_COOKIE_SECURE

 限制 “Remember Me” cookie 在某些安全通道下有用 (典型地 HTTPS)。默认值: None 

REMEMBER_COOKIE_HTTPONLY

保护 “Remember Me” cookie 不能通过客户端脚本访问。 默认值: False

REMEMBER_COOKIE_REFRESH_EACH_REQUEST

如果设置为True,cookie则会在每次请求时刷新,这会破坏生命周期,默认值:False

 

10、会话保护

虽然上述功能有助于保护您的“记住我”令牌免受Cookie窃贼的攻击,但会话Cookie仍然容易受到攻击。Flask-Login包括会话保护,以帮助防止用户的会话被盗。

你可以LoginManager在应用程序的配置中配置会话保护如果启用,它可以在任一 模式basicstrong模式下运行。要将其设置为LoginManager,请将session_protection属性设置 "basic""strong"

login_manager.session_protection = "strong"

或者,禁用它:

login_manager.session_protection = None

默认情况下,它在"basic"模式下激活它可以在应用程序的配置通过设置被禁用SESSION_PROTECTION设置None, "basic""strong"

当会话保护处于活动状态时,每个请求都会为用户的计算机生成一个标识符(基本上是IP地址和用户代理的安全散列)。如果会话没有关联的标识符,则将存储生成的标识符。如果它有一个标识符,并且它与生成的标识符匹配,那么请求就可以了。

如果标识符在basic模式下不匹配,或者会话是永久性的,则会话将被简单地标记为非新鲜,并且任何需要重新登录的内容将强制用户重新进行身份验证。(当然,您必须已经在适当的时候使用新的登录才能产生效果。)

如果标识符在strong非永久会话的模式下不匹配,则删除整个会话(以及记忆标记,如果存在)。

 

11、禁用API的会话

在对API进行身份验证时,您可能希望禁用设置Flask会话cookie。为此,请使用自定义会话接口,该接口根据您在请求中设置的标志跳过保存会话。例如:

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

 

12、本地化

默认情况下,LoginManager用于flash在需要用户登录时显示消息。这些消息是英文的。如果您需要本地化,请在发送之前将这些消息localize_callback属性设置LoginManager为要调用的函数flash,例如gettext将使用消息调用此函数,并将其返回值发送给flash

 

 

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部