flask-login API 详解

来源:互联网 发布:jsp点击按钮执行java 编辑:程序博客网 时间:2024/06/06 01:20
<div id="cnblogs_post_body" class="blogpost-body"><p>本篇博文跟上一篇<a href="http://www.cnblogs.com/alima/p/5796298.html" target="_blank">[Python][flask][flask-wtf]关于flask-wtf中API使用实例教程</a>有莫大的关系。</p>
<p>&nbsp;</p>
<p>简介:Flask-Login 为 Flask 提供了用户会话管理。它处理了日常的登入,登出并且长时间记住用户的会话。</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; 直白的讲,flask-login包为用户管理了涉及到用户登录相关的缓存(Session)管理。</p>
<p>&nbsp;</p>
<p>Posted by&nbsp;<a href="http://www.cnblogs.com/alima/" target="_blank">Alima | cnblogs</a>.</p>
<p>一.安装(Install)</p>
<p>PC环境:Windows 7,Python 3.5.2。</p>
<p>PS:此次配置环境阶段和<a href="http://www.cnblogs.com/alima/p/5796298.html" target="_blank">上一篇博文</a>中写的一致,如果看了上一篇博文,安装阶段可以直接跳过。</p>
<ul>
<li>创建wtfdemo虚拟运行环境</li>
</ul>
<p>用控制台(管理员运行模式)进入(cd)到想要创建工程的路径下,创建wtfdemo文件夹。</p>
<div class="cnblogs_code">
<pre>mkdir wtfdemo</pre>
</div>
<p>进入(cd)wtfdemo文件夹,创建Python虚拟运行环境。</p>
<div class="cnblogs_code">
<pre>virtualenv flaskr</pre>
</div>
<p>出现如下字样,说明虚拟环境创建成功</p>
<p><img src="http://images2015.cnblogs.com/blog/989365/201608/989365-20160822163656011-830814384.png" alt=""></p>
<p>PS:本次提供第二种创建Python虚拟运行环境的使用方法</p>
<div class="cnblogs_code">
<pre>virtualenv -p source_file\python.exe target_file</pre>
</div>
<p>为什么提出第二种创建方法,你会发现,当你的Python Web程序开发多了以后,PC上难免安装了很多版本的Python运行环境。</p>
<p>举例:当PC上同时安装了Python2.7和Python3.5,运行virtualenv flaskr后,建立的Python虚拟运行环境是Python2.7版本的,但是我们的开发环境是Python3.5。</p>
<p>在控制台中输入一下指令,你就会发现问题。</p>
<div class="cnblogs_code">
<pre>virtualenv -h</pre>
</div>
<p>出现下面图片显示,默认的virtualenv安装路径是Python2.7,也就是默认的安装的虚拟环境是Python2.7版本。</p>
<p><img src="http://images2015.cnblogs.com/blog/989365/201608/989365-20160822164255495-1461527434.png" alt=""></p>
<p>所以,在这种情况下,请指定你需要的Python版本,并建立虚拟运行环境。</p>
<ul>
<li>安装flask-wtf库文件</li>
</ul>
<p>进入Python虚拟运行环境(在上一篇博文中写过),执行以下指令。</p>
<div class="cnblogs_code">
<pre>pip install flask
pip install flask-wtf
pip install flask-login<br>pip install flask-sqlalchemy</pre>
</div>
<p>出现如下图所示,说明安装成功。</p>
<p>flask安装成功。</p>
<p><img src="http://images2015.cnblogs.com/blog/989365/201608/989365-20160822165748839-1077905201.png" alt=""></p>
<p>flask-wtf安装成功。</p>
<p><img src="http://images2015.cnblogs.com/blog/989365/201608/989365-20160822165859386-1640822155.png" alt=""></p>
<p>flask-login安装成功。(本次使用flask-wtf库时需要辅助以该运行库)</p>
<p><img src="http://images2015.cnblogs.com/blog/989365/201608/989365-20160822170051151-141179222.png" alt=""></p>
<p>flask-sqlalchemy安装成功。</p>
<p><img src="http://images2015.cnblogs.com/blog/989365/201608/989365-20160822170328339-764576502.png" alt=""></p>
<p>至此,环境配置阶段结束。</p>
<p>&nbsp;</p>
<p>二.flask-Login介绍</p>
<p>它会:</p>
<p>①将活跃用户的ID储存在缓存(Session)中,让登录和注销更加简单。<br>②让你对登入(或登出)的用户限制浏览不同的视图<br>③处理略棘手的“记住用户”功能<br>④帮助保护使用用户的缓存(Session),以免被恶意盗用<br>⑤可能与Flask-Principal或其他授权扩展,在以后的工作中进行整合</p>
<p>但是,它不会:</p>
<p>①强制让你使用一个特定的数据库或者其他的存储方法。你可以完全负责你的用户时如何加载的。<br>②限制你使用用户名和密码,OpenID或是其他的认证方法。<br>③处理“登入或登出”以外的权限<br>④处理用户注册或者账户恢复</p>
<p>总结,flask-Login包只管理用户登入登出的缓存(Session)管理,而不做过多的超出自己权限的功能。</p>
<p>&nbsp;</p>
<p>三.flask-login下的API介绍</p>
<p>由于flask-login中的API比较少,在本文中,尽可能将所有的API功能介绍列在这里。</p>
<ul>
<li>LoginManager</li>
























































</ul>
<div class="cnblogs_code">
<pre><span style="color: #0000ff;">class</span> flask_login.LoginManager(app=None, add_context_processor=True)</pre>
</div>
<p>这个类是用来保存用户的登录状态的,也是flask-login包的主类。</p>
<p>官方:LoginManager的实例是“不”绑定到特定的应用程序的,所以你可以创建LoginManager对象在你的代码主体上,所以你可以创建应用程序在工厂函数中。</p>
<p>解析:我们首先看LoginManager类的构造函数</p>
<div class="cnblogs_code">
<pre><span style="color: #800080;">__init__</span>(self, app=None, add_context_processor=True)</pre>
</div>
<p>该构造函数提供了一个缺省的局部变量app,默认值为None.和上文说道的,为“不绑定到特定的应用程序”解释说明。也就是说,你可以直接定义LoginManager的实例,而不必去绑定到应用程序上才可以实例化。当你在代码主题中定义了app后,可以随时绑定到LoginManager上。</p>
<p>该构造函数初始化了一些列变量。</p>
<div class="cnblogs_code"><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a href="javascript:void(0);" onclick="copyCnblogsCode(this)" title="复制代码"><img src="//common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div>
<pre><span style="color: #800000;">'''</span><span style="color: #800000;">
LoginManager构造函数<br>Blog:   <a href="http://www.cnblogs.com/alima/">www.cnblogs.com/alima/</a>
Editor: Alima | <a href="http://www.cnblogs.com/alima/" target="_blank">cnblog<br></a></span><span style="color: #800000;">'''</span>


<span style="color: #008000;">#</span><span style="color: #008000;">: 提供了一个游客用户,该用户是在没有登录状态下时使用</span>
self.anonymous_user =<span style="color: #000000;"> AnonymousUserMixin


</span><span style="color: #008000;">#</span><span style="color: #008000;">: 提供当用户需要登录时的重定向页面参数</span><span style="color: #008000;">
#</span><span style="color: #008000;">: (此处也可以是一个绝对路径)</span>
self.login_view =<span style="color: #000000;"> None


</span><span style="color: #008000;">#</span><span style="color: #008000;">: 提供一个蓝图,当用户需要登录时的重定向页面</span><span style="color: #008000;">
#</span><span style="color: #008000;">: 如果键值为空,则默认重定向到login_view下的地址</span>
self.blueprint_login_views =<span style="color: #000000;"> {}


</span><span style="color: #008000;">#</span><span style="color: #008000;">: 这条消息将flash在重定向页面上</span>
self.login_message =<span style="color: #000000;"> LOGIN_MESSAGE


</span><span style="color: #008000;">#</span><span style="color: #008000;">: login_message的类型,默认LOGIN_MESSAGE_CATEGORY,可自定义</span>
self.login_message_category =<span style="color: #000000;"> LOGIN_MESSAGE_CATEGORY


</span><span style="color: #008000;">#</span><span style="color: #008000;">: 如果需要一个活跃的登录,使用户重定向到其他界面,重新验证登录</span>
self.refresh_view =<span style="color: #000000;"> None


</span><span style="color: #008000;">#</span><span style="color: #008000;">: 这条消息将flash到用户重新验证的界面上</span>
self.needs_refresh_message =<span style="color: #000000;"> REFRESH_MESSAGE


</span><span style="color: #008000;">#</span><span style="color: #008000;">: needs_refresh_message的类型,默认REFRESH_MESSAGE_CATEGORY,可自定义</span>
self.needs_refresh_message_category =<span style="color: #000000;"> REFRESH_MESSAGE_CATEGORY


</span><span style="color: #008000;">#</span><span style="color: #008000;">: 缓存(Session)的保护等级</span><span style="color: #008000;">
#</span><span style="color: #008000;">: 有三种等级,分别为: 'basic'(默认),'strong','None'(关闭缓存保护功能)</span>
self.session_protection = <span style="color: #800000;">'</span><span style="color: #800000;">basic</span><span style="color: #800000;">'</span>


