前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Flask 博客接入第三方登录

Flask 博客接入第三方登录

作者头像
岂不美哉Frost
发布2019-12-02 13:36:42
1.8K0
发布2019-12-02 13:36:42
举报
文章被收录于专栏:Frost's BlogFrost's Blog

上一篇文章中我留了一部分内容,就是如何给评论登录接入第三方登录。我不希望来访问我博客的用户有太大的登录成本,否则本想留下些话的人,就会被挡在这个门槛之外。

Flask不像Django一样有各种现成的组件可以选用,Flask的各种扩展也不那么「开箱即用」。在我的博客项目中,我选用的是Authlib,它是国内的一名Python资深开发者@lepture开发的一款全面完善的OAuth认证库。大家可能在别的教程里会看到用的是flask-oauthlib,它们的作者其实是同一人,而且在2019年的今天,我绝对会推荐你用Authlib而不是flask-oauthlib。

网上能搜索到的教程,有很多都已经过时,或者不那么「与时俱进」了,截止今天Flask已经到1.1.1版本了,而很多教程还停留在0.10.x时代1。我是个喜欢与时俱进的人,我写的Flask相关文章,以及这个博客项目,保证都是基于最新的推荐,并会尽量保持更新。

开发思路

首先我们要搞清楚我们需要第三方登录来做什么。很简单,获取用户的邮箱地址(用于通知)、用户头像、用户名称(用于展示)这些基本的信息。登录时,我们到对应的平台上获取令牌,然后通过此令牌去请求用户信息,存到我们的数据库里,以备后面使用。如果大家对OAuth不太了解的,OAuth分为OAuth1协议与OAuth2协议,是一种开放的用户认证协议,它允许任何已注册的外部调用方(Client),获取平台(Provider)内部的授权访问的资源。OAuth2协议更加简化些,我预备接入的Github和Google都属于这一种协议,认证的主要过程是:

接入过程

Github的OAuth2接入是最简单的,很多教程都选择以Github为例,所以我这里选择用Google为例。 第一步,到Google API Console申请OAuth2凭据

选择Web应用,填入你的应用名称,和已获授权的重定向URI,在上图中,当你确认授权访问以后,Google会重定向到这个URI进行后续的动作。访问这个URI时会带上code的信息,一般地,这个URI的视图函数中应该做三件事情:

  1. 使用传入的code去Google交换访问令牌
  2. 存储访问令牌
  3. 使用访问令牌获取用户信息

完成了以后你就可以看到你的客户端ID客户端密钥了。

Authlib的使用

安装过程就不用说了,用pip安装即可。先在models.py中加入一个新的表:

Python

代码语言:javascript
复制
class OAuth2Token(db.Model):
    id = db.Column(db.Integer(), primary_key=True)
    name = db.Column(db.String(40))
    token_type = db.Column(db.String(40))
    access_token = db.Column(db.String(200))
    refresh_token = db.Column(db.String(200))
    expires_at = db.Column(db.Integer())
    user_id = db.Column(db.Integer(), db.ForeignKey("user.id"))
    user = db.relationship("User", backref=db.backref("tokens", lazy="dynamic"))

    def to_token(self):
        return dict(
            access_token=self.access_token,
            token_type=self.token_type,
            refresh_token=self.refresh_token,
            expires_at=self.expires_at,
        )

然后创建oauth对象:

Python

代码语言:javascript
复制
from authlib.integrations.flask_client import OAuth

def fetch_token(name):
    token = OAuth2Token.query.filter_by(name=name, user=current_user).first()
    return token.to_token()


def update_token(name, token, refresh_token=None, access_token=None):
    if refresh_token:
        item = OAuth2Token.filter_by(name=name, refresh_token=refresh_token).first()
    elif access_token:
        item = OAuth2Token.filter_by(name=name, access_token=access_token).first()
    else:
        return
    if not item:
        return
    # update old token
    item.access_token = token['access_token']
    item.refresh_token = token.get('refresh_token')
    item.expires_at = token['expires_at']
    db.session.commit()


oauth = OAuth(fetch_token=fetch_token, update_token=update_token)

google = oauth.register(
    name='google',
    access_token_url='https://www.googleapis.com/oauth2/v4/token',
    access_token_params={'grant_type': 'authorization_code'},
    authorize_url='https://accounts.google.com/o/oauth2/v2/auth?access_type=offline',
    authorize_params=None,
    api_base_url='https://www.googleapis.com/',
    client_kwargs={'scope': 'email profile'}
)

fetch_tokenupdate_token两个函数是Authlib需要用来获取和更新令牌用的。然后,在配置文件中加入两个配置:

Python

代码语言:javascript
复制
GOOGLE_CLIENT_ID = os.getenv('GOOGLE_CLIENT_ID')
GOOGLE_CLIENT_SECRET = os.getenv('GOOGLE_CLIENT_SECRET')

因为这两个配置是敏感信息,推荐从环境变量读取,不要暴露在代码库中。记得在create_app中将oauth对象注册到Flask中:

Python

代码语言:javascript
复制
oauth.init_app(app)

好了,现在我们可以来写视图了:

Python

代码语言:javascript
复制
def google_login():
    origin_url = request.headers['Referer']
    session['oauth_origin'] = origin_url
    redirect_uri = url_for('.google_auth', _external=True)
    if not current_app.debug:
        redirect_uri = redirect_uri.replace('http://', 'https://')
    return google.authorize_redirect(redirect_uri)

def google_auth():
    token = google.authorize_access_token()
    # save token
    resp = google.get('oauth2/v3/userinfo')
    resp.raise_for_status()
    profile = resp.json()
    # save profile

注意到我在login函数中把request.headers['Referer']的值保存到了会话中,这是为了登录成功后跳转会原来的页面,而中途会跳转到外部的网址,所以需要把原地址记下来。跳转google认证地址的URL中需要包含回调的地址,而这个地址必须和之前在Google API Console中配置的地址一致(可以允许是子页面)。现在我们就可以使用第三方登录了。

进一步简化

大家可以发现这样使用我们必须知道Google的认证地址、令牌地址和一些额外请求参数,虽然我们可以查阅Google OAuth文档获取这些信息,但这多少也是一种负担。所以authlib甚至提供一个库loginpass,包含几乎所有主流的OAuth提供方,使用loginpass以后,上面的三段代码可以替换成下面几行:

Python

代码语言:javascript
复制
from flask import Flask
from authlib.integrations.flask_client import OAuth
from loginpass import create_flask_blueprint, Google

app = Flask(__name__)
oauth = OAuth(app)

def handle_authorize(remote, token, user_info):
    if token:
        save_token(remote.name, token)
    if user_info:
        save_user(user_info)
        return user_page
    raise some_error

github_bp = create_flask_blueprint(Google, oauth, handle_authorize)
app.register_blueprint(github_bp, url_prefix='/google')

我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=23bvqemu5etcw

  1. 比如Flask-Script这个扩展,我不推荐任何新的Flask项目使用,因为Flask从0.11.0开始已经内置了命令行的支持。
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-11-27T,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 开发思路
  • 接入过程
  • Authlib的使用
  • 进一步简化
相关产品与服务
访问管理
访问管理(Cloud Access Management,CAM)可以帮助您安全、便捷地管理对腾讯云服务和资源的访问。您可以使用CAM创建子用户、用户组和角色,并通过策略控制其访问范围。CAM支持用户和角色SSO能力,您可以根据具体管理场景针对性设置企业内用户和腾讯云的互通能力。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档