Python的Flask框架中实现简单的登录功能的教程

 回顾

在前面的系列章节中,我们创建了一个数据库并且学着用用户和邮件来填充,但是到现在我们还没能够植入到我们的程序中。 两章之前,我们已经看到怎么去创建网络表单并且留下了一个实现完全的登陆表单。

在这篇文章中,我们将基于我门所学的网络表单和数据库来构建并实现我们自己的用户登录系统。教程的最后我们小程序会实现新用户注册,登陆和退出的功能。

为了能跟上这章节,你需要前一章节最后部分,我们留下的微博程序。请确保你的程序已经正确安装和运行。

在前面的章节,我们开始配置我们将要用到的Flask扩展。为了登录系统,我们将使用两个扩展,Flask-Login 和 Flask-OpenID. 配置如下所示 (fileapp\__init__.py):

import os
from flaskext.login import LoginManager
from flaskext.openid import OpenID
from config import basedir

lm = LoginManager()
lm.setup_app(app)
oid = OpenID(app, os.path.join(basedir, 'tmp'))

Flask-OpenID 扩展为了可以存储临时文件,需要一个临时文件夹路径。为此,我们提供了它的位置。

重访我们的用户模型

Flask-Login扩展需要在我们的User类里实现一些方法。除了这些方法以外,类没有被要求实现其它方法。

下面是我们的User类 (fileapp/models.py):

class User(db.Model):
 id = db.Column(db.Integer, primary_key = True)
 nickname = db.Column(db.String(64), unique = True)
 email = db.Column(db.String(120), unique = True)
 role = db.Column(db.SmallInteger, default = ROLE_USER)
 posts = db.relationship('Post', backref = 'author', lazy = 'dynamic')

 def is_authenticated(self):
  return True

 def is_active(self):
  return True

 def is_anonymous(self):
  return False

 def get_id(self):
  return unicode(self.id)

 def __repr__(self):
  return '<User %r>' % (self.name)

is_authenticated方法是一个误导性的名字的方法,通常这个方法应该返回True,除非对象代表一个由于某种原因没有被认证的用户。

is_active方法应该为用户返回True除非用户不是激活的,例如,他们已经被禁了。

is_anonymous方法应该为那些不被获准登录的用户返回True。

最后,get_id方法为用户返回唯一的unicode标识符。我们用数据库层生成唯一的id。

用户加载回调

现在我们通过使用Flask-Login和Flask-OpenID扩展来实现登录系统

首先,我们需要写一个方法从数据库加载到一个用户。这个方法会被Flask-Login使用(fileapp/views.py):

@lm.user_loader
def load_user(id):
 return User.query.get(int(id))

记住Flask-Login里的user id一直是unicode类型的,所以在我们把id传递给Flask-SQLAlchemy时,有必要把它转化成integer类型。

登录视图函数

接下来我们要更新登录视图函数(fileapp/views.py):

from flask import render_template, flash, redirect, session, url_for, request, g
from flaskext.login import login_user, logout_user, current_user, login_required
from app import app, db, lm, oid
from forms import LoginForm
from models import User, ROLE_USER, ROLE_ADMIN

@app.route('/login', methods = ['GET', 'POST'])
@oid.loginhandler
def login():
 if g.user is not None and g.user.is_authenticated():
  return redirect(url_for('index'))
 form = LoginForm()
 if form.validate_on_submit():
  session['remember_me'] = form.remember_me.data
  return oid.try_login(form.openid.data, ask_for = ['nickname', 'email'])
 return render_template('login.html',
  title = 'Sign In',
  form = form,
  providers = app.config['OPENID_PROVIDERS'])

注意到我们导入了一些新的模块,其中有些后面会用到。

跟上个版本的变化很小。我们给视图函数添加了一个新的装饰器:oid.loginhandler。它告诉Flask-OpenID这是我们的登录视图函数。

在方法体的开头,我们检测是是否用户是已经经过登录认证的,如果是就重定向到index页面。这儿的思路是如果一个用户已经登录了,那么我们不会让它做二次登录。

全局变量g是Flask设置的,在一个request生命周期中,用来存储和共享数据的变量。所以我猜你已经想到了,我们将把已经登录的用户放到g变量里。

我们在调用redirect()时使用的url_for()方法是Flask定义的从给定的view方法获取url。如果你想重定向到index页面,你h很可能使用redirect('/index'),但是我们有很好的理由让Flask为你构造url。
 