<span style="color: #008000;">#</span><span style="color: #008000;">: 如果存在,则用来转换使用login_message和needs_refresh_message</span>
self.localize_callback =<span style="color: #000000;"> None


</span><span style="color: #008000;">#</span><span style="color: #008000;">: 检索用户对象回调</span>
self.user_callback =<span style="color: #000000;"> None


</span><span style="color: #008000;">#</span><span style="color: #008000;">: 未经授权的用户回调(未登录状态)</span>
self.unauthorized_callback =<span style="color: #000000;"> None


</span><span style="color: #008000;">#</span><span style="color: #008000;">: 未经授权的用户回调(需要活跃登录状态)</span>
self.needs_refresh_callback =<span style="color: #000000;"> None


</span><span style="color: #008000;">#</span><span style="color: #008000;">: 默认属性以检索用户的Unicode标识(源码注释在flask_login.config文件下)</span>
self.id_attribute =<span style="color: #000000;"> ID_ATTRIBUTE


</span><span style="color: #008000;">#</span><span style="color: #008000;">: 用于回调检索用户对象。(设置令牌状态)</span>
self.header_callback =<span style="color: #000000;"> None


</span><span style="color: #008000;">#</span><span style="color: #008000;">: 用于回调检索用户对象。(设置Flask请求对象状态)</span>
self.request_callback =<span style="color: #000000;"> None


</span><span style="color: #008000;">#</span><span style="color: #008000;">: 如果应用程序(app)为空,则默认创建一个空的LoginManager实例</span>
<span style="color: #0000ff;">if</span> app <span style="color: #0000ff;">is</span> <span style="color: #0000ff;">not</span><span style="color: #000000;"> None:
    self.init_app(app, add_context_processor)</span></pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a href="javascript:void(0);" onclick="copyCnblogsCode(this)" title="复制代码"><img src="//common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div></div>
