Flask之用戶登錄

關(guān)于Flask-Login的用例

重訪我們的用戶模型

Flask-Login擴(kuò)展需要在我們的User類里實(shí)現(xiàn)一些方法。除了這些方法以外参袱,類沒有被要求實(shí)現(xiàn)其它方法善炫。

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')
    #is_authenticated方法是一個(gè)誤導(dǎo)性的名字的方法,通常這個(gè)方法應(yīng)該返回True隘击,除非對(duì)象代表一個(gè)由于某種原因沒有被認(rèn)證的用戶瘟仿。
    def is_authenticated(self):
        return True
    #is_active方法應(yīng)該為用戶返回True除非用戶不是激活的箱锐,例如,他們已經(jīng)被禁了劳较。
    def is_active(self):
        return True
    #is_anonymous方法應(yīng)該為那些不被獲準(zhǔn)登錄的用戶返回True
    def is_anonymous(self):
        return False
    #get_id方法為用戶返回唯一的unicode標(biāo)識(shí)符驹止。我們用數(shù)據(jù)庫(kù)層生成唯一的id浩聋。
    def get_id(self):
        return unicode(self.id)

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

用戶加載回調(diào)

@lm.user_loader
def load_user(id):
    return User.query.get(int(id))
函數(shù)原型
flask.ext.login.login_user(user, remember=False, force=False, fresh=True)

記住Flask-Login里的user id一直是unicode類型的,所以在我們把id傳遞給Flask-SQLAlchemy時(shí)臊恋,有必要把它轉(zhuǎn)化成integer類型.

登錄視圖函數(shù)

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'])

給視圖函數(shù)添加了一個(gè)新的裝飾器:oid.loginhandler衣洁。它告訴Flask-OpenID這是我們的登錄視圖函數(shù)。
在方法體的開頭抖仅,我們檢測(cè)是是否用戶是已經(jīng)經(jīng)過登錄認(rèn)證的闸与,如果是就重定向到index頁面。這兒的思路是如果一個(gè)用戶已經(jīng)登錄了岸售,那么我們不會(huì)讓它做二次登錄。
全局變量g是Flask設(shè)置的厂画,在一個(gè)request生命周期中凸丸,用來存儲(chǔ)和共享數(shù)據(jù)的變量。所以我猜你已經(jīng)想到了袱院,我們將把已經(jīng)登錄的用戶放到g變量里屎慢。
我們?cè)谡{(diào)用redirect()時(shí)使用的url_for()方法是Flask定義的從給定的view方法獲取url。如果你想重定向到index頁面忽洛,你h很可能使用redirect('/index')腻惠,但是我們有很好的理由讓Flask為你構(gòu)造url。
當(dāng)我們從登錄表單得到返回?cái)?shù)據(jù)欲虚,接下來要運(yùn)行的代碼也是新寫的集灌。這兒我們做兩件事。首先我們保存remember_me的布爾值到Flask的session中复哆,別和Flask-SQLAlchemy的db.session混淆了欣喧。我們已經(jīng)知道在一個(gè)request的生命周期中用Flask的g對(duì)象來保存和共享數(shù)據(jù)。沿著這條線路Flask的session提供了更多梯找,更復(fù)雜的服務(wù)唆阿。一旦數(shù)據(jù)被保存到session中,它將在同一客戶端發(fā)起的這次請(qǐng)求和這次以后的請(qǐng)求中永存而不會(huì)消亡锈锤。數(shù)據(jù)將保持在session中直到被明確的移除驯鳖。為了做到這些,F(xiàn)lask為每個(gè)客戶端建立各自的session久免。
下面的oid.try_login是通過Flask-OpenID來執(zhí)行用戶認(rèn)證浅辙。這個(gè)方法有兩個(gè)參數(shù),web表單提供的openid和OpenID provider提供的我們想要的list數(shù)據(jù)項(xiàng)阎姥。由于我們定義了包含nickname和email的User類摔握,所以我們要從找nickname和email這些項(xiàng)。

基于OpenID的認(rèn)證是異步的丁寄。如果認(rèn)證成功氨淌,F(xiàn)lask-OpenID將調(diào)用有由oid.after_login裝飾器注冊(cè)的方法泊愧。如果認(rèn)證失敗那么用戶會(huì)被重定向到login頁面。

Flask-OpenID登錄回調(diào)

@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參數(shù)包含了OpenID provider返回的一些信息盛正。

第一個(gè)if聲明僅僅是為了驗(yàn)證删咱。我們要求一個(gè)有效的email,所以一個(gè)沒有沒提供的email我們是沒法讓他登錄的豪筝。

全局變量g.user

