背景
昨天在研究Redash中的权限控制逻辑时,老板问了我一个Redash源码相关的问题:login handler中的current_user是从哪儿来的?它里面的相关属性是从哪儿来,在哪儿被设置更新的?
Redash中的login/session的管理使用的是Flask-Login框架(其实Redash的服务端使用了大量的Flask生态的框架)。因为我之前对Flask-Login和Flask-security这两个框架有一些了解,所以就根据我的理解回答了老板的问题。同时趁着这个问题,今天花了两个小时的时间简单探索了一下Flask-Login的基本原理。
Web端登录操作的基本流程
我们大概想一想通常的Web端的用户登录流程:
第一步:用户进入登录页面,通常页面上会显示让用户输入相应的用户名(或者手机号/邮箱等)和密码。此时浏览器和服务端还不知道用户是谁。
第二步:用户填入自己的用户名和密码,然后点击登录按钮顺利登入。很多网站会让用户勾选是否记住登录状态,为了避免用户隔一段时间后再次进入页面时的反复登录操作。
第三步:用户登录成功后,网站通常会跳转到首页或者是链接中带有的重定向页面。这个时候网站和后端已经记录下了用户的身份信息,也就是“知道你是谁了”。接下来用户就可以自由访问内部页面了。
Flask-Login是什么?
我们翻一下Flask-Login的官方文档上对它的基本介绍:
Flask-Login provides user session management for Flask. It handles the
common tasks of logging in, logging out, and remembering your users’
sessions over extended periods of time.
这里写的很直观:Flask-Login是用来进行session管理,处理登录/登出操作的框架。因为session通常有一定的时效性,所以Flask-Login会在过期时间之内记录用户的session信息。所以从命名上通俗的讲,Flask-Login就是用来进行登录操作的一个基于Flask的框架。
从技术角度来讲,使用Flask-Login框架时,上一章节中提到这三步操作都是与Flask-Login直接相关的。我们接下来从登录流程的角度来详细说一下它的基本原理。
登录前访问登录页面时的后端逻辑
当进入登录页面时,通常客户端会GET请求login api(例如“/api/login”)。login api的设计通常会支持两种http方法:GET和POST。GET方法对应于登录之前进入登录页面的handler,POST方法对应进行登录操作的handler。我们以Redash里的login api来举例:
@routes.route(org_scoped_rule("/login"), methods=["GET", "POST"])
@limiter.limit(settings.THROTTLE_LOGIN_PATTERN)
def login(org_slug=None):
# We intentionally use == as otherwise it won't actually use the proxy. So weird :O
# noinspection PyComparisonWithNone
if current_org == None and not settings.MULTI_ORG:
return redirect("/setup")
elif current_org == None:
return redirect("/")
index_url = url_for("redash.index", org_slug=org_slug)
unsafe_next_path = request.args.get("next", index_url)
next_path = get_next_path(unsafe_next_path)
if current_user.is_authenticated:
return redirect(next_path)
if request.method == "POST&#