pip install flask-loginhtml
接下来建立扩展对象实例:算法
from flask import Flask from flask_login import LoginManager app = Flask(__name__) login_manager = LoginManager(app)
同时,你能够对LoginManager对象赋上配置参数:数据库
# 设置登陆视图的名称,若是一个未登陆用户请求一个只有登陆用户才能访问的视图, # 则闪现一条错误消息,并重定向到这里设置的登陆视图。 # 若是未设置登陆视图,则直接返回401错误。 login_manager.login_view = 'login' # 设置当未登陆用户请求一个只有登陆用户才能访问的视图时,闪现的错误消息的内容, # 默认的错误消息是:Please log in to access this page.。 login_manager.login_message = 'Unauthorized User' # 设置闪现的错误消息的类别 login_manager.login_message_category = "info"
使用Flask-Login以前,你须要先定义用户类,该类必须实现如下三个属性和一个方法:flask
属性 is_authenticated安全
当用户登陆成功后,该属性为True。session
属性 is_activeapp
若是该用户帐号已被激活,且该用户已登陆成功,则此属性为True。eclipse
属性 is_anonymous函数
是否为匿名用户(未登陆用户)。ui
方法 get_id()
每一个用户都必须有一个惟一的标识符做为ID,该方法能够返回当前用户的ID,这里ID必须是Unicode。
由于每次写个用户类很麻烦,Flask-Login提供了”UserMixin”类,你能够直接继承它便可:
from flask_login import UserMixin class User(UserMixin): pass
在编写登陆登出视图前,咱们要先写一个加载用户对象的方法。它的功能是根据传入的用户ID,构造一个新的用户类的对象。为了简化范例,咱们不引入数据库,而是在列表里定义用户记录。
# 用户记录表 users = [ {'username': 'Tom', 'password': '111111'}, {'username': 'Michael', 'password': '123456'} ] # 经过用户名,获取用户记录,若是不存在,则返回None def query_user(username): for user in users: if user['username'] == username: return user # 若是用户名存在则构建一个新的用户类对象,并使用用户名做为ID # 若是不存在,必须返回None @login_manager.user_loader def load_user(username): if query_user(username) is not None: curr_user = User() curr_user.id = username return curr_user
上述代码中,经过”@login_manager.user_loader”装饰器修饰的方法,既是咱们要实现的加载用户对象方法。它是一个回调函数,在每次请求过来后,Flask-Login都会从Session中寻找”user_id”的值,若是找到的话,就会用这个”user_id”值来调用此回调函数,并构建一个用户类对象。所以,没有这个回调的话,Flask-Login将没法工做。
有一个问题,启用Session的话必定须要客户端容许Cookie,由于Session ID是保存在Cookie中的,若是Cookie被禁用了怎么办?那咱们的应用只好经过请求参数将用户信息带过来,通常状况下会使用一个动态的Token来表示登陆用户的信息。此时,咱们就不能依靠”@login_manager.user_loader”回调,而是使用”@login_manager.request_loader”回调。
from flask import request # 从请求参数中获取Token,若是Token所对应的用户存在则构建一个新的用户类对象 # 并使用用户名做为ID,若是不存在,必须返回None @login_manager.request_loader def load_user_from_request(request): username = request.args.get('token') if query_user(username) is not None: curr_user = User() curr_user.id = username return curr_user
为了简化代码,上面的例子就直接使用用户名做为Token了,实际项目中,你们仍是要用一个复杂的算法来验证Token。
一切准备就绪,咱们开始实现登陆视图:
from flask import render_template, redirect, url_for, flash from flask_login import login_user @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': username = request.form.get('username') user = query_user(username) # 验证表单中提交的用户名和密码 if user is not None and request.form['password'] == user['password']: curr_user = User() curr_user.id = username # 经过Flask-Login的login_user方法登陆用户 login_user(curr_user) # 若是请求中有next参数,则重定向到其指定的地址, # 没有next参数,则重定向到"index"视图 next = request.args.get('next') return redirect(next or url_for('index')) flash('Wrong username or password!') # GET 请求 return render_template('login.html')
上述代码同以前Login视图最大的不一样就是你在用户验证经过后,须要调用Flask-Login扩展提供的”login_user()”方法来让用户登陆,该方法需传入用户类对象。这个”login_user()”方法会帮助你操做用户Session,而且会在请求上下文中记录用户信息。另外,在具体实现时,建议你们对”next”参数值做验证,避免被URL注入攻击。
“login.html”模板很简单,就是显示一个用户名密码的表单:
<!doctype html> <title>Login Sample</title> <h1>Login</h1> {% with messages = get_flashed_messages() %} <div>{{ messages[0] }}</div> {% endwith %} <form action="{{ url_for('login') }}" method="POST"> <input type="text" name="username" id="username" placeholder="Username"></input> <input type="password" name="password" id="password" placeholder="Password"></input> <input type="submit" name="submit"></input> </form>
接下来,让咱们写个index视图
from flask_login import current_user, login_required @app.route('/') @login_required def index(): return 'Logged in as: %s' % current_user.get_id()
装饰器”@login_required”确保只有登陆用户才能访问这个index视图,Flask-Login帮咱们实现了这个装饰器。若是用户未登陆,它就会将页面重定向到登陆视图,也就是咱们在第一节中配置的”login_manager.login_view”的视图。
同时,重定向的地址会自动加上”next”参数,参数的值是当前用户请求的地址,这样,登陆成功后就会跳转回当前视图。能够看到咱们对于用户登陆所须要的操做,这个装饰器基本都实现了
Flask-Login还提供了”current_user”代理,能够访问到登陆用户的用户类对象。咱们在模板中也可使用这个代理。让咱们再写一个home视图:
@app.route('/home') @login_required def home(): return render_template('hello.html')
模板代码以下:
<!doctype html> <title>Login Sample</title> {% if current_user.is_authenticated %} <h1>Hello {{ current_user.get_id() }}!</h1> {% endif %}
在上面的模板代码中,咱们直接访问了”current_user”对象的属性和方法。
登出视图也很简单,Flask-Login提供了”logout_user()”方法来帮助你清理用户Session。
from flask_login import logout_user @app.route('/logout') @login_required def logout(): logout_user() return 'Logged out successfully!'
“@login_required”装饰器对于未登陆用户访问的默认处理是重定向到登陆视图,若是咱们不想它这么作的话,能够自定义处理方法:
@login_manager.unauthorized_handler def unauthorized_handler(): return 'Unauthorized'
这个”@login_manager.unauthorized_handler”装饰器所修饰的方法就会代替”@login_required”装饰器的默认处理方法。有了上面的代码,当未登陆用户访问index视图时,页面就会直接返回”Unauthorized”信息。
在登陆视图中,调用”login_user()”方法时,传入”remember=True”参数,便可实现“记住我”功能:
... login_user(curr_user, remember=True) ...
Flask-Login是经过在Cookie实现的,它会在Cookie中添加一个”remember_token”字段来记住以前登陆的用户信息,因此禁用Cookie的话,该功能将没法工做。
当用户经过帐号和密码登陆后,Flask-Login会将其标识为Fresh登陆,即在Session中设置”_fresh”字段为True。而用户经过Remember Me自动登陆的话,则不标识为Fresh登陆。对于”@login_required”装饰器修饰的视图,是否Fresh登陆均可以访问,可是有些状况下,咱们会强制要求用户登陆一次,好比修改登陆密码,这时候,咱们能够用”@fresh_login_required”装饰器来修饰该视图。这样,经过Remember Me自动登陆的用户,将没法访问该视图:
from flask_login import fresh_login_required @app.route('/home') @fresh_login_required def home(): return 'Logged in as: %s' % current_user.get_id()
Flask-Login自动启用会话保护功能。对于每一个请求,它会验证用户标识,这个标识是由客户端IP地址和User Agent的值经SHA512编码而来。在用户登陆成功时,Flask-Login就会将这个值保存起来以便后续检查。默认的会话保护模式是”basic”,为了增强安全性,你能够启用强会话保护模式,方法是配置LoginManager实例对象中的”session_protection”属性:
login_manager.session_protection = "strong"
在”strong”模式下,一旦用户标识检查失败,便会清空所用Session内容,而且Remember Me也失效。而”basic”模式下,只是将登陆标为非Fresh登陆。你还能够将”login_manager.session_protection”置为None来取消会话保护。