本文概述
应用程序通常需要登录功能, 以便用户可以保存数据, 创建自己的配置文件, 或者仅仅是为了限制对敏感资源的访问。在现代应用中, 用户希望具有与登录相关的标准功能, 例如电子邮件验证, 密码重置或多因素身份验证。这些功能虽然必要, 但不容易正确使用, 通常也不是应用程序的主要业务。
在用户方面, 他们可能不想经过冗长的注册过程, 因为他们需要创建并记住另一个电子邮件和密码对。如果没有适当的密码管理器, 则用户往往会重复使用相同的密码, 这在安全性方面很糟糕。
发明了单点登录(SSO)(通常被用户称为使用社交媒体按钮登录)作为解决此问题的方法。对于用户来说, 不经历另一个痛苦的注册过程很容易。对于企业而言, 消除用户的摩擦始终是一个巨大的胜利-对于开发人员而言, 所有与登录相关的功能现在都委派给了身份提供商(Facebook, Google, Twitter等), 这意味着更少的代码!你的应用程序仅信任身份提供者执行其验证用户身份的工作。
SSO通常由OpenId Connect(OIDC)或SAML协议提供动力。 SAML通常用于企业应用程序中。 OIDC建立在OAuth2之上, 并被Facebook, Google等社交身份提供商使用。在本文中, 我们将重点介绍OIDC / OAuth2协议。
在此Flask登录教程中, 我们将编写分步指南, 以使用SimpleLogin和Facebook作为身份提供者的SSO登录按钮添加到Flask应用程序中。无需使用任何外部库即可完成此操作, 但是为了简化OAuth的复杂性, 我们将使用Requests-OAuthlib(用于集成OAuth提供程序的库)。如果你有兴趣从头开始实施SSO, 请查看”实施SSO登录” –原始方法。
在本文的结尾, 你应该拥有一个包含以下页面的Flask应用程序:
- 具有登录按钮的主页
- 用户信息页面, 成功登录后, 用户将能够看到诸如姓名, 电子邮件和头像之类的信息
该教程的所有代码都可以在flask-social-login-example存储库中找到。
此处也提供了演示。随意在Glitch上重新混合代码。
第1步:Bootstrap Flask应用
安装flask和Requests-OAuthlib。你也可以使用virtualenv或pipenv隔离环境。
pip安装烧瓶requests_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中, 请将这些行添加到文件顶部以获取客户端ID和客户端密码。
import os
CLIENT_ID = os.environ.get("CLIENT_ID")
CLIENT_SECRET = os.environ.get("CLIENT_SECRET")
还要在下一步将要使用的app.py顶部添加这些OAuth 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中的代码, 你的应用程序将使用该代码来交换访问令牌, 从而使你以后可以从服务提供商处获取用户信息。
因此, 我们需要两条路由:将用户重定向到身份提供者的登录路由, 以及接收代码并将其交换为访问令牌的回调路由。回调路由还负责显示用户信息。
@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或选择正确的范围。这些超出了本文的范围。
除了复杂的用户界面外, 集成Facebook最困难的部分可能是找到一种在本地HTTPS上为你的Web应用提供服务的方法, 因为新版本的Facebook SDK不允许使用本地纯HTTP。我建议使用Ngrok(一种免费工具)来获取快速的HTTPS URL。
步骤1:建立Facebook应用程式
请前往https://developers.facebook.com并创建一个新应用:
然后在下一个屏幕上选择”集成Facebook登录”:
步骤2:Facebook OAuth凭证
单击左侧的”设置/基本”, 然后复制App ID和App Secret。它们实际上是OAuth客户端ID和客户端秘密。
更新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 https://abcdefgh.ngrok.io/fb-callback添加到你的Facebook登录/设置, 有效的OAuth重定向URI设置:
为了访问用户电子邮件, 你需要在范围内添加电子邮件:
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需要进行合规性修复:
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, i.e. 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快乐!