如果你注意力很集中痰滋,那么你應(yīng)該記得在login view方法中我們通過檢查g.user來判斷一個(gè)用戶是否登錄了。為了實(shí)現(xiàn)這個(gè)我們將使用Flask提供的before_request事件续崖。任何一個(gè)被before_request裝飾器裝飾的方法將會(huì)在每次request請(qǐng)求被收到時(shí)提前與view方法執(zhí)行敲街。所以在這兒來設(shè)置我們的g.user變量

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

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)

在這個(gè)方法中只有兩處變動(dòng)。首先严望,我們?cè)黾恿薼ogin_required裝飾器多艇。這樣表明了這個(gè)頁面只有登錄用戶才能訪問。

另一個(gè)改動(dòng)是把g.user傳給了模板像吻,替換了之間的假對(duì)象峻黍。

注銷登錄

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

<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>

***碰到的問題:
if g.user is not None and g.user.is_authenticated():
TypeError: 'bool' object is not callable

原因居然是:
https://plus.google.com/+MiguelGrinberg/posts/9o9idDfaYhK,尼瑪拨匆,Miguel Grinberg這貨說Flask-Login的0.3版本有問題姆涩,等他有時(shí)間再改,先用0.2.11惭每。
好骨饿,果斷換回舊版本:

pip uninstall Flask-Login
pip install -v Flask-Login==0.2.11

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市台腥,隨后出現(xiàn)的幾起案子样刷,更是在濱河造成了極大的恐慌,老刑警劉巖览爵,帶你破解...
    沈念sama閱讀 222,183評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件置鼻,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡蜓竹,警方通過查閱死者的電腦和手機(jī)箕母,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來俱济,“玉大人嘶是,你說我怎么就攤上這事≈肼担” “怎么了聂喇?”我有些...
    開封第一講書人閱讀 168,766評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我希太,道長(zhǎng)克饶,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,854評(píng)論 1 299
  • 正文 為了忘掉前任誊辉,我火速辦了婚禮矾湃,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘堕澄。我一直安慰自己邀跃,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評(píng)論 6 398
  • 文/花漫 我一把揭開白布蛙紫。 她就那樣靜靜地躺著拍屑,像睡著了一般。 火紅的嫁衣襯著肌膚如雪坑傅。 梳的紋絲不亂的頭發(fā)上僵驰,一...
    開封第一講書人閱讀 52,457評(píng)論 1 311
  • 那天,我揣著相機(jī)與錄音裁蚁,去河邊找鬼。 笑死继准,一個(gè)胖子當(dāng)著我的面吹牛枉证,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播移必,決...
    沈念sama閱讀 40,999評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼室谚,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了崔泵?” 一聲冷哼從身側(cè)響起秒赤,我...
    開封第一講書人閱讀 39,914評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎憎瘸,沒想到半個(gè)月后入篮,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,465評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡幌甘,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評(píng)論 3 342
  • 正文 我和宋清朗相戀三年潮售,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片锅风。...
    茶點(diǎn)故事閱讀 40,675評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡酥诽,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出皱埠,到底是詐尸還是另有隱情肮帐,我是刑警寧澤,帶...
    沈念sama閱讀 36,354評(píng)論 5 351
  • 正文 年R本政府宣布边器,位于F島的核電站训枢,受9級(jí)特大地震影響托修,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜肮砾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評(píng)論 3 335
  • 文/蒙蒙 一诀黍、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧仗处,春花似錦眯勾、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至洋幻,卻和暖如春郁轻,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背文留。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工好唯, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人燥翅。 一個(gè)月前我還...
    沈念sama閱讀 49,091評(píng)論 3 378
  • 正文 我出身青樓骑篙,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親森书。 傳聞我的和親對(duì)象是個(gè)殘疾皇子靶端,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評(píng)論 2 360

推薦閱讀更多精彩內(nèi)容

  • 22年12月更新:個(gè)人網(wǎng)站關(guān)停,如果仍舊對(duì)舊教程有興趣參考 Github 的markdown內(nèi)容[https://...
    tangyefei閱讀 35,189評(píng)論 22 257
  • 使用會(huì)話 使用會(huì)話對(duì)象是Flask中實(shí)現(xiàn)身份認(rèn)證的一種方式凛膏。會(huì)話(session)讓服務(wù)端可以通過一種簡(jiǎn)單的方式杨名,...
    welkinz閱讀 2,264評(píng)論 1 1
  • 第二部分 Blog例子 第八章 用戶驗(yàn)證 大部分程序需要追蹤用戶身份。當(dāng)用戶連接到程序猖毫,通過一系列步驟使自己的身份...
    易木成華閱讀 1,295評(píng)論 0 4
  • 最近在學(xué)習(xí)flask台谍,用到flask-login,發(fā)現(xiàn)網(wǎng)上只有0.1版本的中文文檔吁断,看了官方已經(jīng)0.4了典唇,并且添加...
    ZZES_ZCDC閱讀 5,956評(píng)論 3 24
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)胯府,斷路器介衔,智...
    卡卡羅2017閱讀 134,707評(píng)論 18 139