当我们从登录表单得到返回数据,接下来要运行的代码也是新写的。这儿我们做两件事。首先我们保存remember_me的布尔值到Flask的session中,别和Flask-SQLAlchemy的db.session混淆了。我们已经知道在一个request的生命周期中用Flask的g对象来保存和共享数据。沿着这条线路Flask的session提供了更多,更复杂的服务。一旦数据被保存到session中,它将在同一客户端发起的这次请求和这次以后的请求中永存而不会消亡。数据将保持在session中直到被明确的移除。为了做到这些,Flask为每个客户端建立各自的session。

下面的oid.try_login是通过Flask-OpenID来执行用户认证。这个方法有两个参数,web表单提供的openid和OpenID provider提供的我们想要的list数据项。由于我们定义了包含nickname和email的User类,所以我们要从找nickname和email这些项。

基于OpenID的认证是异步的。如果认证成功,Flask-OpenID将调用有由oid.after_login装饰器注册的方法。如果认证失败那么用户会被重定向到login页面。

Flask-OpenID登录回调

这是我们实现的after_login方法(app/views.py)

@oid.after_login
def after_login(resp):
 if resp.email is None or resp.email == "":
  flash('Invalid login. Please try again.')
  redirect(url_for('login'))
 user = User.query.filter_by(email = resp.email).first()
 if user is None:
  nickname = resp.nickname
  if nickname is None or nickname == "":
   nickname = resp.email.split('@')[0]
  user = User(nickname = nickname, email = resp.email, role = ROLE_USER)
  db.session.add(user)
  db.session.commit()
 remember_me = False
 if 'remember_me' in session:
  remember_me = session['remember_me']
  session.pop('remember_me', None)
 login_user(user, remember = remember_me)
 return redirect(request.args.get('next') or url_for('index'))

传给after_login方法的resp参数包含了OpenID provider返回的一些信息。

第一个if声明仅仅是为了验证。我们要求一个有效的email,所以一个没有没提供的email我们是没法让他登录的。

接下来,我们将根据email查找数据库。如果email没有被找到我们就认为这是一个新的用户,所以我们将在数据库中增加一个新用户,做法就像我们从之前章节学到的一样。注意我们没有处理nickname,因为一些OpenID provider并没有包含这个信息。

做完这些我们将从Flask session中获取remember_me的值,如果它存在,那它是我们之前在login view方法中保存到session中的boolean类型的值。

然后我们调用Flask-Login的login_user方法,来注册这个有效的登录。

最后,在最后一行我们重定向到下一个页面,或者如果在request请求中没有提供下个页面时,我们将重定向到index页面。

跳转到下一页的这个概念很简单。比方说我们需要你登录才能导航到一个页面,但你现在并未登录。在Flask-Login中你可以通过login_required装饰器来限定未登录用户。如果一个用户想连接到一个限定的url,那么他将被自动的重定向到login页面。Flask-Login将保存最初的url作为下一个页面,一旦登录完成我们便跳转到这个页面。

做这个工作Flask-Login需要知道用户当前在那个页面。我们可以在app的初始化组件里配置它(app/__init__.py):

lm = LoginManager()
lm.setup_app(app)
lm.login_view = 'login'

全局变量g.user

如果你注意力很集中,那么你应该记得在login view方法中我们通过检查g.user来判断一个用户是否登录了。为了实现这个我们将使用Flask提供的before_request事件。任何一个被before_request装饰器装饰的方法将会在每次request请求被收到时提前与view方法执行。所以在这儿来设置我们的g.user变量(app/views.py):

@app.before_request
def before_request():
 g.user = current_user

这就是它要做的一切,current_user全局变量是被Flask-Login设定的,所以我们只需要把它拷贝到更容易被访问的g变量就OK了。这样,所有的请求都能访问这个登录的用户,甚至于内部的模板。

index视图

在之前的章节中我们用假代码遗留了我们的index视图,因为那个时候我们系统里并没有用户和博客文章。现在我们有用户了,所以,让我们来完成它吧:

@app.route('/')
@app.route('/index')
@login_required
def index():
 user = g.user
 posts = [
  {
   'author': { 'nickname': 'John' },
   'body': 'Beautiful day in Portland!'
  },
  {
   'author': { 'nickname': 'Susan' },
   'body': 'The Avengers movie was so cool!'
  }
 ]
 return render_template('index.html',
  title = 'Home',
  user = user,
  posts = posts)

