用 Flask 來寫個輕部落格 (23) — 應用 OAuth 來實現 Facebook 第三方登入
目錄
前文列表
擴充套件閱讀
第三方登入流程
Resource Owner:資源所有者,本文中又稱”使用者”(user)。
Authorization server:認證伺服器,即服務提供商專門用來處理認證的伺服器。
Resource server:資源伺服器,即服務提供商存放使用者生成的資源的伺服器。它與認證伺服器,可以是同一臺伺服器,也可以是不同的伺服器。
(A)使用者開啟客戶端以後,客戶端要求使用者給予授權。
(B)使用者同意給予客戶端授權。
(C)客戶端使用上一步獲得的授權,向認證伺服器申請令牌。
(D)認證伺服器對客戶端進行認證以後,確認無誤,同意發放令牌。
(E)客戶端使用令牌,向資源伺服器申請獲取資源。
(F)資源伺服器確認令牌無誤,同意向客戶端開放資源。
不難看出來,上面六個步驟之中,(B) 是整個流程的關鍵,有了這個授權以後,客戶端就可以獲取令牌,進而憑令牌獲取資源。 那使用者怎樣才能給於客戶端授權? 我們可以使用 OAuth 來實現.
OAuth
OAuth 的作用就是讓”客戶端(blog application)”安全可控地獲取”服務商使用者(Facebook User)”帳號的授權,與”服務商提供商(Facebook)”進行互動。
OAuth在”客戶端”與”服務提供商”之間,設定了一個授權層(authorization layer)。”客戶端”不能直接登入”服務提供商”,只能登入授權層,以此將使用者與客戶端分離。”客戶端”登入授權層所用的令牌(token),與使用者的密碼不同。使用者可以在登入的時候,指定授權層令牌的許可權範圍和有效期。
“客戶端”登入授權層以後,”服務提供商”根據令牌的許可權範圍和有效期,向”客戶端”開放使用者儲存的資料。由此可見, 使用者賬戶的安全性就交由這些知名的可靠的”服務提供商”來處理了, 可以更好的降低使用者登入 blog 的信任成本.
應用 OAuth 實現 Facebook 第三方登入
- 安裝
pip install Flask-OAuth
pip freeze > requirements.txt
初始化 OAuth 物件, 並使用該物件來獲取 facebook 物件
vim extensions.py
from flask_oauth import OAuth
# Create the Flask-OAuth's instance
oauth = OAuth()
# facebook 第三方登入介面
# Create the auth object for facebook.
facebook = oauth.remote_app(
'facebook',
base_url='https://graph.facebook.com/',
request_token_url=None,
access_token_url='/oauth/access_token',
authorize_url='https://www.facebook.com/dialog/oauth',
consumer_key='<Your_app_number>',
consumer_secret='<Your_app_secret>',
request_token_params={'scope': 'email'})
@facebook.tokengetter
def get_facebook_token():
return session.get('facebook_oauth_token')
NOTE 1: facebook 物件是上述步驟中建立的 facebook 應用物件, 其包含了認證的結果資訊和請求授權的 facebook user 資訊. facebook 是完成第三方登入流程的介面物件.
NOTE 2: 方法 get_facebook_oauth_token()
獲取 facebook 發放的 token. 當成功建立 facebook 物件並完成授權時, 表示 client(blog) 成功的向 Authorization server 完成認證並接受到服務端的 token, 並儲存在 session 中. 然後 Client 就可以使用這個 token 去訪問 facebook 的資源(賬戶許可權資源)了.
- 實現在 client 觸發使用者授權的邏輯
vim jmilkfansblog/controllers/main.py
from jmilkfansblog.extensions import openid, facebook
# 當訪問 /facebook 時, 觸發授權流程
main_blueprint.route('/facebook')
def facebook_login():
return facebook.authorize(
callback=url_for('main.facebook_authorized',
next=request.referrer or None,
_external=True))
# 該檢視會接受從 facebook 認證伺服器返回的 resp 物件, 可以通過 resp 物件來判斷 Client 在 Server 上的認證結果.
@main_blueprint.route('/facebook/authorized')
@facebook.authorized_handler
def facebook_authorized(resp):
if resp is None:
return 'Access denied: reason=%s error=%s' % (
request.args['error_reason'],
request.args['error_description'])
session['facebook_oauth_token'] = (resp['access_token'], '')
me = facebook.get('/me')
if me.data.get('first_name', False):
facebook_username = me.data['first_name'] + " " + me.data['last_name']
else:
facebook_username = me.data['name']
user = User.query.filter_by(username=facebook_username).first()
if user is None:
user = User(id=str(uuid4()), username=facebook_username, password='jmilkfan')
db.session.add(user)
db.session.commit()
flash('You have been logged in.', category='success')
return redirect(url_for('blog.home'))
- 實現登入連結
<div class="row">
<div class="form-group">
<a href="{{ url_for('main.facebook_login') }}">Login via Facebook</a>
</div>
</div>
實現效果
訪問 /faceboook/authorized 進入使用者授權頁面:
NOTE: 需要首先登入到 facebook 之後, 才能夠進入授權的處理流程.
授權並登入成功: