如果您有一个正在编写的现有 Flask Web 应用程序,那么拼凑一个完整的身份验证系统可能需要大量工作。 Okta 使插入完整的用户身份验证系统变得更加容易,而无需大量额外的工作。在本教程中,我们将以 Flask Git Dashboard 项目为例,并为其添加 Okta。
图书馆
本教程需要 Python 3,我们还将使用:
- Flask 网络框架版本 1.0.2
- Flask-OIDC,其中 OIDC 代表“OpenID Connect”。它支持在 Flask 应用程序中使用 OpenIDConnect。
- Okta Python 帮助程序库
- 一个免费的 Okta 开发者帐户
这篇博文中的所有完成代码都是在 GitHub 上的 blog-code-examples 存储库的 auth-existing-flask-app/finished 目录下的 MIT 许可下作为开源提供的。为您自己的应用程序使用和滥用源代码。
安装依赖
我们将从现有的 Flask 网络应用程序开始。如果您没有自己要修改的内容,请克隆此 Git 存储库:
git clone [email protected]:fullstackpython/blog-code-examples.git
接下来,为这个项目创建一个新的 Python virtualenv:
python3 -m venv flaskauth
使用activate
脚本激活虚拟环境:
. ./flaskauth/bin/activate
激活后命令提示符应该改变:
请记住,您必须在每个要使用此 virtualenv 中包含的依赖项的终端窗口中激活 virtualenv。
切换到您克隆的 block-code-examples
Girepository 中的项目目录。
cd blog-code-examples/auth-existing-flask-app/start/
现在我们可以为现有项目安装依赖项。
pip install -r requirements.txt
查找类似于以下内容的输出以确认依赖项已成功安装:
... Collecting amqp<3.0,>=2.1.4 (from kombu<5.0,>=4.0.2->Celery==4.1.0->-r requirements.txt (line 4)) Downloading https://files.pythonhosted.org/packages/7f/cf/12d4611fc67babd4ae250c9e8249c5650ae1933395488e9e7e3562b4ff24/amqp-2.3.2-py2.py3-none-any.whl (48kB) 100% |ââââââââââââââââââââââââââââââââ| 51kB 10.7MB/s Collecting six>=1.5 (from python-dateutil->alembic>=0.6->Flask-Migrate==2.2.0->-r requirements.txt (line 2)) Using cached https://files.pythonhosted.org/packages/67/4b/141a581104b1f6397bfa78ac9d43d8ad29a7ca43ea90a2d863fe3056e86a/six-1.11.0-py2.py3-none-any.whl Collecting vine>=1.1.3 (from amqp<3.0,>=2.1.4->kombu<5.0,>=4.0.2->Celery==4.1.0->-r requirements.txt (line 4)) Downloading https://files.pythonhosted.org/packages/10/50/5b1ebe42843c19f35edb15022ecae339fbec6db5b241a7a13c924dabf2a3/vine-1.1.4-py2.py3-none-any.whl Installing collected packages: click, itsdangerous, Werkzeug, MarkupSafe, Jinja2, Flask, SQLAlchemy, Flask-SQLAlchemy, Mako, python-editor, six, python-dateutil, alembic, Flask-Migrate, billiard, pytz, vine, amqp, kombu, Celery, redis, WTForms Running setup.py install for MarkupSafe ... done Running setup.py install for SQLAlchemy ... done Running setup.py install for Mako ... done Running setup.py install for python-editor ... done Running setup.py install for alembic ... done Running setup.py install for billiard ... done Running setup.py install for WTForms ... done Successfully installed Celery-4.1.0 Flask-1.0.2 Flask-Migrate-2.2.0 Flask-SQLAlchemy-2.3.2 Jinja2-2.10 Mako-1.0.7 MarkupSafe-1.0 SQLAlchemy-1.2.12 WTForms-2.1 Werkzeug-0.14.1 alembic-1.0.1 amqp-2.3.2 billiard-3.5.0.4 click-7.0 itsdangerous-1.1.0 kombu-4.2.1 python-dateutil-2.7.5 python-editor-1.0.3 pytz-2018.7 redis-2.10.6 six-1.11.0 vine-1.1.4
我们的项目需要一些额外的依赖项,flask-oidc
和 okta
:
pip install flask-oidc>=1.4.0 okta==0.0.4
现在依赖项已正确安装到我们的虚拟环境中。让我们测试应用程序,看看我们是否可以使其正常运行。
export FLASK_APP=flaskdash.py export FLASK_ENV=development flask run
我们应该看到应用程序以一些默认的开发时间值启动:
* Serving Flask app "flaskdash.py" (lazy loading)
* 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: 203-814-092
在您的 Web 浏览器中转到 localhost:5000,我们应该会看到一个正在进行的仪表板:
是时候设置 Okta 开发者帐户了,这样我们就可以为我们的应用程序获取适当的配置信息。
用于身份验证的 Okta
前往 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 应用程序中。
使用 Okta 更新 Flask 应用
我们需要将我们的 Flask 代码连接到我们的新 Okta 帐户。在 Flask 应用程序中包含帐户凭据等变量的推荐方法是通过配置处理。
使用以下突出显示的行更新 config.py
Flask 代码。
import os class Config(object): SECRET_KEY = os.getenv('SECRET_KEY') or 'development key' # Redis REDIS_SERVER = os.getenv('REDIS_SERVER') or 'localhost' REDIS_PORT = os.getenv('REDIS_PORT') or 6379 REDIS_DB = os.getenv('REDIS_DB') or 1 REDIS_URL = 'redis://{}:{}'.format(REDIS_SERVER, REDIS_PORT) # Celery task queue CELERY_BROKER_URL = os.getenv('CELERY_BROKER_URL') or REDIS_URL CELERY_RESULT_BACKEND = os.getenv('CELERY_RESULT_BACKEND') or REDIS_URL # database settings SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_URL') or \ 'sqlite:///' + os.path.join(os.path.abspath(os.path.dirname(__file__)), 'flaskdash.db') SQLALCHEMY_TRACK_MODIFICATIONS = False OIDC_CLIENT_SECRETS = "openidconnect_secrets.json" OIDC_COOKIE_SECURE = False OIDC_CALLBACK_ROUTE = "/oidc/callback" OIDC_SCOPES = ["openid", "email", "profile"] OIDC_ID_TOKEN_COOKIE_NAME = "oidc_token"
我们首先添加三个导入行,一个用于从环境变量中提取值,接下来的两个导入可以在我们的应用程序中使用 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
属性:
- FlaskApp for
Name
- http://localhost:5000 for
###
- 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 选项卡。
点击“创建令牌”按钮。
将令牌命名为 FlaskToken
并复制它。将令牌保存在安全的地方,因为我们将无法再次通过仪表板访问它。我们将在本教程的下一节中设置 OKTA_AUTH_TOKEN
环境变量时使用此标记。
好的,我们的 openidconnect_secret.json
文件中终于有了完成应用程序所需的所有 Okta 服务配置和令牌。
用这些突出显示的行更新 app/__init__.py
:
import redis from os import environ from flask import Flask from app.utils import make_celery from config import Config from flask_sqlalchemy import SQLAlchemy from flask_migrate import Migrate from flask_oidc import OpenIDConnect from okta import UsersClient app = Flask(__name__, static_url_path='/static') app.config.from_object(Config) db = SQLAlchemy(app) migrate = Migrate(app, db) # connect to Redis instance redis_db = redis.StrictRedis(host=app.config['REDIS_SERVER'], port=app.config['REDIS_PORT'], db=app.config['REDIS_DB']) celery = make_celery(app) # 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")) from app import routes
我们现在可以在我们的路由中访问 okta_client
。打开 app/routes.py
并更新以下行:
from flask import send_from_directory, render_template from flask import redirect, g from app import app, oidc, okta_client @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('/js/<path:path>') def send_js(path): return send_from_directory('js', path) @app.route('/css/<path:path>') def send_css(path): return send_from_directory('css', path) @app.route("/") def dashboard(): return render_template('dashboard.html') @app.route("/repositories") @oidc.require_login def repositories(): return render_template('repositories.html') @app.route("/login") @oidc.require_login def login(): return redirect(url_for(".repositories")) @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/repositories 转到 /dashboard
路由时,让我们测试重定向功能。我们被重定向到 Oktalogin 页面。
输入您的 Okta 开发人员用户名和密码以登录到您的应用程序。出于开发目的,这可以很好地用于测试,但显然在生产应用程序中,您将创建其他帐户供用户登录。
要取消对您的用户的身份验证,请转到 localhost:5000/logout。当您再次返回 localhost:5000/repositories 时,您现在必须重新进行身份验证。
现在怎么办?
我们将现有的 Flask 应用程序配置为使用 Okta 通过 Okta API 进行用户身份验证和身份管理。
接下来您可以尝试以下教程之一来向 Flask 应用程序添加其他功能:
- 如何向 Flask Web 应用程序添加托管监控
- 在 Docker 容器中开发和运行 Flask 应用程序
- 使用 Python 和 Flask 响应 SMS 文本消息
您还可以通过阅读完整堆栈 Python 目录页面来确定您的 Python 项目中接下来要编码的内容。
有问题吗?通过 Twitter@fullstackpython 或@mattmakai 与我联系。我也在 GitHub 上,用户名是 mattmakai。
这篇文章有问题吗?在 GitHub 上创建此页面的源代码并提交拉取请求。