在这个方法中只有两处变动。首先,我们增加了login_required装饰器。这样表明了这个页面只有登录用户才能访问。

另一个改动是把g.user传给了模板,替换了之间的假对象。

现在可以运行我们的应用了。

当我们连接到http://localhost:5000你将会看到登陆页面。记着如果你通过OpenID登录那么你必须使用你的提供者提供的OpenID URL。你可以下面URL中的任何一个OpenID provider来为你产生一个正确的URL。

作为登录进程的一部分,你将会被重定向到OpenID提供商的网站,你将在那儿认证和授权你共享给我们应用的一些信息(我们只需要email和nickname,放心,不会有任何密码或者其他个人信息被曝光)。

一旦登录完成你将作为已登录用户被带到index页面。

试试勾选remember_me复选框。有了这个选项当你在浏览器关闭应用后重新打开时,你还是已登录状态。

注销登录

我们已经实现了登录,现在是时候来实现注销登录了。

注销登录的方法灰常简单(file app/views.py):

@app.route('/logout')
def logout():
 logout_user()
 return redirect(url_for('index'))

但我们在模板中还没有注销登录的链接。我们将在base.html中的顶部导航栏添加这个链接(file app/templates/base.html):

<html>
 <head>
 {% if title %}
 <title>{{title}} - microblog</title>
 {% else %}
 <title>microblog</title>
 {% endif %}
 </head>
 <body>
 <div>Microblog:
  <a href="{{ url_for('index') }}">Home</a>
  {% if g.user.is_authenticated() %}
  | <a href="{{ url_for('logout') }}">Logout</a>
  {% endif %}
 </div>
 <hr>
 {% with messages = get_flashed_messages() %}
 {% if messages %}
 <ul>
 {% for message in messages %}
  <li>{{ message }} </li>
 {% endfor %}
 </ul>
 {% endif %}
 {% endwith %}
 {% block content %}{% endblock %}
 </body>
</html>

这是多么多么简单啊,我们只需要检查一下g.user中是否有一个有效的用户,如果有我们就添加注销链接。在我们的模板中我们再一次使用了url_for方法。

最后的话
我们现在有了一个全功能的用户登录系统。在下一章中,我们将创建用户的个人资料页,并显示用户的头像。

在此期间,这里是更新的应用程序代码,包括在这篇文章中的所有变化:

下载 microblog-0.5.zip

(0)