<p>API:init_app()</p>
<div class="cnblogs_code">
<pre>init_app(self, app, add_context_processor=True)</pre>
</div>
<p>配置的应用程序。这将注册一个'after_request`调用,并附加这个`LoginManager`把它作为'app.login_manager`。</p>
<p>这里渗透一下,三个flask架构自带的装饰器。</p>
<p><code>before_request</code>&nbsp;:在请求收到之前绑定一个函数做一些事情。</p>
<p><code>after_request</code>: 每一个请求之后绑定一个函数,如果请求没有异常。</p>
<p><code>teardown_request</code>: 每一个请求之后绑定一个函数,即使遇到了异常。</p>
<ul>
<li>重新定制你的用户类</li>
</ul>
<p>在之前的章节中,分别定义了不同的用户类,flask-login包控制登入登出需要如下属性。</p>
<p>①&nbsp;is_authenticated</p>
<p>当用户通过验证时,返回有效凭证True。</p>
<p>②&nbsp;is_active</p>
<p>一个活动用具有1)通过验证 2)账户也已激活 3)未被停用 4)也不符合任何的应用拒绝登入条件,返回True。不活动的账号无法登入。</p>
<p>③ is_anonyous</p>
<p>如果是一个匿名用户,返回True,如果是登录用户则返回False</p>
<p>④ get_id()</p>
<p>返回一个能唯一识别用户的,并能用于从user_loader回调中加载用户的<span style="font-family: monospace;">ID,这个ID必须是Unicode</span>。</p>
<p>如果你对你的新定制的用户类没有其他的要求,你可以继承flask_login.<span class="final-path">mixins下定义的UserMixin作为你的用户类的父类。</span></p>
<ul>
<li><span class="final-path">flask-login下的API详解</span></li>
</ul>
<p><span class="final-path">① </span>配置登录</p>
<p>API:@user_loader</p>
<p>解析:这个API设置一个回调,用于从缓存中加载用户登录信息。你构造的函数需要设置一个user ID并且返回一个用户实体。如果用户不存在则返回None。</p>
<p>使用举例(简单实现了一个加载用户的操作,并没有对入口变量做判断):</p>
<div class="cnblogs_code"><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a href="javascript:void(0);" onclick="copyCnblogsCode(this)" title="复制代码"><img src="//common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div>
<pre><span style="color: #800000;">'''<br>Editor: Alima | <a href="http://www.cnblogs.com/alima/" target="_blank"><span style="color: #800000;">cnblog<br></span></a>'''</span></pre>
<pre><span style="color: #000000;">@login_manager.user_loader
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> load_user(id):
    user </span>= User.query.filter_by(id=<span style="color: #000000;">id).first()
    </span><span style="color: #0000ff;">return</span> user</pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a href="javascript:void(0);" onclick="copyCnblogsCode(this)" title="复制代码"><img src="//common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div></div>
<p>API:@request_loader(@header_loader)</p>
<p>解析:设置一个从flask请求加载用户的回调。你需要给函数一个Flask请求,函数返回用户实体,用户不存在则返回None。</p>
<p>使用实例:(来自官方文档的一段代码,很有启迪,<strong>这段代码欢迎探讨</strong>,<a href="http://www.cnblogs.com/alima/" target="_blank">博主</a>理解不是很好。</p>
<div class="cnblogs_code"><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a href="javascript:void(0);" onclick="copyCnblogsCode(this)" title="复制代码"><img src="//common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div>
<pre><span style="color: #800000;">'''<br>Blog: www.cnblogs.com/alima/<br>From: Offical Doc</span>
<span style="color: #800000;">'''</span><span style="color: #000000;">


@login_manager.request_loader
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> load_user_from_request(request):


    </span><span style="color: #008000;">#</span><span style="color: #008000;"> first, try to login using the api_key url arg</span>
    api_key = request.args.get(<span style="color: #800000;">'</span><span style="color: #800000;">api_key</span><span style="color: #800000;">'</span><span style="color: #000000;">)
    </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> api_key:
        user </span>= User.query.filter_by(api_key=<span style="color: #000000;">api_key).first()
        </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> user:
            </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> user


    </span><span style="color: #008000;">#</span><span style="color: #008000;"> next, try to login using Basic Auth</span>
    api_key = request.headers.get(<span style="color: #800000;">'</span><span style="color: #800000;">Authorization</span><span style="color: #800000;">'</span><span style="color: #000000;">)
    </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> api_key:
        api_key </span>= api_key.replace(<span style="color: #800000;">'</span><span style="color: #800000;">Basic </span><span style="color: #800000;">'</span>, <span style="color: #800000;">''</span>, 1<span style="color: #000000;">)
        </span><span style="color: #0000ff;">try</span><span style="color: #000000;">:
            api_key </span>=<span style="color: #000000;"> base64.b64decode(api_key)
        </span><span style="color: #0000ff;">except</span><span style="color: #000000;"> TypeError:
            </span><span style="color: #0000ff;">pass</span><span style="color: #000000;">
        user </span>= User.query.filter_by(api_key=<span style="color: #000000;">api_key).first()
        </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> user:
            </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> user


    </span><span style="color: #008000;">#</span><span style="color: #008000;"> finally, return None if both methods did not login the user</span>
    <span style="color: #0000ff;">return</span> None</pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a href="javascript:void(0);" onclick="copyCnblogsCode(this)" title="复制代码"><img src="//common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div></div>
<p>API:anonymous_user</p>
<p>解析:提供了一个游客用户,该用户是在没有登录状态下时使用</p>
<p>使用实例:(你可以判断当前用户的is_anonymous属性,是不是True来看是不是随机用户)</p>
<div class="cnblogs_code">
<pre><span style="color: #0000ff;">if</span> current_user.is_anonymous == True</pre>
</div>
<p>② 未经授权的配置</p>
<p><span style="line-height: 1.5;">API: flask_login.</span><span style="line-height: 1.5;">login_view</span></p>
<p>解析:当用户需要登录时,将用户重定向到的界面。</p>
<p>使用举例(将非法登录强制重定向到登录界面):</p>
<div class="cnblogs_code">
<pre>login_manager.login_view = <span style="color: #800000;">'</span><span style="color: #800000;">login</span><span style="color: #800000;">'</span></pre>
</div>
<p>API: flask_login.login_message</p>
<p>解析:当用户重定向时,flash出的消息。</p>
<p>使用举例:</p>
<div class="cnblogs_code">
<pre>login_manager.login_message = <span style="color: #800000;">'</span><span style="color: #800000;">Please enter your account</span><span style="color: #800000;">'</span></pre>
</div>
<p><span style="line-height: 1.5;">API:@unauthorized_handler</span></p>
<p>解析:如果你不想用默认的重定向定制你的登录,你可以在代码视图实体中显示的写出一个函数体,将@unauthorized_handler装饰器添加到函数体之上。这样做之后,当你用@login_required阻止用户非法登录,将执行我们新定义的函数体中的内容。</p>
<p>使用举例(将非法登录强制重定向到登录界面):</p>
<div class="cnblogs_code"><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a href="javascript:void(0);" onclick="copyCnblogsCode(this)" title="复制代码"><img src="//common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div>
<pre><span style="color: #800000;">'''
Editor: Alima | <a href="http://www.cnblogs.com/alima/" target="_blank"><span style="color: #800000;">cnblog<br></span></a>'''</span></pre>
<pre><span>@login_manager.unauthorized_handler
def<span> unauthorized_handler():
    return redirect('/login')</span></span></pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a href="javascript:void(0);" onclick="copyCnblogsCode(this)" title="复制代码"><img src="//common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div></div>
<p>③需要活跃登录的配置</p>
<p>API: flask_login.refresh_view</p>
<p>解析:当用户需要活跃登录时,将用户重定向到的界面。</p>
<p>使用举例(将非法登录强制重定向到登录界面):</p>
<div class="cnblogs_code">
<pre>login_manager.refresh_view = 'login'</pre>
</div>
<p>&nbsp;API:&nbsp;flask_login.needs_refresh_message</p>
<p>解析:当需要活跃登录的用户重定向时,flash出的消息。</p>
<p>使用举例:</p>
<div class="cnblogs_code">
<pre>login_manager.needs_refresh_message = 'You need a fresh log in.Please enter your account'<span style="background-color: #ffffff; font-family: verdana, Arial, Helvetica, sans-serif; font-size: 14px; line-height: 1.5;">&nbsp;</span></pre>
</div>
<p>API:@needs_refresh_handler</p>
<p>解析:作用和unauthorized_handler类似,当你需要登录的用户是输入用户名密码登录时,而你又不想使用默认的重定向方法,那么,你可以显示的定义你自己处理函数。</p>
<p>使用举例(将非活跃登录强制重定向到登录界面,并flash出消息):</p>
<div class="cnblogs_code"><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a href="javascript:void(0);" onclick="copyCnblogsCode(this)" title="复制代码"><img src="//common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div>
<pre></pre>
<pre><span style="color: #800000;">'''
Editor: Alima | <a href="http://www.cnblogs.com/alima/" target="_blank"><span style="color: #800000;">cnblog<br></span></a>'''</span></pre>
<pre><span style="color: #0000ff;"><span style="color: #000000;">@login_manager.needs_refresh_handler</span><br>def</span><span style="color: #000000;"> refresh():
    flash(</span><span style="color: #800000;">'</span><span style="color: #800000;">You should log in!</span><span style="color: #800000;">'</span><span style="color: #000000;">)
    </span><span style="color: #0000ff;">return</span> logout()</pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a href="javascript:void(0);" onclick="copyCnblogsCode(this)" title="复制代码"><img src="//common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div></div>
<p>&nbsp;④登录机制</p>
<p>API:flask_login.current_user</p>
<p>解析:获取当前缓存中保存的用户帐户信息(一个当前用户的代理)</p>
<p>API:flask_login.login_fresh()</p>
<p>如果当前用户是活跃登录,则返回True。</p>
<p>API:flask_login.login_user(<em>user</em>,&nbsp;<em>remember=False</em>,&nbsp;<em>force=False</em>,<em>fresh=True</em>)</p>
<p>解析Ⅰ:登录用户。你需要向函数中传进一个用户对象。如果用户'is_active'属性为'Flase',只有当'force'是'True'时才会允许登录。当登录成功后返回'True',当失败时返回'False'。</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;这里特别提一下函数参数'fresh',当设置为'False'时,将登陆用户的缓存(Session)中标志为不是活跃登录。默认值为'True'</p>
<p>举例:</p>
<div class="cnblogs_code">
<pre>@app.route('/post'<span>)
@login_required
def<span> post():
    pass</span></span></pre>
</div>
<p>解析Ⅱ:如果只有当前时刻你需要要求你的用户登录,你可以这样做:</p>
<div class="cnblogs_code">
<pre>if not<span> current_user.is_authenticated:
    return current_app.login_manager.unauthorized()</span></pre>
</div>
<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 实际上,将上边的代码添加到你的视图中去就可以达到要求了。</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 当单体测试的时候它可以很方便的全局关闭认证。将'LOGIN_DISABLED'设置为True,装饰器就会被忽略了。</p>
<p>API:flask_login.logout_user()</p>
<p>解析:用户登出(你不需要通过实际用户)。这个函数同时会清除remember me中的cookie(如果存在的话)。</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; 作用就是清除一系列的cookie信息。</p>
<p>API:flask_login.confirm_login()</p>
<p>解析:函数将会将当前Sesslion设置为活跃。当从cookie加载之后,Sesson会变成不活跃状态。</p>
<p>⑤保护视图</p>
<p>API:flask_login.login_required</p>
<p>解析:如果你将这个装饰器放在视图上,它会保证你的当前用户是登录状态,并且在调用实际视图之前进行认证。</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; 如果当前用户不是系统认证的登录状态,它将调用LoginManager.unauthorized回调。</p>
<p>API:flask_login.fresh_login_required</p>
<p>解析:如果用这个装饰器放在视图上,它将确保当前用户是活跃登录的。用户的Session不是从'remember me'的cookie中加载的。</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; 敏感的操作,例如修改密码或者邮箱,应该用这个装饰器保护,来阻止cookie被盗取。</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;如果用户没有认证,通常调用'LoginManager.unauthorized'。</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;如果用户被认证了,但是缓存不是活跃登陆,它将会'LoginManager.needs_refresh'代替,而且你需要提供一个'LoginManager.refresh_view'。</p>
<p>几乎所有的flask-login下的API都在本文介绍了一下,其中<span style="font-size: 14px;">@request_loader装饰器,博主理解不是很好,一旦有进展,会更新在文章中。如果你知道@request_loader装饰器的用法,欢迎私信评论给博主。</span></p>
<p><span style="font-size: 14px;">以上就是API的介绍,我们下面来看实例。</span></p>
<p>&nbsp;</p>
<p><span style="font-size: 14px;">四.应用实例</span></p>
<p><span style="font-size: 14px;">本次在<a href="http://www.cnblogs.com/alima/p/5796298.html" target="_blank">前一篇博文</a>的基础上,我们来展开本篇博文。</span></p>
<p><span style="font-size: 14px;">首先,我们要介绍model.py类,以便匹配本次的flask-login。</span></p>
<div class="cnblogs_code"><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a href="javascript:void(0);" onclick="copyCnblogsCode(this)" title="复制代码"><img src="//common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div>
<pre><span style="color: #800000;">'''</span><span style="color: #800000;">
File name: model.py
Editor: Alima | cnblogs
Blog:   www.cnblogs.com/alima/
</span><span style="color: #800000;">'''</span>


<span style="color: #0000ff;">from</span> wtf <span style="color: #0000ff;">import</span><span style="color: #000000;"> db
</span><span style="color: #0000ff;">from</span> flask_login <span style="color: #0000ff;">import</span><span style="color: #000000;"> UserMixin


</span><span style="color: #0000ff;">class</span><span style="color: #000000;"> User(db.Model, UserMixin):
    id </span>= db.Column(db.Integer, primary_key=<span style="color: #000000;">True)
    username </span>= db.Column(db.String(80), unique=<span style="color: #000000;">True)
    password </span>= db.Column(db.String(80), unique=<span style="color: #000000;">True)
    
    </span><span style="color: #0000ff;">def</span> <span style="color: #800080;">__init__</span><span style="color: #000000;">(self, username, password):
        self.username </span>=<span style="color: #000000;"> username
        self.password </span>=<span style="color: #000000;"> password
        
    </span><span style="color: #0000ff;">def</span> <span style="color: #800080;">__repr__</span><span style="color: #000000;">(self):
        </span><span style="color: #0000ff;">return</span> <span style="color: #800000;">'</span><span style="color: #800000;">&lt;User %r&gt;</span><span style="color: #800000;">'</span> % self.username</pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a href="javascript:void(0);" onclick="copyCnblogsCode(this)" title="复制代码"><img src="//common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div></div>
<p><span style="font-size: 14px;">根据我们讲到的,使用flask-login验证用户登录时需要如下属性。</span></p>
<p>①&nbsp;is_authenticated</p>
<p>②&nbsp;is_active</p>
<p>③ is_anonyous</p>
<p>④ get_id()</p>
<p>User继承了UserMixin类的属性,方法。下面附加上flask-login包中的UseMixin类的源码。</p>
<div class="cnblogs_code"><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a href="javascript:void(0);" onclick="copyCnblogsCode(this)" title="复制代码"><img src="//common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div>
<pre><span style="color: #800000;">'''</span><span style="color: #800000;">
From Github: <a href="https://github.com/maxcountryman/flask-login/blob/master/flask_login/mixins.py" target="_blank">maxcountryman/flask-login
</a></span><span style="color: #800000;">'''</span>


<span style="color: #0000ff;">class</span><span style="color: #000000;"> UserMixin(object):
    </span><span style="color: #800000;">'''</span><span style="color: #800000;">
    This provides default implementations for the methods that Flask-Login
    expects user objects to have.
    </span><span style="color: #800000;">'''</span>


    <span style="color: #0000ff;">if</span> <span style="color: #0000ff;">not</span> PY2:  <span style="color: #008000;">#</span><span style="color: #008000;"> pragma: no cover</span>
        <span style="color: #008000;">#</span><span style="color: #008000;"> Python 3 implicitly set __hash__ to None if we override __eq__</span>
        <span style="color: #008000;">#</span><span style="color: #008000;"> We set it back to its default implementation</span>
        <span style="color: #800080;">__hash__</span> = object.<span style="color: #800080;">__hash__</span><span style="color: #000000;">


    @property
    </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> is_active(self):
        </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> True


    @property
    </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> is_authenticated(self):
        </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> True


    @property
    </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> is_anonymous(self):
        </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> False


    </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> get_id(self):
        </span><span style="color: #0000ff;">try</span><span style="color: #000000;">:
            </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> text_type(self.id)
        </span><span style="color: #0000ff;">except</span><span style="color: #000000;"> AttributeError:
            </span><span style="color: #0000ff;">raise</span> NotImplementedError(<span style="color: #800000;">'</span><span style="color: #800000;">No `id` attribute - override `get_id`</span><span style="color: #800000;">'</span><span style="color: #000000;">)


    </span><span style="color: #0000ff;">def</span> <span style="color: #800080;">__eq__</span><span style="color: #000000;">(self, other):
        </span><span style="color: #800000;">'''</span><span style="color: #800000;">
        Checks the equality of two `UserMixin` objects using `get_id`.
        </span><span style="color: #800000;">'''</span>
        <span style="color: #0000ff;">if</span><span style="color: #000000;"> isinstance(other, UserMixin):
            </span><span style="color: #0000ff;">return</span> self.get_id() ==<span style="color: #000000;"> other.get_id()
        </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> NotImplemented


    </span><span style="color: #0000ff;">def</span> <span style="color: #800080;">__ne__</span><span style="color: #000000;">(self, other):
        </span><span style="color: #800000;">'''</span><span style="color: #800000;">
        Checks the inequality of two `UserMixin` objects using `get_id`.
        </span><span style="color: #800000;">'''</span><span style="color: #000000;">
        equal </span>= self.<span style="color: #800080;">__eq__</span><span style="color: #000000;">(other)
        </span><span style="color: #0000ff;">if</span> equal <span style="color: #0000ff;">is</span><span style="color: #000000;"> NotImplemented:
            </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> NotImplemented
        </span><span style="color: #0000ff;">return</span> <span style="color: #0000ff;">not</span> equal</pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a href="javascript:void(0);" onclick="copyCnblogsCode(this)" title="复制代码"><img src="//common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div></div>
<p>附加属性一目了然,如果你想重新定制你的用户类,那么请重新类中的方法重写这个方法,如果仍想调用父类UserMixin中的方法,请使用super()语句。</p>
<p>这下,就解释了之前没有提到的UserMixin的作用。</p>
<p>下面来解释上次有很多有疑点的view.py,并在其中加入本文介绍的一些API的使用。</p>
<div class="cnblogs_code"><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a href="javascript:void(0);" onclick="copyCnblogsCode(this)" title="复制代码"><img src="//common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div>
<pre><span style="color: #008080;"> 1</span> <span style="color: #800000;">'''</span>
<span style="color: #008080;"> 2</span> <span style="color: #800000;">File Name: view.py
</span><span style="color: #008080;"> 3</span> <span style="color: #800000;">Editor: Alima | cnblogs
</span><span style="color: #008080;"> 4</span> <span style="color: #800000;">Blog: www.cnblogs.com/alima/
</span><span style="color: #008080;"> 5</span> <span style="color: #800000;">'''</span>
<span style="color: #008080;"> 6</span> 
<span style="color: #008080;"> 7</span> <span style="color: #0000ff;">from</span> flask <span style="color: #0000ff;">import</span><span style="color: #000000;"> render_template, flash, redirect, session, url_for, request, g
</span><span style="color: #008080;"> 8</span> <span style="color: #0000ff;">from</span> flask_login <span style="color: #0000ff;">import</span><span style="color: #000000;"> login_user, logout_user, current_user, login_required, AnonymousUserMixin, fresh_login_required, login_fresh
</span><span style="color: #008080;"> 9</span> <span style="color: #0000ff;">from</span> wtf <span style="color: #0000ff;">import</span><span style="color: #000000;"> app, db, lm, User
</span><span style="color: #008080;">10</span> <span style="color: #0000ff;">from</span> form <span style="color: #0000ff;">import</span><span style="color: #000000;"> LoginForm, UploadForm
</span><span style="color: #008080;">11</span> 
<span style="color: #008080;">12</span> <span style="color: #000000;">@app.before_request
</span><span style="color: #008080;">13</span> <span style="color: #0000ff;">def</span><span style="color: #000000;"> before_request():
</span><span style="color: #008080;">14</span>     g.user =<span style="color: #000000;"> current_user
</span><span style="color: #008080;">15</span>  
<span style="color: #008080;">16</span> <span style="color: #000000;">@lm.user_loader
</span><span style="color: #008080;">17</span> <span style="color: #0000ff;">def</span><span style="color: #000000;"> load_user(id):
</span><span style="color: #008080;">18</span>     user = User.query.filter_by(id=<span style="color: #000000;">id).first()
</span><span style="color: #008080;">19</span>     <span style="color: #0000ff;">return</span><span style="color: #000000;"> user
</span><span style="color: #008080;">20</span> 
<span style="color: #008080;">21</span> @app.route(<span style="color: #800000;">'</span><span style="color: #800000;">/login</span><span style="color: #800000;">'</span>, methods=[<span style="color: #800000;">'</span><span style="color: #800000;">GET</span><span style="color: #800000;">'</span>, <span style="color: #800000;">'</span><span style="color: #800000;">POST</span><span style="color: #800000;">'</span><span style="color: #000000;">])
</span><span style="color: #008080;">22</span> <span style="color: #0000ff;">def</span><span style="color: #000000;"> login():
</span><span style="color: #008080;">23</span>     <span style="color: #0000ff;">if</span> g.user <span style="color: #0000ff;">is</span> <span style="color: #0000ff;">not</span> None <span style="color: #0000ff;">and</span><span style="color: #000000;"> g.user.is_authenticated:
</span><span style="color: #008080;">24</span> <span style="color: #008000;">        #login_fresh()
</span><span style="color: #008080;">25</span>         session[<span style="color: #800000;">'</span><span style="color: #800000;">_fresh</span><span style="color: #800000;">'</span>] =<span style="color: #000000;"> False
</span><span style="color: #008080;">26</span>         <span style="color: #0000ff;">return</span> redirect(url_for(<span style="color: #800000;">'</span><span style="color: #800000;">index</span><span style="color: #800000;">'</span><span style="color: #000000;">))
</span><span style="color: #008080;">27</span>     <span style="color: #0000ff;">if</span> g.user.is_active ==<span style="color: #000000;"> True:
</span><span style="color: #008080;">28</span>         flash(<span style="color: #800000;">'</span><span style="color: #800000;">active is True</span><span style="color: #800000;">'</span><span style="color: #000000;">)
</span><span style="color: #008080;">29</span>     <span style="color: #0000ff;">else</span><span style="color: #000000;">:
</span><span style="color: #008080;">30</span>         flash(<span style="color: #800000;">'</span><span style="color: #800000;">active is False</span><span style="color: #800000;">'</span><span style="color: #000000;">)
</span><span style="color: #008080;">31</span>     form =<span style="color: #000000;"> LoginForm()
</span><span style="color: #008080;">32</span>     <span style="color: #0000ff;">if</span><span style="color: #000000;"> form.validate_on_submit():
</span><span style="color: #008080;">33</span>         user = User.query.filter_by(username=form.username.data, password=<span style="color: #000000;">form.password.data).first()
</span><span style="color: #008080;">34</span>         <span style="color: #0000ff;">if</span>(user <span style="color: #0000ff;">is</span> <span style="color: #0000ff;">not</span><span style="color: #000000;"> None):
</span><span style="color: #008080;">35</span>             login_user(user,remember=<span style="color: #000000;">form.remember_me.data)
</span><span style="color: #008080;">36</span>             <span style="color: #0000ff;">return</span> redirect(request.args.get(<span style="color: #800000;">"</span><span style="color: #800000;">next</span><span style="color: #800000;">"</span>) <span style="color: #0000ff;">or</span> url_for(<span style="color: #800000;">'</span><span style="color: #800000;">index</span><span style="color: #800000;">'</span><span style="color: #000000;">))
</span><span style="color: #008080;">37</span>     <span style="color: #0000ff;">return</span> render_template(<span style="color: #800000;">'</span><span style="color: #800000;">login.html</span><span style="color: #800000;">'</span>, form=<span style="color: #000000;">form)
</span><span style="color: #008080;">38</span>     
<span style="color: #008080;">39</span> @app.route(<span style="color: #800000;">'</span><span style="color: #800000;">/</span><span style="color: #800000;">'</span>, methods = [<span style="color: #800000;">'</span><span style="color: #800000;">GET</span><span style="color: #800000;">'</span>, <span style="color: #800000;">'</span><span style="color: #800000;">POST</span><span style="color: #800000;">'</span><span style="color: #000000;">])
</span><span style="color: #008080;">40</span> @app.route(<span style="color: #800000;">'</span><span style="color: #800000;">/index</span><span style="color: #800000;">'</span>, methods=[<span style="color: #800000;">'</span><span style="color: #800000;">GET</span><span style="color: #800000;">'</span>, <span style="color: #800000;">'</span><span style="color: #800000;">POST</span><span style="color: #800000;">'</span><span style="color: #000000;">])
</span><span style="color: #008080;">41</span> <span style="color: #000000;">@login_required
</span><span style="color: #008080;">42</span> <span style="color: #0000ff;">def</span><span style="color: #000000;"> index():
</span><span style="color: #008080;">43</span>     form =<span style="color: #000000;"> UploadForm()
</span><span style="color: #008080;">44</span> 
<span style="color: #008080;">45</span>     <span style="color: #0000ff;">if</span><span style="color: #000000;"> form.validate_on_submit():
</span><span style="color: #008080;">46</span>         filename =<span style="color: #000000;"> secure_filename(form.file.data.filename)
</span><span style="color: #008080;">47</span>         form.file.data.save(<span style="color: #800000;">'</span><span style="color: #800000;">uploads/</span><span style="color: #800000;">'</span> +<span style="color: #000000;"> filename)
</span><span style="color: #008080;">48</span>         <span style="color: #0000ff;">return</span> redirect(url_for(<span style="color: #800000;">'</span><span style="color: #800000;">upload</span><span style="color: #800000;">'</span><span style="color: #000000;">))
</span><span style="color: #008080;">49</span> 
<span style="color: #008080;">50</span>     <span style="color: #0000ff;">return</span> render_template(<span style="color: #800000;">'</span><span style="color: #800000;">upload.html</span><span style="color: #800000;">'</span>, form=<span style="color: #000000;">form)
</span><span style="color: #008080;">51</span>     
<span style="color: #008080;">52</span> @app.route(<span style="color: #800000;">'</span><span style="color: #800000;">/logout</span><span style="color: #800000;">'</span><span style="color: #000000;">)
</span><span style="color: #008080;">53</span> <span style="color: #000000;">@login_required
</span><span style="color: #008080;">54</span> <span style="color: #0000ff;">def</span><span style="color: #000000;"> logout():
</span><span style="color: #008080;">55</span> <span style="color: #000000;">    logout_user()
</span><span style="color: #008080;">56</span>     <span style="color: #0000ff;">return</span> redirect(url_for(<span style="color: #800000;">'</span><span style="color: #800000;">login</span><span style="color: #800000;">'</span>))</pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a href="javascript:void(0);" onclick="copyCnblogsCode(this)" title="复制代码"><img src="//common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div></div>
<p>解释:</p>
<p>①第14行,current_user是在当前缓存中取出的用户实体,如果存在用户登录的缓存则返回实体,如果Session不存在则返回None。</p>
<p>②第16行,@lm.user_loader,这个装饰器表示,每次有请求时,都会调用它所装饰的函数。</p>
<p>Q:&nbsp;user_loader的运行机制是什么呢?</p>
<p>A: 答案在stackoverflow上有一个最好的解释,附上连接:<a class="question-hyperlink" href="http://stackoverflow.com/questions/12075535/flask-login-cant-understand-how-it-works">flask-login: can't understand how it works</a></p>
<p>&nbsp; &nbsp; 解释一下答案中的最后几个点,</p>
<p>&nbsp; &nbsp; 1)你需要由你自己写出这段代码。,检查用户名和密码是否匹配(非请求到数据库的情况下)。</p>
<p>&nbsp; &nbsp; 也就是说,用户的cookie中可以存在非法的登录信息,你需要自己写出代码校验是否符合你所用数据库的格式。</p>
<p>&nbsp; &nbsp; 2)如果认证成功,取得用户ID,然后将用户实体它传递给login_user()。</p>
<p>③第35行,<strong>login_user()</strong>博主准备放在左后写,这是flask-Login的精髓,也是很多人不了解的。</p>
<p>④第41行,@login_required装饰器将验证所装饰的函数是否是在用户登录状态下访问的。</p>
<p>&nbsp; &nbsp;我们在配置文件(wtf.py)中配置如下信息,指定了当用户非法登录时重定向的界面和flash出的消息。</p>
<div class="cnblogs_code">
<pre>lm.login_view = <span style="color: #800000;">'</span><span style="color: #800000;">login</span><span style="color: #800000;">'</span><span style="color: #000000;">
lm.login_message </span>= <span style="color: #800000;">'</span><span style="color: #800000;">Please log in!</span><span style="color: #800000;">'</span><span style="color: #000000;">
lm.login_message_category </span>= <span style="color: #800000;">'</span><span style="color: #800000;">info</span><span style="color: #800000;">'</span></pre>
</div>
<p>&nbsp;⑤第25行,session['_fresh'] = False没看懂,请Ctrl+F在本文中搜索,下面有解释。</p>
<p>让我们之间访问<a href="http://127.0.0.1:5000/index" target="_blank">http://127.0.0.1:5000/index</a>会发生什么。</p>
<p><img src="http://images2015.cnblogs.com/blog/989365/201609/989365-20160902132746558-196552247.png" alt=""></p>
<p>看到红色框之内的信息了么,就是定义的login_message中的信息。同时页面跳转到了<a href="http://127.0.0.1:5000/login" target="_blank">http://127.0.0.1:5000/login</a>,也就是定义的login_view。</p>
<p>&nbsp;同时,上文还讲到使用unauthorized_handler装饰器,可以定制你自己的非法登陆处理过程。这里我们做一个简单的使用过程,非法登录时,页面将显示You have not logged in,你可以自己体验其中的妙处。</p>
<div class="cnblogs_code">
<pre><span style="color: #000000;">@lm.unauthorized_handler
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> unauthorized_handler():
    return </span><span style="color: #800000;">'</span><span style="color: #800000;">You have not logged in!</span><span style="color: #800000;">'</span></pre>
</div>
<p>&nbsp;下面,让我们新加入一个需要活跃登录的界面。</p>
<p>Q:什么是活跃登录(fresh login)?</p>
<p>A:活跃登录是指当用户通过键入账户,并点击登录按钮后的登录状态。</p>
<p>&nbsp; &nbsp; &nbsp;而当用户通过缓存(Session)登录时,被认为是非活跃登录状态。</p>
<p>Q:为什么区分活跃登录和非活跃登录?</p>
<p>A:当用户需要修改用户信息,密码或是其他隐私信息的时候,防止你的PC被别人登录并恶意修改,或是其它人盗用你的缓存达到你不期望的一些恶意操作。</p>
<p>在view.py中添加如下代码段。</p>
<div class="cnblogs_code">
<pre>@app.route(<span style="color: #800000;">'</span><span style="color: #800000;">/info</span><span style="color: #800000;">'</span>, methods = [<span style="color: #800000;">'</span><span style="color: #800000;">GET</span><span style="color: #800000;">'</span>, <span style="color: #800000;">'</span><span style="color: #800000;">POST</span><span style="color: #800000;">'</span><span style="color: #000000;">])
@fresh_login_required
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> info():
    </span><span style="color: #0000ff;">return</span> <span style="color: #800000;">'</span><span style="color: #800000;">Edit your infomation</span><span style="color: #800000;">'</span></pre>
</div>
<p>在wtf.py中配置如下信息。</p>
<div class="cnblogs_code">
<pre>lm.refresh_view = <span style="color: #800000;">'</span><span style="color: #800000;">login</span><span style="color: #800000;">'</span><span style="color: #000000;">
lm.needs_refresh_message </span>= <span style="color: #800000;">'</span><span style="color: #800000;">Please enter your info</span><span style="color: #800000;">'</span><span style="color: #000000;">
lm.needs_refresh_message_category </span>= <span style="color: #800000;">"</span><span style="color: #800000;">refresh_info</span><span style="color: #800000;">"</span><span style="background-color: #ffffff; font-family: verdana, Arial, Helvetica, sans-serif; font-size: 14px; line-height: 1.5;">&nbsp;</span></pre>
</div>
<p>如果想将页面重定向到login界面,需要做点小的改善,就是修改flask-login源码</p>
<p>文件在flaskr\Lib\site-packages\flask_login.py。</p>
<p>修改源码中的needs_refresh()函数。</p>
<div class="cnblogs_code" onclick="cnblogs_code_show('d8a8ca7e-db2c-4449-840b-acad7c2c1539')"><img id="code_img_closed_d8a8ca7e-db2c-4449-840b-acad7c2c1539" class="code_img_closed" src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" alt=""><img id="code_img_opened_d8a8ca7e-db2c-4449-840b-acad7c2c1539" class="code_img_opened" style="display: none;" onclick="cnblogs_code_hide('d8a8ca7e-db2c-4449-840b-acad7c2c1539',event)" src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" alt="">
<div id="cnblogs_code_open_d8a8ca7e-db2c-4449-840b-acad7c2c1539" class="cnblogs_code_hide">
<pre><span style="color: #008080;"> 1</span> <span style="color: #0000ff;">def</span><span style="color: #000000;"> needs_refresh(self):
</span><span style="color: #008080;"> 2</span>     <span style="color: #800000;">'''</span>
<span style="color: #008080;"> 3</span> <span style="color: #800000;">    This is called when the user is logged in, but they need to be
</span><span style="color: #008080;"> 4</span> <span style="color: #800000;">    reauthenticated because their session is stale. If you register a
</span><span style="color: #008080;"> 5</span> <span style="color: #800000;">    callback with `needs_refresh_handler`, then it will be called.
</span><span style="color: #008080;"> 6</span> <span style="color: #800000;">    Otherwise, it will take the following actions:
</span><span style="color: #008080;"> 7</span> 
<span style="color: #008080;"> 8</span> <span style="color: #800000;">    - Flash :attr:`LoginManager.needs_refresh_message` to the user.
</span><span style="color: #008080;"> 9</span> 
<span style="color: #008080;">10</span> <span style="color: #800000;">    - Redirect the user to :attr:`LoginManager.refresh_view`. (The page
</span><span style="color: #008080;">11</span> <span style="color: #800000;">      they were attempting to access will be passed in the ``next``
</span><span style="color: #008080;">12</span> <span style="color: #800000;">      query string variable, so you can redirect there if present
</span><span style="color: #008080;">13</span> <span style="color: #800000;">      instead of the homepage.)
</span><span style="color: #008080;">14</span> 
<span style="color: #008080;">15</span> <span style="color: #800000;">    If :attr:`LoginManager.refresh_view` is not defined, then it will
</span><span style="color: #008080;">16</span> <span style="color: #800000;">    simply raise a HTTP 401 (Unauthorized) error instead.
</span><span style="color: #008080;">17</span> 
<span style="color: #008080;">18</span> <span style="color: #800000;">    This should be returned from a view or before/after_request function,
</span><span style="color: #008080;">19</span> <span style="color: #800000;">    otherwise the redirect will have no effect.
</span><span style="color: #008080;">20</span>     <span style="color: #800000;">'''</span>
<span style="color: #008080;">21</span> <span style="color: #000000;">    user_needs_refresh.send(current_app._get_current_object())
</span><span style="color: #008080;">22</span> 
<span style="color: #008080;">23</span>     <span style="color: #0000ff;">if</span><span style="color: #000000;"> self.needs_refresh_callback:
</span><span style="color: #008080;">24</span>        <span style="color: #0000ff;">return</span><span style="color: #000000;"> self.needs_refresh_callback()
</span><span style="color: #008080;">25</span> 
<span style="color: #008080;">26</span>     <span style="color: #0000ff;">if</span> <span style="color: #0000ff;">not</span><span style="color: #000000;"> self.refresh_view:
</span><span style="color: #008080;">27</span>         abort(401<span style="color: #000000;">)
</span><span style="color: #008080;">28</span> 
<span style="color: #008080;">29</span>     <span style="color: #0000ff;">if</span> self.localize_callback <span style="color: #0000ff;">is</span> <span style="color: #0000ff;">not</span><span style="color: #000000;"> None:
</span><span style="color: #008080;">30</span> <span style="color: #000000;">       flash(self.localize_callback(self.needs_refresh_message),
</span><span style="color: #008080;">31</span>            category=<span style="color: #000000;">self.needs_refresh_message_category)
</span><span style="color: #008080;">32</span>     <span style="color: #0000ff;">else</span><span style="color: #000000;">:
</span><span style="color: #008080;">33</span> <span style="color: #000000;">           flash(self.needs_refresh_message,
</span><span style="color: #008080;">34</span>                   category=<span style="color: #000000;">self.needs_refresh_message_category)
</span><span style="color: #008080;">35</span>     
<span style="color: #008080;">36</span>     logout_user() <span style="color: #008000;">#</span><span style="color: #008000;">Edit by Alima | cnblogs</span>
<span style="color: #008080;">37</span>     <span style="color: #0000ff;">return</span> redirect(login_url(self.refresh_view, request.url))</pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
<p>Q:为什么要更改needs_refresh()函数?</p>
<p>A:试想当你进入info界面,info界面重定向到login界面,由于浏览器保留了用户的登录缓存,又由login界面跳转到index界面。</p>
<p>&nbsp; &nbsp; 最后当你进入info界面的时候,现在不是活跃登录,你预想的结果是让用户登录,其实不然,你发现你的浏览器进入了index界面。</p>
<p>&nbsp; &nbsp; 所以你需要重新定制一下needs_refresh()函数,先将当前用户退出登录,这样就会跳转到login界面了,一个小小的思维陷阱。</p>
<p>&nbsp; &nbsp; 在源码中没有实现我们想要的效果,那么就修改源码。</p>
<p>Q:当我看到view.py中,login函数时,有一个session['_fresh'] = False语句看不懂。</p>
<p>A:flask-login包,并没有在用户非活跃登录状态,将session['_fresh']设置为Flase的地方,所以我们在那里显示的设置为Flase。</p>
<p>&nbsp;</p>
<p>分别用两种方式访问<a href="http://127.0.0.1:5000/info">http://127.0.0.1:5000/info</a></p>
<p>①在键入用户账户并登录成功的情况下,在浏览器中直接输入127.0.0.1:5000/index和127.0.0.1:5000/info</p>
<p>②关闭浏览器界面,打开一个新的浏览器界面再次分别访问127.0.0.1:5000/index和127.0.0.1:5000/info</p>
<p>结果:</p>
<p>①在键入用户账户并登录成功的情况下,两个界面都能够正常显示。</p>
<p>②关闭浏览器界面,打开一个新的浏览器界面。可以访问127.0.0.1:5000/index,但当访问127.0.0.1:5000/info会跳转到登录界面,如下图所示。</p>
<p><img src="http://images2015.cnblogs.com/blog/989365/201609/989365-20160902150726480-841327792.png" alt=""></p>
<p>看到红色的方框中,正是我们定义的needs_refresh_message信息,并且重定向到login界面。</p>
<p>相信读到这里,你已经能够使用:</p>
<p>①login_fresh()判断当前是否是活跃登录,是则返回True。</p>
<p>②confirm_login()将当前状态强制转换为活跃登录。</p>
<p>&nbsp;</p>
<p>最后的最后,我们解释login_user()函数。</p>
<p>login_user()函数是flask-login的最核心的函数,附上源代码。</p>
<div class="cnblogs_code" onclick="cnblogs_code_show('6e4fac7e-bebe-401e-a4c5-a9c37b16dd58')"><img id="code_img_closed_6e4fac7e-bebe-401e-a4c5-a9c37b16dd58" class="code_img_closed" src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" alt=""><img id="code_img_opened_6e4fac7e-bebe-401e-a4c5-a9c37b16dd58" class="code_img_opened" style="display: none;" onclick="cnblogs_code_hide('6e4fac7e-bebe-401e-a4c5-a9c37b16dd58',event)" src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" alt="">
<div id="cnblogs_code_open_6e4fac7e-bebe-401e-a4c5-a9c37b16dd58" class="cnblogs_code_hide">
<pre><span style="color: #008080;"> 1</span> <span style="color: #0000ff;">def</span> login_user(user, remember=False, force=False, fresh=<span style="color: #000000;">True):
</span><span style="color: #008080;"> 2</span>     <span style="color: #800000;">'''</span>
<span style="color: #008080;"> 3</span> <span style="color: #800000;">    Logs a user in. You should pass the actual user object to this. If the
</span><span style="color: #008080;"> 4</span> <span style="color: #800000;">    user's `is_active` property is ``False``, they will not be logged in
</span><span style="color: #008080;"> 5</span> <span style="color: #800000;">    unless `force` is ``True``.
</span><span style="color: #008080;"> 6</span> 
<span style="color: #008080;"> 7</span> <span style="color: #800000;">    This will return ``True`` if the log in attempt succeeds, and ``False`` if
</span><span style="color: #008080;"> 8</span> <span style="color: #800000;">    it fails (i.e. because the user is inactive).
</span><span style="color: #008080;"> 9</span> 
<span style="color: #008080;">10</span> <span style="color: #800000;">    :param user: The user object to log in.
</span><span style="color: #008080;">11</span> <span style="color: #800000;">    :type user: object
</span><span style="color: #008080;">12</span> <span style="color: #800000;">    :param remember: Whether to remember the user after their session expires.
</span><span style="color: #008080;">13</span> <span style="color: #800000;">        Defaults to ``False``.
</span><span style="color: #008080;">14</span> <span style="color: #800000;">    :type remember: bool
</span><span style="color: #008080;">15</span> <span style="color: #800000;">    :param force: If the user is inactive, setting this to ``True`` will log
</span><span style="color: #008080;">16</span> <span style="color: #800000;">        them in regardless. Defaults to ``False``.
</span><span style="color: #008080;">17</span> <span style="color: #800000;">    :type force: bool
</span><span style="color: #008080;">18</span> <span style="color: #800000;">    :param fresh: setting this to ``False`` will log in the user with a session
</span><span style="color: #008080;">19</span> <span style="color: #800000;">    marked as not "fresh". Defaults to ``True``.
</span><span style="color: #008080;">20</span> <span style="color: #800000;">    :type fresh: bool
</span><span style="color: #008080;">21</span>     <span style="color: #800000;">'''</span>
<span style="color: #008080;">22</span>     <span style="color: #0000ff;">if</span> <span style="color: #0000ff;">not</span> force <span style="color: #0000ff;">and</span> <span style="color: #0000ff;">not</span><span style="color: #000000;"> user.is_active:
</span><span style="color: #008080;">23</span>         <span style="color: #0000ff;">return</span><span style="color: #000000;"> False
</span><span style="color: #008080;">24</span> 
<span style="color: #008080;">25</span>     user_id =<span style="color: #000000;"> getattr(user, current_app.login_manager.id_attribute)()
</span><span style="color: #008080;">26</span>     session[<span style="color: #800000;">'</span><span style="color: #800000;">user_id</span><span style="color: #800000;">'</span>] =<span style="color: #000000;"> user_id
</span><span style="color: #008080;">27</span>     session[<span style="color: #800000;">'</span><span style="color: #800000;">_fresh</span><span style="color: #800000;">'</span>] =<span style="color: #000000;"> fresh
</span><span style="color: #008080;">28</span>     session[<span style="color: #800000;">'</span><span style="color: #800000;">_id</span><span style="color: #800000;">'</span>] =<span style="color: #000000;"> _create_identifier()
</span><span style="color: #008080;">29</span> 
<span style="color: #008080;">30</span>     <span style="color: #0000ff;">if</span><span style="color: #000000;"> remember:
</span><span style="color: #008080;">31</span>         session[<span style="color: #800000;">'</span><span style="color: #800000;">remember</span><span style="color: #800000;">'</span>] = <span style="color: #800000;">'</span><span style="color: #800000;">set</span><span style="color: #800000;">'</span>
<span style="color: #008080;">32</span> 
<span style="color: #008080;">33</span>     _request_ctx_stack.top.user =<span style="color: #000000;"> user
</span><span style="color: #008080;">34</span>     user_logged_in.send(current_app._get_current_object(), user=<span style="color: #000000;">_get_user())
</span><span style="color: #008080;">35</span>     <span style="color: #0000ff;">return</span> True</pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
<p><span style="color: #ff6600;"><strong>Top One:&nbsp;</strong></span>不关闭浏览器情况下的Remember me功能</p>
<p><span style="color: #993300;"><strong>Operation:&nbsp;</strong></span>读者可以尝试一下,当我们运行本次实例时。</p>
<p>①当你登录用户,但不使用<strong>Remember me</strong>功能。</p>
<p>②不关闭浏览器,打开一个新的页面,并将刚刚登录过的界面关闭。</p>
<p>③在新的界面输入<a href="http://127.0.0.1:5000/login" target="_blank">127.0.0.1:5000/login</a></p>
<p><span style="color: #800000;"><strong>Explanation:</strong></span></p>
<p>你会发现,你并没有进入login界面,而是进入了index界面,你会想我不是没有使用Remember me功能么?</p>
<p>其实,经常做Web程序的人会有直觉,问题出现在Session上,的确,确实发生在Session上。</p>
<p>以Chrome浏览器为例,打开Chrome菜单-&gt;设置-&gt;显示高级设置-&gt;内容设置(隐私内容)-&gt;所有Cookie和数据库数据(博主将截图部分cookie和日期做了模糊处理,请见谅)</p>
<p><img src="http://images2015.cnblogs.com/blog/989365/201609/989365-20160904171408983-1333477969.jpg" alt=""></p>
<p>我们看到这个session的过期时间是在关闭浏览器之后,这样就解释了上面的现象。</p>
<p>当然,你可以设置cookie失效时间,下面表格是cookie的一些设置。</p>
<table border="2" align="left">
<tbody>
<tr>
<td><span style="font-size: 13px;">REMEMBER_COOKIE_NAME</span></td>
<td><span style="font-size: 13px;">存储"Remember me"信息的Cookie名。默认值:remember_token</span></td>
</tr>
<tr>
<td><span style="font-size: 13px;">REMEMBER_COOKIE_DURATION</span></td>
<td><span style="font-size: 13px;">cookie过期时间,是一个datetime.timedelta对象。默认值:365天</span></td>
</tr>
<tr>
<td><span style="font-size: 13px;">REMEMBER_COOKIE_DOMAIN</span></td>
<td><span style="font-size: 13px;">如果"Remember me"cookie需要跨域,在此设置域名值(eg: .example.com会允许example下所有子域名)。默认值:None</span></td>
</tr>
<tr>
<td><span style="font-size: 13px;">REMEMBER_COOKIE_PATH</span></td>
<td><span style="font-size: 13px;">限制"Remember me"的cookie存储到某一路径下。默认值:/</span></td>
</tr>
<tr>
<td><span style="font-size: 13px;">REMEMBER_COOKIE_SECURE</span></td>
<td><span style="font-size: 13px;">限制"Remember me"的cookie在某些安全通道下有用(HTTP)。默认值:None</span></td>
</tr>
<tr>
<td><span style="font-size: 13px;">REMEMBER_COOKIE_HTTPONLY</span></td>
<td><span style="font-size: 13px;">保护"Remember me"的cookie不能通过客户端脚本访问。默认值:False</span></td>
</tr>
</tbody>
</table>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span style="color: #ff6600;">TOP Two:&nbsp;</span></strong><span style="color: #ff6600;"><span style="color: #000000;">关闭浏览器后的Remember me功能。</span></span></p>
<p><span style="color: #ff6600;"><span style="color: #000000;"><strong><span style="color: #993300;">Operation:</span>&nbsp;</strong>读者可以尝试一下,使用Remember me功能登录。</span></span></p>
<p><span style="color: #ff6600;"><span style="color: #000000;">①进入本次实例的登录界面,输入模拟用户,勾选Remember me,点击登录按钮登录。</span></span></p>
<p><span style="color: #ff6600;"><span style="color: #000000;">②关闭浏览器,重新进入本次实例系统</span></span></p>
<p><span style="color: #ff6600;"><span style="color: #000000;">③你会发现你没有进入预想的index界面,而是重新进入了login界面。</span></span></p>
<p><span style="color: #ff6600;"><span style="color: #000000;">为什么?让我们好好回忆一下,提示跟login_user()函数和类的初始化有关系。</span></span></p>
<p><span style="color: #ff6600;"><span style="color: #000000;">对,你可以想到,LoginManager类的初始化是在一切操作之前,那么我们的当前用户设置成了AnonymousUserMixin(游客),那我们不是做了login_user()操作了么。</span></span></p>
<p><span style="color: #ff6600;"><span style="color: #000000;">让我们回去看一下代码。</span></span></p>
<div class="cnblogs_code"><div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a href="javascript:void(0);" onclick="copyCnblogsCode(this)" title="复制代码"><img src="//common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div>
<pre><span style="color: #008080;"> 1</span> @app.route(<span style="color: #800000;">'</span><span style="color: #800000;">/login</span><span style="color: #800000;">'</span>, methods=[<span style="color: #800000;">'</span><span style="color: #800000;">GET</span><span style="color: #800000;">'</span>, <span style="color: #800000;">'</span><span style="color: #800000;">POST</span><span style="color: #800000;">'</span><span style="color: #000000;">])
</span><span style="color: #008080;"> 2</span> <span style="color: #0000ff;">def</span><span style="color: #000000;"> login():
</span><span style="color: #008080;"> 3</span>     <span style="color: #800000;">'''</span>
<span style="color: #008080;"> 4</span> <span style="color: #800000;">    Alima | cnblogs
</span><span style="color: #008080;"> 5</span>     <span style="color: #800000;">'''</span>
<span style="color: #008080;"> 6</span>     api_key = request.args.get(<span style="color: #800000;">'</span><span style="color: #800000;">api_key</span><span style="color: #800000;">'</span><span style="color: #000000;">)
</span><span style="color: #008080;"> 7</span>     <span style="color: #0000ff;">if</span> g.user <span style="color: #0000ff;">is</span> <span style="color: #0000ff;">not</span> None <span style="color: #0000ff;">and</span><span style="color: #000000;"> g.user.is_authenticated:
</span><span style="color: #008080;"> 8</span>         session[<span style="color: #800000;">'</span><span style="color: #800000;">_fresh</span><span style="color: #800000;">'</span>] =<span style="color: #000000;"> False
</span><span style="color: #008080;"> 9</span> <span style="color: #000000;">        flash(current_app.login_manager._login_disabled)
</span><span style="color: #008080;">10</span> <span style="color: #000000;">        flash(current_user.is_authenticated)
</span><span style="color: #008080;">11</span>         <span style="color: #0000ff;">return</span> redirect(url_for(<span style="color: #800000;">'</span><span style="color: #800000;">index</span><span style="color: #800000;">'</span><span style="color: #000000;">))
</span><span style="color: #008080;">12</span>     form =<span style="color: #000000;"> LoginForm()
</span><span style="color: #008080;">13</span>     <span style="color: #0000ff;">if</span><span style="color: #000000;"> form.validate_on_submit():
</span><span style="color: #008080;">14</span> <span style="color: #000000;">        flash(form.remember_me.data)
</span><span style="color: #008080;">15</span>         user = User.query.filter_by(username=form.username.data, password=<span style="color: #000000;">form.password.data).first()
</span><span style="color: #008080;">16</span>         <span style="color: #0000ff;">if</span>(user <span style="color: #0000ff;">is</span> <span style="color: #0000ff;">not</span><span style="color: #000000;"> None):
</span><span style="color: #008080;">17</span> <span style="color: #000000;">            login_user(user, form.remember_me.data)
</span><span style="color: #008080;">18</span>             <span style="color: #0000ff;">return</span> redirect(request.args.get(<span style="color: #800000;">"</span><span style="color: #800000;">next</span><span style="color: #800000;">"</span>) <span style="color: #0000ff;">or</span> url_for(<span style="color: #800000;">'</span><span style="color: #800000;">index</span><span style="color: #800000;">'</span><span style="color: #000000;">))
</span><span style="color: #008080;">19</span>         <span style="color: #0000ff;">else</span><span style="color: #000000;">:
</span><span style="color: #008080;">20</span>             <span style="color: #0000ff;">print</span><span style="color: #000000;">(form.errors)
</span><span style="color: #008080;">21</span>     <span style="color: #0000ff;">return</span> render_template(<span style="color: #800000;">'</span><span style="color: #800000;">login.html</span><span style="color: #800000;">'</span>, form=form)</pre>
<div class="cnblogs_code_toolbar"><span class="cnblogs_code_copy"><a href="javascript:void(0);" onclick="copyCnblogsCode(this)" title="复制代码"><img src="//common.cnblogs.com/images/copycode.gif" alt="复制代码"></a></span></div></div>
<p><span style="color: #ff6600;"><span style="color: #000000;">发现问题了么,这是因为博主之前了解flask-login包不是很深刻,以为@user_loader会使用户自动load进LoginManager函数,其实不然。</span></span></p>
<p><span style="color: #ff6600;"><span style="color: #000000;">将第8行改为:</span></span></p>
<div class="cnblogs_code">
<pre>login_user(load_user(g.user.id), True, False, False)</pre>
</div>
<p><span style="color: #ff6600;"><span style="color: #000000;">Q:为什么不传入g.user或者current_user?</span></span></p>
<p><span style="color: #ff6600;"><span style="color: #000000;">A:传入g.user或者current_user,会发出一个maximum recursion depth exceeded in comparison的错误信息。</span></span></p>
<p><span style="color: #ff6600;"><span style="color: #000000;">&nbsp; &nbsp; &nbsp;current_user不是一个纯正的用户类,它的返回值是一个Thread Local 对象。(解析:<a href="https://blog.tonyseek.com/post/the-context-mechanism-of-flask/" target="_blank">Flask的Context机制</a>)</span></span></p>
<div class="cnblogs_code">
<pre>current_user = LocalProxy(<span style="color: #0000ff;">lambda</span>: _get_user())</pre>
</div>
<p><strong><span style="color: #ff6600;">Topic Three</span></strong><span style="color: #000000;">:logout_user()函数功能。</span></p>
<p><span style="color: #000000;">logout_user()函数功能就是将缓存的用户信息清楚,将Remember me标记位设置为清空状态。</span></p>
<div class="cnblogs_code" onclick="cnblogs_code_show('741da6d5-807c-4795-b9c1-e1b045a9c3bf')"><img id="code_img_closed_741da6d5-807c-4795-b9c1-e1b045a9c3bf" class="code_img_closed" src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" alt=""><img id="code_img_opened_741da6d5-807c-4795-b9c1-e1b045a9c3bf" class="code_img_opened" style="display: none;" onclick="cnblogs_code_hide('741da6d5-807c-4795-b9c1-e1b045a9c3bf',event)" src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" alt="">
<div id="cnblogs_code_open_741da6d5-807c-4795-b9c1-e1b045a9c3bf" class="cnblogs_code_hide">
<pre><span style="color: #008080;"> 1</span> <span style="color: #0000ff;">def</span><span style="color: #000000;"> logout_user():
</span><span style="color: #008080;"> 2</span>     <span style="color: #800000;">'''</span>
<span style="color: #008080;"> 3</span> <span style="color: #800000;">    Logs a user out. (You do not need to pass the actual user.) This will
</span><span style="color: #008080;"> 4</span> <span style="color: #800000;">    also clean up the remember me cookie if it exists.
</span><span style="color: #008080;"> 5</span>     <span style="color: #800000;">'''</span>
<span style="color: #008080;"> 6</span> 
<span style="color: #008080;"> 7</span>     user =<span style="color: #000000;"> _get_user()
</span><span style="color: #008080;"> 8</span> 
<span style="color: #008080;"> 9</span>     <span style="color: #0000ff;">if</span> <span style="color: #800000;">'</span><span style="color: #800000;">user_id</span><span style="color: #800000;">'</span> <span style="color: #0000ff;">in</span><span style="color: #000000;"> session:
</span><span style="color: #008080;">10</span>         session.pop(<span style="color: #800000;">'</span><span style="color: #800000;">user_id</span><span style="color: #800000;">'</span><span style="color: #000000;">)
</span><span style="color: #008080;">11</span> 
<span style="color: #008080;">12</span>     <span style="color: #0000ff;">if</span> <span style="color: #800000;">'</span><span style="color: #800000;">_fresh</span><span style="color: #800000;">'</span> <span style="color: #0000ff;">in</span><span style="color: #000000;"> session:
</span><span style="color: #008080;">13</span>         session.pop(<span style="color: #800000;">'</span><span style="color: #800000;">_fresh</span><span style="color: #800000;">'</span><span style="color: #000000;">)
</span><span style="color: #008080;">14</span> 
<span style="color: #008080;">15</span>     cookie_name = current_app.config.get(<span style="color: #800000;">'</span><span style="color: #800000;">REMEMBER_COOKIE_NAME</span><span style="color: #800000;">'</span><span style="color: #000000;">, COOKIE_NAME)
</span><span style="color: #008080;">16</span>     <span style="color: #0000ff;">if</span> cookie_name <span style="color: #0000ff;">in</span><span style="color: #000000;"> request.cookies:
</span><span style="color: #008080;">17</span>         session[<span style="color: #800000;">'</span><span style="color: #800000;">remember</span><span style="color: #800000;">'</span>] = <span style="color: #800000;">'</span><span style="color: #800000;">clear</span><span style="color: #800000;">'</span>
<span style="color: #008080;">18</span> 
<span style="color: #008080;">19</span>     user_logged_out.send(current_app._get_current_object(), user=<span style="color: #000000;">user)
</span><span style="color: #008080;">20</span> 
<span style="color: #008080;">21</span> <span style="color: #000000;">    current_app.login_manager.reload_user()
</span><span style="color: #008080;">22</span>     <span style="color: #0000ff;">return</span> True</pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
<p>&nbsp;</p>
<p><span style="color: #ff6600;"><span style="color: #000000;">至此,本篇介绍基本结束,更深入的flask-login包的使用,博主将在以后为大家开设专题深入讲解。</span></span></p>
<p><span style="color: #ff6600;"><span style="color: #000000;">如果你对博文中某些观点,某些思考角度不一样的,欢迎在Alima的cnblogs下面留言私信,你们的互动是我的动力。</span></span></p>
<p><span style="color: #ff6600;"><span style="color: #000000;">Posted by Alima | cnblogs。</span></span></p>
<p><span style="color: #ff6600;"><span style="color: #000000;">If there is some question&nbsp;Obsession about this blog,welcome to enter www.cnblogs.com/alima/ and seed message to me.</span></span></p>
<p><span style="color: #ff6600;"><span style="color: #000000;">参考:</span></span></p>
<p><span style="color: #ff6600;"><span style="color: #000000;">[1] flask-login Github maxcountryman&nbsp;https://github.com/maxcountryman/flask-login</span></span></p>
<p><span style="color: #ff6600;"><span style="color: #000000;">[2] flask-login Doc maxcountryman&nbsp;https://flask-login.readthedocs.io/en/latest/</span></span></p>
<p>&nbsp;</p>
<p><span style="color: #ff6600;"><span style="color: #000000;">PS:</span></span></p>
<p><span style="color: #ff6600;"><span style="color: #000000;">本篇博文撰写时间比之前的已发表的几篇博文相比,时间较长。但内容相比之前更充实,解析并修改源码是一种新的尝试。</span></span></p>
<p><span style="color: #ff6600;"><span style="color: #000000;">学习新知识本应如此,追求一些细致的东西,会让一些未知的问题迎刃而解。</span></span></p>
<p><span style="color: #ff6600;"><span style="color: #000000;">如果你喜欢Alima的博文,欢迎Follow Alima的cnblogs的最新动态。</span></span></p>
<p><span style="color: #ff6600;"><span style="color: #000000;">另外,Alima在谋求一个进取的平台发展,一份安心的Offer,欢迎投来橄榄枝,欢迎提出职位需求,请您联系我。</span></span></p>
<p><strong><span style="color: #ff6600;"><span style="color: #000000;">----------------------------------</span></span></strong></p>
<p><span style="color: #0000ff;"><strong><span style="color: #000000;">|&nbsp;</span>Alima的联系方式</strong></span></p>
<p><span style="color: #0000ff;"><strong><span style="color: #800080;"><strong><span style="color: #000000;">|&nbsp;</span>QQ Chat: 995816845</strong></span></strong></span></p>
<p><span style="color: #0000ff;"><strong><span style="color: #800080;"><span style="color: #000000;">|&nbsp;</span>E-mail:xoxo2191@163.com</span><br></strong></span></p>
<p><strong><span style="color: #000000;">----------------------------------</span></strong></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span style="color: #ff6600;">*本文为Alima原创,转载注明格式[转载][博客园][Alima][关于flask-login中各种API使用实例],并在文首注明本文链接,多谢合作。</span></strong></p>
<p><strong><span style="color: #ff6600;">*非法转载及非法抄袭博文将依照网络著作权流程办理,请尊重作者劳动成果,最终解释权归Alima与博客园共同所有,感谢合作。</span></strong></p>
<p><strong><span style="color: #ff6600;">*关于恶意爬虫与删除关于博主信息的原文进行转载,请您高抬贵手,分享无价,别让新博主对这一行失去兴趣,营造良好的互联网环境。</span></strong></p>
<p>&nbsp;</p>
<p>Power by Alima | cnblogs。</p></div>
原创粉丝点击