如何创建 SSO 按钮 – Flask 登录教程
已发表: 2022-03-11应用程序通常需要登录功能,以便用户可以保存数据、创建自己的配置文件,或者可能只是限制对敏感资源的访问。 在现代应用程序中,用户希望拥有与登录相关的标准功能,例如电子邮件验证、密码重置或多因素身份验证。 这些功能虽然是必要的,但并不容易做好,而且通常不是应用程序的主要业务。
在用户方面,他们可能不想经历冗长的注册过程,因为他们需要创建并记住另一个电子邮件和密码对。 如果没有适当的密码管理器,用户往往会重复使用相同的密码,这在安全性方面很糟糕。
单点登录 (SSO),通常被用户称为使用社交媒体按钮登录,是为了解决这个问题而发明的。 对于用户来说,不用经历另一个痛苦的注册过程很容易。 对于企业来说,为用户消除摩擦总是一个巨大的胜利——对于开发人员来说,所有与登录相关的功能现在都委托给身份提供者(Facebook、Google、Twitter 等),这意味着更少的代码! 您的应用只是信任身份提供者来完成其验证用户身份的工作。
SSO 通常由 OpenId Connect (OIDC) 或 SAML 协议提供支持。 SAML 主要用于企业应用程序。 OIDC 建立在 OAuth2 之上,并被 Facebook、Google 等社交身份提供商使用。在这篇文章中,我们将重点介绍 OIDC/OAuth2 协议。
在这个 Flask 登录教程中,我们将编写一个分步指南,以将 SSO 登录按钮添加到使用 SimpleLogin 和 Facebook 作为身份提供者的 Flask 应用程序中。 这可以在不使用任何外部库的情况下完成,但为了简化 OAuth 的复杂性,我们将使用 Requests-OAuthlib,一个集成 OAuth 提供程序的库。 如果您有兴趣从头开始实施 SSO,请查看实施 SSO 登录 - 原始方式。
在本文的最后,你应该有一个 Flask 应用程序,它具有以下页面:
- 带有登录按钮的主页
- 用户信息页面,成功登录后,用户将能够看到诸如姓名、电子邮件和头像等信息
本教程的所有代码都可以在 flask-social-login-example 存储库中找到。
此处还提供了演示。 随意在 Glitch 上重新混合代码。
第 1 步:引导 Flask 应用程序
安装flask
和Requests-OAuthlib
。 您还可以使用virtualenv
或pipenv
来隔离环境。
点安装烧瓶请求_oauthlib
创建app.py
和在主页显示登录按钮的路由:
import flask app = flask.Flask(__name__) @app.route("/") def index(): return """ <a href="/login">Login</a> """ if __name__ == '__main__': app.run(debug=True)
让我们运行这个应用程序并验证一切正常:
python app.py
打开 http://localhost:5000 时应该会看到此页面。 完整的代码在 step1.py 上。
第 2 步:身份提供者凭证
目前有数百个(如果不是数千个)身份提供者,其中最受欢迎的是 Facebook、Google、GitHub 和 Instagram。 对于这篇文章,选择 SimpleLogin 是因为它对开发人员友好。 不过,相同的代码适用于任何 OAuth2 身份提供者。 (免责声明:我碰巧是 SimpleLogin 的联合创始人,这可能是我决定使用它的一个因素。)
如果您还没有帐户,请前往 SimpleLogin 并创建一个帐户,然后在“开发人员”选项卡中创建一个新应用程序。
在应用详情页面,请将您的 AppID 和 AppSecret 复制并保存到变量环境中。 在 OAuth 术语中,客户端实际上是指第三方应用程序,即您的应用程序。 我们可以将这些值直接放在代码中,但最好将凭据保存到环境变量中。 这也是十二因缘中的第三因缘。
export CLIENT_ID={your AppID} export CLIENT_SECRET={your AppSecret}
在app.py
中,请在文件顶部添加这些行以获取client id
和client secret
。
import os CLIENT_ID = os.environ.get("CLIENT_ID") CLIENT_SECRET = os.environ.get("CLIENT_SECRET")
还请在app.py
顶部添加这些 OAuth URL,这些 URL 将在下一步中使用。 它们也可以在 OAuth 端点页面上复制。
AUTHORIZATION_BASE_URL = "https://app.simplelogin.io/oauth2/authorize" TOKEN_URL = "https://app.simplelogin.io/oauth2/token" USERINFO_URL = "https://app.simplelogin.io/oauth2/userinfo"
由于我们现在不想担心设置 SSL,让我们告诉Requests-OAuthlib
使用纯 HTTP 是可以的:
# This allows us to use a plain HTTP callback os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"
像往常一样,这一步的代码在 step2.py 上。
第 3 步:登录重定向
当用户点击登录按钮时:
- 用户将被重定向到身份登录提供者授权页面,询问用户是否想与您的应用共享他们的信息。
- 在用户批准后,他们将被重定向回您的应用程序的页面以及 URL 中的
code
,您的应用程序将使用该代码来交换access token
,以便您稍后从服务提供商处获取用户信息。
因此,我们需要两条路由:将用户重定向到身份提供者的login
路由和接收code
并将其交换为access token
的callback
路由。 回调路由还负责显示用户信息。
@app.route("/login") def login(): simplelogin = requests_oauthlib.OAuth2Session( CLIENT_ID, redirect_uri="http://localhost:5000/callback" ) authorization_url, _ = simplelogin.authorization_url(AUTHORIZATION_BASE_URL) return flask.redirect(authorization_url) @app.route("/callback") def callback(): simplelogin = requests_oauthlib.OAuth2Session(CLIENT_ID) simplelogin.fetch_token( TOKEN_URL, client_secret=CLIENT_SECRET, authorization_response=flask.request.url ) user_info = simplelogin.get(USERINFO_URL).json() return f""" User information: <br> Name: {user_info["name"]} <br> Email: {user_info["email"]} <br> Avatar <img src="{user_info.get('avatar_url')}"> <br> <a href="/">Home</a> """
单击登录按钮应该会带您完成以下流程。 完整代码可以在 GitHub 上的 step3.py 中找到。

