用户身份验证是 Web 应用程序中的一项基本功能,因此人们可以创建和访问自己的帐户。不幸的是,身份验证并不总是很容易设置,并且有很多方法可以错误地实现登录和注销功能。
本教程将介绍如何使用名为 Okta 的安全身份验证服务(最多可免费使用 1,000 个活跃用户帐户)轻松处理 Flask 应用程序中的用户数据。
我们的工具
强烈建议使用 Python 3 构建应用程序,本教程是使用 Python 3.7 构建的,尽管早期版本的 Python 3 也应该可以正常工作。除了 Python 3.x,我们还将使用:
- Flask 网络框架版本 1.0.2
- Flask-OIDC,其中 OIDC 代表“OpenID Connect”。它支持在 Flask 应用程序中使用 OpenIDConnect。
- Okta Python 帮助程序库
- 一个免费的 Okta 开发者帐户
这篇博文中的所有代码都是在 GitHub 上的 blog-code-examples 存储库的 flask-auth-okta 目录下的 MIT 许可下作为开源提供的。使用和滥用您想要构建的应用程序的源代码。
安装依赖
为此项目创建一个新的 Python virtualenv:
python3 -m venv flaskauth
使用activate
脚本激活虚拟环境:
. ./flaskauth/bin/activate
激活后命令提示符应该改变:
请记住,您必须在每个要使用此 virtualenv 中包含的依赖项的终端窗口中激活 virtualenv。
现在我们可以安装 Flask 和 Okta 依赖项了。
pip install flask>=1.0.2 flask-oidc>=1.4.0 okta==0.0.4
查找类似于以下内容的输出以确认依赖项已成功安装:
... Collecting idna<2.8,>=2.5 (from requests>=2.5.3->okta) Downloading https://files.pythonhosted.org/packages/4b/2a/0276479a4b3caeb8a8c1af2f8e4355746a97fab05a372e4a2c6a6b876165/idna-2.7-py2.py3-none-any.whl (58kB) 100% |ââââââââââââââââââââââââââââââââ| 61kB 16.6MB/s Collecting urllib3<1.24,>=1.21.1 (from requests>=2.5.3->okta) Downloading https://files.pythonhosted.org/packages/bd/c9/6fdd990019071a4a32a5e7cb78a1d92c53851ef4f56f62a3486e6a7d8ffb/urllib3-1.23-py2.py3-none-any.whl (133kB) 100% |ââââââââââââââââââââââââââââââââ| 143kB 14.0MB/s Installing collected packages: MarkupSafe, Jinja2, click, itsdangerous, Werkzeug, flask, pyasn1, pyasn1-modules, rsa, httplib2, six, oauth2client, flask-oidc, chardet, certifi, idna, urllib3, requests, python-dateutil, okta Running setup.py install for MarkupSafe ... done Running setup.py install for itsdangerous ... done Running setup.py install for httplib2 ... done Running setup.py install for flask-oidc ... done Running setup.py install for okta ... done Successfully installed Jinja2-2.10 MarkupSafe-1.0 Werkzeug-0.14.1 certifi-2018.8.24 chardet-3.0.4 click-6.7 flask-1.0.2 flask-oidc-1.4.0 httplib2-0.11.3 idna-2.7 itsdangerous-0.24 oauth2client-4.1.3 okta-0.0.4 pyasn1-0.4.4 pyasn1-modules-0.2.2 python-dateutil-2.7.3 requests-2.19.1 rsa-4.0 six-1.11.0 urllib3-1.23
我们安装了所需的 Flask 和 Okta 依赖项,让我们开始构建 Flask 应用程序吧。
创建一个基本的 Flask 应用
向我们的 Flask 应用程序添加身份验证之前的第一步是编写一些脚手架函数。身份验证将挂接到这些函数,例如 signin
和 signout
,以确保身份验证过程正常工作。
为您的项目创建一个名为 thundercats
的目录。为什么 thundercats
?为什么 不 Thundercats?
在 thundercats
中直接创建一个名为 app.py
的文件,初始内容如下:
# imports for Flask from flask import Flask, Response app = Flask(__name__) @app.route("/lair") def lair(): return Response("Thundercats (supposed to be hidden) lair.") @app.route("/") def landing_page(): return Response("Thundercats, Thundercats, hoooooooooooo!")
我们可以使用以下命令运行我们的 Flask 应用程序:
set FLASK_APP=app.py flask run
在您的网络浏览器中转到 localhost:5000,您应该看到:
现在去我们位于 localhost:5000/lair/ 的“隐藏巢穴”。最终这个页面应该需要身份验证才能访问,但现在它似乎没有任何登录挑战:
太棒了,我们的基本应用已启动并运行,让我们开始使用身份验证功能吧。
身份验证即服务
前往 Okta 开发者注册页面。
注册一个新帐户或登录您现有的帐户。
关于 Okta 开发人员注册流程的有趣之处在于,现在您应该检查您的电子邮件以完成您的帐户创建。查找像这样的电子邮件:
单击“登录”按钮并使用电子邮件中的临时密码登录开发者帐户。设置新密码和挑战问题。然后选择一张图片以匹配您的帐户登录过程。
单击“创建帐户”按钮,您将被带到 Okta 开发人员仪表板。
找到下图所示的“Org URL”。
我们将在我们的秘密凭证文件中使用该 URL,以便我们的 Flask 网络应用程序可以正确连接到 Okta 服务。
在名为openidconnect_secrets.json
的项目目录中创建一个新文件,内容如下:
{ "web": { "client_id": "{{ OKTA_CLIENT_ID }}", "client_secret": "{{ OKTA_CLIENT_SECRET }}", "auth_uri": "{{ OKTA_ORG_URL }}/oauth2/default/v1/authorize", "token_uri": "{{ OKTA_ORG_URL }}/oauth2/default/v1/token", "issuer": "{{ OKTA_ORG_URL }}/oauth2/default", "userinfo_uri": "{{ OKTA_ORG_URL }}/oauth2/default/userinfo", "redirect_uris": [ "http://localhost:5000/oidc/callback" ] } }
将四个 {{ OKTA_ORG_URL }}
占位符替换为在仪表板中找到的 Org URL 值。在继续学习本教程时,我们将用实际值填充其余的占位符。根据我的开发人员仪表板组织 URL,我的openidconnect_secret.json
文件当前具有以下值。请记住,您的 URL 值会有所不同!
{ "web": { "client_id": "{{ OKTA_CLIENT_ID }}", "client_secret": "{{ OKTA_CLIENT_SECRET }}", "auth_uri": "https://dev-860408.oktapreview.com/oauth2/default/v1/authorize", "token_uri": "https://dev-860408.oktapreview.com/oauth2/default/v1/token", "issuer": "https://dev-860408.oktapreview.com/oauth2/default", "userinfo_uri": "https://dev-860408.oktapreview.com/oauth2/default/userinfo", "redirect_uris": [ "http://localhost:5000/oidc/callback" ] } }
好的,太棒了,我们已经设置了 Okta 帐户,因此我们可以将身份验证代码添加到我们的 Flask 应用程序中。
将 Flask 连接到 Okta
我们需要将我们的 Flask 代码连接到我们的新 Okta 帐户。在 Flask 应用程序中包含帐户凭据等变量的推荐方法是通过配置处理,因此我们将在我们的帐户中使用它。
使用以下突出显示的行更新 Flask 代码。
# imports for both Flask and Okta connection from os import environ from flask import Flask, Response from flask_oidc import OpenIDConnect from okta import UsersClient app = Flask(__name__) # secret credentials for Okta connection app.config["OIDC_CLIENT_SECRETS"] = "openidconnect_secrets.json" app.config["OIDC_COOKIE_SECURE"] = False app.config["OIDC_CALLBACK_ROUTE"] = "/oidc/callback" app.config["OIDC_SCOPES"] = ["openid", "email", "profile"] app.config["SECRET_KEY"] = environ.get("SECRET_KEY") app.config["OIDC_ID_TOKEN_COOKIE_NAME"] = "oidc_token" # instantiate OpenID client to handle user session oidc = OpenIDConnect(app) # Okta client will determine if a user has an appropriate account okta_client = UsersClient(environ.get("OKTA_ORG_URL"), environ.get("OKTA_AUTH_TOKEN")) @app.route("/lair") def lair(): return Response("Thundercats (supposed to be hidden) lair.") @app.route("/") def landing_page(): return Response("Thundercats, Thundercats, hoooooooooooo!")
我们首先添加三个导入行,一个用于从环境变量中提取值,接下来的两个导入可以在我们的应用程序中使用 OpenIDConnect 和 Okta。
其余的新代码设置了可用于实例化 OpenID Connect 和 Okta 客户端的 Flask 应用程序配置值。
OIDC_CLIENT_SECRETS
:OpenID Connect secrets文件所在位置OIDC_COOKIE_SECURE
:允许测试用户登录的开发模式和没有 SSL 的注册。您的应用程序必须在生产应用程序中将此设置为True
。OIDC_CALLBACK_ROUTE
:Web 应用程序中用于处理用户登录的 URLOIDC_SCOPES
:用户登录时要请求的用户数据。我们的应用程序请求基本的电子邮件、姓名和个人资料信息###
:这是一个 Flask 设置,用于确保会话安全。密钥绝不能公开,否则您的 Web 应用程序用户会话将受到损害。
我们从哪里获得这些应用程序配置值?我们需要从我们的 Okta 帐户获取它们,因此返回仪表板以创建一个新的 OpenID Connect 应用程序。
OpenID Connect 应用程序使用客户端 ID 和客户端密码代替传统的用户名和密码。客户端 ID 和客户端密码将告诉您的授权服务器识别您的应用程序。按“添加应用程序”按钮。
在新的应用程序屏幕上选择“Web”,然后按“下一步”。
在下一页上有许多配置选项,但在我们获得凭据之前我们只需要填写几个值。将以下值设置为 Name
、Base URIs
和 Login redirect URIs
属性:
- ThunderFlaskCats 用于
Name
- http://localhost:5000 用于
###
- http://localhost:5000/oidc/callback for
Base URIs
这是您现在需要填写的三个值,因此请保存应用程序以创建它。
在下一页向下滚动以找到您的客户端和密钥。
将客户端 ID 和客户端密码复制并粘贴到以下突出显示的行中,以替换 {{ OKTA_CLIENT_ID }}
和{{ OKTA_CLIENT_SECRET }}
占位符。
{ "web": { "client_id": "{{ OKTA_CLIENT_ID }}", "client_secret": "{{ OKTA_CLIENT_SECRET }}", "auth_uri": "https://dev-860408.oktapreview.com/oauth2/default/v1/authorize", "token_uri": "https://dev-860408.oktapreview.com/oauth2/default/v1/token", "issuer": "https://dev-860408.oktapreview.com/oauth2/default", "userinfo_uri": "https://dev-860408.oktapreview.com/oauth2/default/userinfo", "redirect_uris": [ "http://localhost:5000/oidc/callback" ] } }
保存文件并确保将其置于版本控制之外,因为这些秘密值需要保密。
在使用身份验证代码升级我们的 Flask 应用程序之前,我们在 Okta 开发人员仪表板中还有一个步骤:创建 API 身份验证令牌。转到 API 选项卡。
点击“创建令牌”按钮。
将令牌命名为 ThunderFlaskCatsToken
并复制它。将令牌保存在安全的地方,因为我们将无法再次通过仪表板访问它。我们将在本教程的下一节中设置 OKTA_AUTH_TOKEN
环境变量时使用此标记。
好的,我们的 openidconnect_secret.json
文件中终于有了完成应用程序所需的所有 Okta 服务配置和令牌。
保护巢穴
我们的配置已设置,因此使用以下突出显示的行更新 app.py
文件:
# imports for both Flask and Okta connection from os import environ from flask import Flask, Response, redirect, g, url_for from flask_oidc import OpenIDConnect from okta import UsersClient app = Flask(__name__) # secret credentials for Okta connection app.config["OIDC_CLIENT_SECRETS"] = "openidconnect_secrets.json" app.config["OIDC_COOKIE_SECURE"] = False app.config["OIDC_CALLBACK_ROUTE"] = "/oidc/callback" app.config["OIDC_SCOPES"] = ["openid", "email", "profile"] app.config["SECRET_KEY"] = environ.get("SECRET_KEY") app.config["OIDC_ID_TOKEN_COOKIE_NAME"] = "oidc_token" # instantiate OpenID client to handle user session oidc = OpenIDConnect(app) # Okta client will determine if a user has an appropriate account okta_client = UsersClient(environ.get("OKTA_ORG_URL"), environ.get("OKTA_AUTH_TOKEN")) @app.before_request def before_request(): if oidc.user_loggedin: g.user = okta_client.get_user(oidc.user_getfield("sub")) else: g.user = None @app.route("/lair") @oidc.require_login def lair(): return Response("Thundercats (supposed to be hidden) lair.") @app.route("/") def landing_page(): return Response("Thundercats, Thundercats, hoooooooooooo!") @app.route("/login") @oidc.require_login def login(): return redirect(url_for(".lair")) @app.route("/logout") def logout(): oidc.logout() return redirect(url_for(".landing_page"))
上面新突出显示的行检查用户是否在每个请求之前登录。如果路由由于 @oidc.require_login
装饰器而需要登录用户,则用户将被重定向到登录页面。我们还在 /login
和 /logout
下添加了路由,以便可以登录和退出应用程序。
设置三个环境变量,以便我们的应用程序在运行时可以使用它们。确保占位符 ORG_URL
和 AUTH_TOKEN
设置为来自 Okta 开发人员仪表板的实际组织 URL 值和身份验证令牌。
在命令行上运行以下命令,确保用您自己的标记和 URL 替换任何占位符值:
# this tells Flask we want to run the built-in server in dev mode export FLASK_ENV=development # make sure to use a very long random string here that cannot be guessed export SECRET_KEY='a very long string with lots of numbers and letters' # this is the same Org URL found on your developer dashboard # for example, https://dev-860408.oktapreview.com export OKTA_ORG_URL='ORG_URL' # this is the API authentication token we created export OKTA_AUTH_TOKEN='AUTH_TOKEN'
现在重新运行 Flask 应用程序:
set FLASK_APP=app.py flask run
如果开发服务器启动时输出如下,你应该处于良好状态:
(flaskauth)$ flask run * Environment: development * Debug mode: on * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) * Restarting with stat * Debugger is active! * Debugger PIN: 415-920-546
在您尚未登录 Okta 帐户的浏览器中前往 localhost:5000(网络浏览器的隐身窗口效果很好)。
当我们尝试通过转到 localhost:5000/lair 转到 /lair
路由时,让我们测试重定向功能。我们被重定向到 Oktalogin 页面。
输入您的 Okta 开发人员用户名和密码以登录到您的应用程序。出于开发目的,这可以很好地用于测试,但显然在生产应用程序中,您将创建其他帐户供用户登录。
让我们在我们的应用程序中再做一点调整,以解决在成功完成本教程的身份验证代码时明显缺乏兴奋感的问题。更新突出显示的两行以匹配下面代码块中的内容:
# imports for both Flask and Okta connection from os import environ from flask import Flask, Response, redirect, g, url_for from flask_oidc import OpenIDConnect from okta import UsersClient app = Flask(__name__) # secret credentials for Okta connection app.config["OIDC_CLIENT_SECRETS"] = "openidconnect_secrets.json" app.config["OIDC_COOKIE_SECURE"] = False app.config["OIDC_CALLBACK_ROUTE"] = "/oidc/callback" app.config["OIDC_SCOPES"] = ["openid", "email", "profile"] app.config["SECRET_KEY"] = environ.get("SECRET_KEY") app.config["OIDC_ID_TOKEN_COOKIE_NAME"] = "oidc_token" # instantiate OpenID client to handle user session oidc = OpenIDConnect(app) # Okta client will determine if a user has an appropriate account okta_client = UsersClient(environ.get("OKTA_ORG_URL"), environ.get("OKTA_AUTH_TOKEN")) @app.before_request def before_request(): if oidc.user_loggedin: g.user = okta_client.get_user(oidc.user_getfield("sub")) else: g.user = None @app.route("/lair") @oidc.require_login def lair(): thundercats_lair = '<html><head><title>Thundercats, hoooo!</title></head><body><h1>Thundercats now hidden lair.</h1><iframe src="https://giphy.com/embed/ahXtBEbHiraxO" width="480" height="273" frameBorder="0" allowFullScreen></iframe><p><a href="https://giphy.com/gifs/retro-cartoons-thundercats-ahXtBEbHiraxO">via GIPHY</a></p></body></html>' return Response(thundercats_lair) @app.route("/") def landing_page(): return Response("Thundercats, Thundercats, hoooooooooooo!") @app.route("/login") @oidc.require_login def login(): """Force user to login and then redirect them to the lair. """ return redirect(url_for(".lair")) @app.route("/logout") def logout(): oidc.logout() return redirect(url_for(".landing_page"))
刷新巢穴页面。
好吧,这只是好一点点!转到 localhost:5000/logout 以对您的用户进行身份验证。当您再次访问 localhost:5000/lair 时,您现在必须重新进行身份验证。
现在怎么办?
我们刚刚构建了一个示例 Flask 应用程序,通过 Okta API 进行用户身份验证。
接下来尝试按照以下教程将其他功能添加到您的 Flask 应用程序中:
- 使用 Python 和 Flask 响应 SMS 文本消息
- 如何将托管监控添加到 Flask Web 应用程序
- 在 Docker 容器中开发和运行 Flask 应用程序
您还可以通过阅读完整堆栈 Python 目录页面来确定您的 Python 项目中接下来要编码的内容。
有问题吗?通过 Twitter@fullstackpython 或@mattmakai 与我联系。我也在 GitHub 上,用户名是 mattmakai。
这篇文章有问题吗?在 GitHub 上创建此页面的源代码并提交拉取请求。