相关推荐

  • 使用Python的Flask框架实现视频的流媒体传输

    Flask 是一个 Python 实现的 Web 开发微框架.这篇文章是一个讲述如何用它实现传送视频数据流的详细教程. 我敢肯定,现在你已经知道我在O'Reilly Media上发布了有关Flask的一本书和一些视频资料.在这些上面,Flask框架介绍的覆盖面是相当完整的,出于某种原因,也有一小部分的功能没有太多的提到,因此我认为在这里写一篇介绍它们的文章是一个好主意. 这篇文章是专门介绍流媒体的,这个有趣的功能让Flask应用拥有这样一种能力,以分割成小数据块的方式,高效地为大型请求提供数据,

  • Python使用Flask框架同时上传多个文件的方法

    本文实例讲述了Python使用Flask框架同时上传多个文件的方法,分享给大家供大家参考.具体如下: 下面的演示代码带有详细的html页面和python代码 import os # We'll render HTML templates and access data sent by POST # using the request object from flask. Redirect and url_for # will be used to redirect the user once t

  • Flask框架的学习指南之开发环境搭建

    Flask是一个使用 Python 编写的轻量级 Web 应用框架.其 WSGI 工具箱采用 Werkzeug ,模板引擎则使用 Jinja2.很多功能的实现都参考了django框架.由于项目需要,在此记录下学习的过程及心得. 工欲善其事,必先利其器.就从搭建一套flask开发环境开始flask之旅吧. 一.平台说明 操作系统:window 7  64bit  数据库:mysql5.6  python:v2.7  开发集成软件:PyCharm5.0 二.开发环境搭建 1.安装flask框架包 1

  • 在Linux上安装Python的Flask框架和创建第一个app实例的教程

    无论你在linux上娱乐还是工作,这对你而言都是一个使用python来编程的很好的机会.回到大学我希望他们教我的是Python而不是Java,这学起来很有趣且在实际的应用如yum包管理器中很有用. 本篇教程中我会带你使用python和一个称为flask的微型框架来构建一个简单的应用,来显示诸如每个进程的内存使用,CPU百分比之类有用的信息. 前置需求 Python基础.列表.类.函数.模块.HTML/CSS (基础). 学习这篇教程你不必是一个python高级开发者,但是首先我建议你阅读http

  • Flask框架学习笔记(一)安装篇(windows安装与centos安装)

    Flask 依赖于两个外部库: Werkzeug  和  Jinja2  . Werkzeug 是一个 WSGI (在 web 应用和多种服务器之间开发和部署的标准 Python 接口) 的工具集,Jinja2 负责渲染模板. 一.安装 Flask安装的前提条件 1.已安装python2.x版本 2.已安装easy_install 在安装flask之前,你必须要先安装python和easy_install,easy_install只支持pyhon2.x版本不支持python3.x版本,所以你在安

  • Python的Flask框架中@app.route的用法教程

    在我上一篇文章,我搭了一个框架,模拟了Flask网站上"@app.route('/')"第一条例子的行为. 如果你错过了那篇"这不是魔法",请点击这里. 在这篇文章中,我们打算稍微调高点难度,为我们的URL加入可变参数的能力,在本文的最后,我们将支持下述代码段所期望达到的行为. app = Flask(__name__) @app.route("/hello/<username>") def hello_user(username):

  • Python的Flask框架与数据库连接的教程

     命令行方式运行Python脚本 在这个章节中,我们将写一些简单的数据库管理脚本.在此之前让我们来复习一下如何通过命令行方式执行Python脚本. 如果Linux 或者OS X的操作系统,需要有执行脚本的权限.例如: chmod a+x script.py 该脚本有个指向使用解释器的命令行.再脚本赋予执行权限后就可以通过命令行执行,就像这样: like this: ./script.py <arguments> 然而,在Windows系统上这样做是不行的,你必须提供Python解释器作为必选参

  • Flask框架的学习指南之制作简单blog系统

    之前写了一篇flask开发环境搭建,今天继续,进行一个实战小项目-blog系统. blog系统很简单,只有一个页面,然后麻雀虽小五脏俱全.这里目的不是为了做项目而做项目,这篇文章本意是通过这次练习传达以下几个知识点: 1.从全局上了解flask项目的目录结构 2.flask项目的运行机制 3.flask框架实现MVC架构 4.flask-sqlalchemy 操作mysql数据库 一.新建项目:blog系统 在pycharm中,新建flask项目,如下图: 完成后的目录结构是这样的:非常简单,一

  • 30分钟搭建Python的Flask框架并在上面编写第一个应用

    Flask 是一种很赞的Python web框架.它极小,简单,最棒的是它很容易学. 今天我来带你搭建你的第一个Flask web应用!和官方教程 一样,你将搭建你自己的微博客系统:Flaskr.和官方Flask教程不同的是--我们通过使用Stormpath来创建并管理用户账户和数据,你的工作效率会更高.开发进程会显著地加快! 我们这就开始吧. 注意:这篇教程面向Flask开发新人,帮助他们理解如何使用Flask和Stormpath建立一个简单的网站.本文是Flask官方教程的改版. 目标 Fl

  • Python的Flask框架中web表单的教程

     概要 在前面章节我们为主页定义了一个简单的模板,部分尚未实现的模块如用户或帖子等使用模拟的对象作为临时占位. 本章我们将看到如何利用web表单填补这些空白. web表单是web应用中最基本的构建要素,我们将通过表单来实现用户发帖和应用登录功能. 完成本章内容你需要基于前面章节完成的微博应用代码,请确认这些代码已安装并能正常运行. 配置 Flask-WTF是WTForms项目的Flask框架扩展,我们将用他来帮助我们处理web表单. 大部分Flask扩展都需要定义相关配置项,所以我们先来在应用根

  • Flask框架的学习指南之用户登录管理

    继续flask的学习之旅.今天介绍flask的登陆管理模块,还记得上一篇中的blog小项目么,登录是咱们自己写的验证代码,大概有以下几个步骤: 1.在登录框中输入用户名和密码 2.flask view函数获取用户密码,然后到数据库中查询该用户信息,进行匹配 3.如果成功,就写入session中,重定向到首页 4.如果对于特定视图,必须要登录才能访问,那么需要在每个视图函数验证session里是否存在该用户. 今天继续改造blog项目,介绍的flask-login模块就是替我们来搞定这些业务相关度

随机推荐