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

 回顾

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

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

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

在前面的章节,我们开始配置我们将要用到的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 。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索python
python web框架 flask、python flask框架、python的flask框架、python实现简单爬虫、flask框架,以便于您获取更多的相关知识。

时间: 2024-09-20 06:17:47

Python的Flask框架中实现简单的登录功能的教程_python的相关文章

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

  Python的Flask框架中实现简单的登录功能的教程,登录是各个web框架中的基础功能,需要的朋友可以参考下 回顾 在前面的系列章节中,我们创建了一个数据库并且学着用用户和邮件来填充,但是到现在我们还没能够植入到我们的程序中. 两章之前,我们已经看到怎么去创建网络表单并且留下了一个实现完全的登陆表单. 在这篇文章中,我们将基于我门所学的网络表单和数据库来构建并实现我们自己的用户登录系统.教程的最后我们小程序会实现新用户注册,登陆和退出的功能. 为了能跟上这章节,你需要前一章节最后部分,我们

在Python的Tornado框架中实现简单的在线代理的教程

  这篇文章主要介绍了在Python的Tornado框架中实现简单的在线代理的教程,代理功能是一个常见的网络编程实现,需要的朋友可以参考下 实现代理的方式很多种,流行的web服务器也大都有代理的功能,比如http://www.tornadoweb.cn用的就是nginx的代理功能做的tornadoweb官网的镜像. 最近,我在开发一个移动运用(以下简称APP)的后台程序(Server),该运用需要调用到另一平台产品(Platform)的API.对于这个系统来说,可选的一种实现方式方式是APP同时

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

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

Python的ORM框架中SQLAlchemy库的查询操作的教程_python

1. 返回列表和标量(Scalar) 前面我们注意到Query对象可以返回可迭代的值(iterator value),然后我们可以通过for in来查询.不过Query对象的all().one()以及first()方法将返回非迭代值(non-iterator value),比如说all()返回的是一个列表: >>> query = session.query(User).\ >>> filter(User.name.like('%ed')).order_by(User.

如何在Python的Flask框架中使用模版的入门教程

  如何在Python的Flask框架中使用模版的入门教程?          概述 如果你已经阅读过上一个章节,那么你应该已经完成了充分的准备工作并且创建了一个很简单的具有如下文件结构的Web应用: microblog |-flask文件夹 |-<一些虚拟环境的文件> |-app文件夹 | |-static文件夹 | |-templates文件夹 | |-__init__.py文件 | |-views.py文件 |-tmp文件夹 |-run.py文件 以上给你介绍了在Python的Flask

Python的Flask框架中实现分页功能的教程

  这篇文章主要介绍了Python的Flask框架中实现分页功能的教程,文中的示例基于一个博客来实现,需要的朋友可以参考下 Blog Posts的提交 让我们从简单的开始.首页上必须有一张用户提交新的post的表单. 首先我们定义一个单域表单对象(fileapp/forms.py): ? 1 2 class PostForm(Form): post = TextField('post', validators = [Required()]) 下面,我们把这个表单添加到template中(file

在Python的Flask框架中实现单元测试的教程

  在Python的Flask框架中实现单元测试的教程,属于自动化部署的方面,可以给debug工作带来诸多便利,需要的朋友可以参考下 概要 在前面的章节里我们专注于在我们的小应用程序上一步步的添加功能上.到现在为止我们有了一个带有数据库的应用程序,可以注册用户,记录用户登陆退出日志以及查看修改配置文件. 在本节中,我们不为应用程序添加任何新功能,相反,我们要寻找一种方法来增加我们已写代码的稳定性,我们还将创建一个测试框架来帮助我们防止将来程序中出现的失败和回滚. 让我们来找bug 在上一章的结尾

如何在Python的Flask框架中实现全文搜索?

  这篇文章主要介绍了在Python的Flask框架中实现全文搜索功能,这个基本的web功能实现起来非常简单,需要的朋友可以参考下 全文检索引擎入门 灰常不幸的是,关系型数据库对全文检索的支持没有被标准化.不同的数据库通过它们自己的方式来实现全文检索,而且SQLAlchemy在全文检索上也没有提供一个好的抽象. 我们现在使用SQLite作为我们的数据库,所以我们可以绕开SQLAlchemy而使用SQLite提供的工具来创建一个全文检索索引.但这么做不怎么好,因为如果有一天我们换用别的数据库,那么

在Python的Flask框架中使用日期和时间的教程

  这篇文章主要介绍了在Python的Flask框架中使用日期和时间的教程,包括对各个时区之间转换的一些处理,需要的朋友可以参考下 时间戳的问题 我们的微博应用的一个忽略了很久的问题就是日间和日期的显示. 直到现在,我们在我们的User和Post对象中使用Python它自己的方式来渲染时间对象,但这并不是一个好的解决方案. 考虑下这样的例子.我正在写这篇文章,此时正是12月31号下午3:54.我的时区是PST(或者你们更习惯的:UTC-8). 在Python解释器中运行,我得到下面输出: ? 1