当前位置: 代码迷 >> 综合 >> 16.flask-login
  详细解决方案

16.flask-login

热度:56   发布时间:2023-09-12 05:39:17.0

Flask-Login

概念

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

会:

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

但是,不会:

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

安装

pip install flask-login

配置

from flask import Flask
from flask_login import LoginManagerapp = 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 userreturn 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 Falsereturn check_password_hash(self.password_hash, password)def get_id(self):"""获取用户ID"""return self.id@staticmethoddef get(user_id):"""根据用户ID获取用户实体,为 login_user 方法提供支持"""if not user_id:return Nonefor 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# 如果提交通过验证则返回Trueif request.method == "POST" and form.validate():# request.method == "POST" and form.validate() # <==> form.validate_on_submit()username = form.username.datapassword = form.password.datauser_info = get_user(username)  # 从用户数据中查找用户记录if user_info is None:msg = "该用户不存在"else:# 创建用户实体user = User(user_info)# 验证密码if user.verify_password(password):# 创建用户sessionlogin_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
  • 登出视图不需要模板,直接跳转到登录页,实际项目中可以增加一个登出页,展示些有趣的东西。

16.flask-login

用户注册

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

注册功能和登录很类似,页面上多了密码确认字段,并且需要验证两次输入的密码是否一致,后台逻辑是:如果用户不存在,且通过检验,将用户数据保存到 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

跳转

  相关解决方案