Z.S.K.'s Records

Flask学习(flask-login使用)

近期在做一个数据库后台管理系统,功能非常简单,就是定时获取oracle数据库的awr性能报告数据,结合highcharts生成图表.因为之前已经用python写了个模块,这次有个功能刚好可以直接用,就也直接想用python写了,这种小系统当然是用python的flask框架了,简单轻便,项目中有用到用户登陆功能,使用了flask-login,对这种内部小型的不需要太复杂权限应用系统实乃必备

flask-login

Flask-Login 为 Flask 提供了用户会话管理,它处理了日常的登入、登出并且长时间记住用户的会话,主要使用的是session机制

它可以轻松实现以下功能:

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

但是,它不会:

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

使用它也是非常简单

  1. 引用flask-login
  2. 在应用中配置login_manager
  3. 实现User(db.Model,UserMixin)
  4. 完成load_user(user_id)回调函数
  5. login_user保存用户信息
  6. 使用@login_required

当然,以上几点并不是它的顺序,而是各自对应一种操作,下面一一介绍:

初始化flask-login

1
2
3
4
5
6
7
8
from flask_login import UserMixin,LoginManager,login_user,logout_user,login_required

login_manager = LoginManager() ####LoginManager对象
login_manager.login_view = "login" ####表示如果用户没有登陆时跳转指向的路由
login_manager.session_protection = "strong" ####session的保护等级,如果发现异常会登出用户
login_manager.login_message = "请先登陆" ####用户没有登陆时访问需要登陆才能访问的页面时的提示
login_manager.login_message_category = "warning" ####上述提示的提示等级,跟flask的flash消息分类等同
login_manager.init_app(app) ####初始化

登录管理(login manager)包含了让你的应用和 Flask-Login 协同工作的代码,比如怎样从一个 ID 加载用户,当用户需要登录的时候跳转到哪里等等。一旦实际的应用对象创建后,就可以使用最后一句话在应用中初始化了

实现User类

使用flask-login验证用户登录时需要如下属性:

  1. is_authenticated

当用户通过验证时,也即提供有效证明时返回 True .(只有通过验证的用户会满足 login_required 的条件)

  1. is_active

    如果这是一个活动用户且通过验证,账户也已激活,未被停用,也不符合任何你 的应用拒绝一个账号的条件,返回 True .不活动的账号可能不会登入(当然, 是在没被强制的情况下)

  2. is_anonymous

    如果是一个匿名用户,返回 True (真实用户应返回 False)

  3. get_id()

    返回一个能唯一识别用户的,并能用于从 user_loader 回调中加载用户的 unicode .注意着 必须 是一个 unicode,如 果 ID 原本是 一个 int 或其它类型,你需要把它转换为 unicode.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class User(db.Model,UserMixin):
__tablename__ = 'XT_REGIST_USER'
rowid = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), unique=True)
password = db.Column(db.String(255))

def __init__(self, rowid=None, username=None, password=None):
self.rowid = rowid
self.username = username
self.password = self.set_password(password)

def set_password(self, password):
return generate_password_hash(password,method="pbkdf2:sha1:100",salt_length=2)

def check_password(self, password):
return check_password_hash(self.password, password)

def is_active():
return True

def is_anonymous(self):
return False

def get_id(self):
return self.rowid

####登陆视图
@app.route("/login/",methods=["GET","POST"])
def login():
if request.method == "POST":
me = User.query.filter_by(username=request.form.get('username')).first()
if me and me.check_password(request.form.get('password')):
session["spasswd"] = request.form.get('password')
login_user(me,remember=request.form.get('rememberme'))
return redirect(url_for("online",_external=True) or request.args.get("next"))
else:
flash("用户不存在或密码错误",'warning')
return render_template("login.html")

这里我们是直接继承UserMixin,它提供了对所有这些方法的默认 实现,当然可以自己实现用户类的.通过werkzeug.security包中的generate_password_hash与check_password_hash来实现密码的加密解密.

我们看到在login视图中,先通过用户名在库中查找到该用户信息(注意这里使用了User类),如果存在该用户而且输入的密码也正确,那么就使用login_user()函数来登录用户,这时用户在会话中的状态就是登录状态了

remember_me

“记住我”的功能很难实现,但是,Flask-Login 几乎透明地实现它 - 只要把 remember=True 传递给 login_user。一个 cookie 将会存储在用户计算机中,如果用户会话中没有用户 ID 的话,Flask-Login 会自动地从 cookie 中恢复用户 ID。cookie 是防纂改的,因此如果用户纂改过它(比如,使用其它的一些东西来代替用户的 ID),它就会被拒绝,就像不存在。该层功能是被自动实现的。但你能(且应该,如果你的应用处理任何敏感的数据)提供 额外基础工作来增强你记住的 cookie 的安全性

load_user()

1
2
3
@login_manager.user_loader
def load_user(rowid):
return User.query.get(int(rowid))

这是一个回调函数,根据session中存储的rowid来reload User object,返回User对象,第一行,其实是一个装饰器,这个装饰器表示,每次有请求时都会调用它所装饰的函数,说的更直白一点就是,每次请求到来时,都会调用这个方法,检查id是否在当前session中,如果存在返回id对应的User给login_user(),不存在则返回None.这里有更详细的说明flask-login: can’t understand how it works

@login_required

如果需要让页面只可让已登陆的用户访问,可使用login_required装饰路由函数,未登陆的请求将会跳转到上面loginManager.login_view设置的登陆页面路由

logout_user()

1
2
3
4
@app.route('/logout/',methods=['GET', 'POST'])
def logout():
logout_user()
return redirect(url_for('index'))

logout_user()函数功能就是将缓存的用户信息清楚,将Remember me标记位设置为清空状态

current_user.is_authenticated()

current_user.is_authenticated()是用来获取当前登陆用户的,可直接在模板中判断用户级别

1
2
3
{% if current_user.is_authenticated() and current_user.username == 'Admin' %}
Hi {{ current_user.name }}
{% endif %}

参考文章:

转载请注明原作者: 周淑科(https://izsk.me)


 wechat
Scan Me To Read on Phone
I know you won't do this,but what if you did?