程序员最近都爱上了这个网站  程序员们快来瞅瞅吧!  it98k网:it98k.com

本站消息

站长简介/公众号

  出租广告位,需要合作请联系站长

+关注
已关注

分类  

python面试大全(3)

python前言(0)

标签  

python面试(2)

python(0)

日期归档  

Flask学习之五 用户登录

发布于2019-08-23 17:12     阅读(725)     评论(0)     点赞(28)     收藏(5)


英文博客地址:http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-v-user-logins

中文翻译地址:http://www.pythondoc.com/flask-mega-tutorial/userlogin.html

开源中国社区:http://www.oschina.net/translate/the-flask-mega-tutorial-part-v-user-logins

 

备注:我是三个一起看的,有些部分的中文翻译太拗口而且还有错,因此我选择是比较清晰的中文解释,而有些部分是直接翻译英文博客。

上一部分:Flask学习之四 数据库

 

一、配置

对于登录系统,我们将会使用到两个扩展,Flask-Login 和 Flask-OpenID。配置情况如下(文件 app__init__.py):

复制代码
import os
from flask.ext.login import LoginManager
from flask.ext.openid import OpenID
from config import basedir

lm = LoginManager()
lm.init_app(app)
oid = OpenID(app, os.path.join(basedir, 'tmp'))
复制代码

Flask-OpenID 扩展需要一个存储文件的临时文件夹的路径。对此,我们提供了一个 tmp 文件夹的路径。

 

二、重构用户模型

Flask-Login扩展需要在我们的User类里实现一些方法。

为 Flask-Login 实现的 User 类(文件 app/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.nickname)
复制代码

is_authenticated 方法:一般而言,这个方法应该只返回 True,除非表示用户的对象因为某些原因不允许被认证。

is_active 方法:应该返回 True,除非用户是无效的,比如他们的账号被禁止。

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

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

 

三、User loader 回调

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

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

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

备注:其实我现在对python中的@符号的用法还是不甚明了。

注意在 Flask-Login 中的用户 ids 永远是 unicode 字符串,因此在我们把 id 发送给 Flask-SQLAlchemy 之前,需要把 id 转成整型,否则会报错!

 

四、登录视图函数

接着更新登录视图函数(文件 app/views.py):

复制代码
from flask import render_template, flash, redirect, session, url_for, request, g
from flask.ext.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

@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这是我们的登录视图函数。

在函数开始的时候我们就检查 g.user 是不是一个已经认证的用户,如果已经认证就直接跳转到主页面,避免二次登录。

Flask 中的 g 全局变量是一个在请求生命周期中用来存储和共享数据。登录的用户存储在这里(g)。

我们在调用redirect()时使用的url_for()方法是Flask定义的从给定的view方法获取url。如果你想重定向到index页面,你很可能使用redirect('/index'),但是让Flask为你构造url是有好处的。见 http://dormousehole.readthedocs.org/en/latest/quickstart.html#url 

当我们从登录表单得到返回数据,接下来要运行的代码也是新写的。这儿我们做两件事。首先我们保存remember_me的布尔值到Flask的 session中,别和Flask-SQLAlchemy的db.session混淆了。之前我们已经知道 flask.g 对象在请求整个生命周期中存储和共享数据。flask.session 提供了一个更加复杂的服务对于存储和共享数据。一旦数据存储在会话对象中,在来自同一客户端的现在和任何以后的请求都是可用的。数据将保持在session中直到被明确的移除。为了做到这些,Flask为每个客户端建立各自的 session。

oid.try_login通过Flask-OpenID来执行用户认证。该函数有两个参数,用户在 web 表单提供的 openid 以及我们从 OpenID 提供商得到的数据项列表。因为我们已经在用户模型类中定义了 nicknameemail,这也是我们将要从 OpenID 提供商索取的。

基于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.')
        return 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)
        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'))
复制代码

resp 参数传入给 after_login 函数,它包含了从 OpenID 提供商返回来的信息。

第一个 if 仅仅是为了验证。我们要求一个有效的email,所以如果不提供email,我们是没法让用户登录的。

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

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

然后,为了注册这个有效的登录,我们调用 Flask-Login 的 login_user 函数。

最后,如果在 next 页没有提供的情况下,我们会重定向到首页,否则会重定向到 next 页。

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

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

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

备注:在修改(文件 app/__init__.py)的时候 “from app import views, models” 这句话需要放到最后,否则会报错,找不到 lm。

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

from app import views, models

 

六、全局变量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

 

七、index视图

之前的index视图是不适合现在的,修改如下(文件 app/views.py):

复制代码
@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://127.0.0.1:5000/ 会被重定向到登录页面

备注:我用的是yahoo的OpenID登录的,要用OpenID,你得先激活yahoo的OpenID,激活方法自行搜索,这里不赘述了。

我的登录时间有点长,这是我登录后的主页。

登录后没有登出之前你是没办法再回到登录页面的,它自动重定向回来。

 

八、用户登出

登出的视图函数是相当地简单(文件 app/views.py):

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

但我们在模板中还没有注销登录的链接。我们将在base.html中的顶部导航栏添加这个链接(文件 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>
复制代码

修改后页面:

 



所属网站分类: 技术文章 > 博客

作者:骷髅无悔

链接:https://www.pythonheidong.com/blog/article/55484/27aabd607266fa5a767c/

来源:python黑洞网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

28 0
收藏该文
已收藏

评论内容:(最多支持255个字符)