使用 Facebook 登录
Facebook、Google 和 Twitter 登录的设置有点复杂,需要设置 SSL 或选择正确的范围等额外步骤。 这些超出了本文的范围。
除了复杂的 UI 之外,集成 Facebook 最困难的部分可能是找到一种在本地通过 HTTPS 为您的 Web 应用程序提供服务的方法,因为新版本的 Facebook SDK 不允许本地纯 HTTP。 我推荐使用 Ngrok,这是一个免费工具,可以快速获取 HTTPS URL。
第 1 步:创建 Facebook 应用
请前往https://developers.facebook.com
并创建一个新应用:
然后在下一个屏幕上选择“集成 Facebook 登录”:
第 2 步:Facebook OAuth 凭据
点击左侧的“设置/基本”,复制App ID和App Secret 。 它们实际上是 OAuth client-id
和client-secret
。
更新client-id
和client-secret
。
export FB_CLIENT_ID={your facebook AppId} export FB_CLIENT_SECRET={your facebook AppSecret}
更新 AUTHORIZATION_BASE_URL 和 TOKEN_URL:
FB_AUTHORIZATION_BASE_URL = "https://www.facebook.com/dialog/oauth" FB_TOKEN_URL = "https://graph.facebook.com/oauth/access_token"
主页:
@app.route("/") def index(): return """ <a href="/fb-login">Login with Facebook</a> """
第 3 步:登录和回调端点
如果使用ngrok http 5000
命令在ngrok
后面提供应用程序,我们需要将当前 URL 设置为 ngrok URL。
# Your ngrok url, obtained after running "ngrok http 5000" URL = "https://abcdefgh.ngrok.io"
请确保将 URL 添加到您的 Facebook 登录/设置、有效 OAuth 重定向 URI 设置:
为了访问用户电子邮件,您需要将email
添加到scope
:
FB_SCOPE = ["email"] @app.route("/fb-login") def login(): facebook = requests_oauthlib.OAuth2Session( FB_CLIENT_ID, redirect_uri=URL + "/fb-callback", scope=FB_SCOPE ) authorization_url, _ = facebook.authorization_url(FB_AUTHORIZATION_BASE_URL) return flask.redirect(authorization_url)
由于 Facebook 需要进行合规性修复,因此callback
路由要复杂一些:
from requests_oauthlib.compliance_fixes import facebook_compliance_fix @app.route("/fb-callback") def callback(): facebook = requests_oauthlib.OAuth2Session( FB_CLIENT_ID, scope=FB_SCOPE, redirect_uri=URL + "/fb-callback" ) # we need to apply a fix for Facebook here facebook = facebook_compliance_fix(facebook) facebook.fetch_token( FB_TOKEN_URL, client_secret=FB_CLIENT_SECRET, authorization_response=flask.request.url, ) # Fetch a protected resource, ie user profile, via Graph API facebook_user_data = facebook.get( "https://graph.facebook.com/me?fields=id,name,email,picture{url}" ).json() email = facebook_user_data["email"] name = facebook_user_data["name"] picture_url = facebook_user_data.get("picture", {}).get("data", {}).get("url") return f""" User information: <br> Name: {name} <br> Email: {email} <br> Avatar <img src="{picture_url}"> <br> <a href="/">Home</a> """
现在,当单击使用 Facebook 登录时,您应该能够完成整个流程。
完整的代码在 facebook.py 上。
结论
恭喜——您已成功将 SSO 登录集成到 Flask 应用程序中!
为简单起见,本教程未提及范围和状态等其他 OAuth 概念,这些概念对于防御跨站点请求伪造攻击很重要。 您可能还需要将用户信息存储在本文未涉及的数据库中。
该应用程序还需要在生产环境中的 https 上提供服务,今天使用 Let's Encrypt 可以很容易地做到这一点。
快乐 OAuthing!