2020 年 3 月 2 日更新
@sjmeyer 编写了更新指南,适用于 Superset 0.28.1 及更高版本。我自己没有尝试过,但感谢@nawazxy 确认此解决方案有效。
我设法解决了我自己的问题。主要问题是由于我对超集正在使用的 flask-openid 插件所做的错误假设造成的。这个插件实际上支持OpenID 2.x,但不支持 OpenID-Connect(这是 Keycloak 实现的版本)。
作为一种解决方法,我决定切换到flask-oidc插件。切换到新的身份验证提供程序实际上需要一些挖掘工作。要集成插件,我必须按照以下步骤操作:
为 keycloak 配置 flask-oidc
不幸的是,flask-oidc 不支持 Keycloak 生成的配置格式。相反,您的配置应如下所示:
{
"web": {
"realm_public_key": "<YOUR_REALM_PUBLIC_KEY>",
"issuer": "http://<YOUR_DOMAIN>/auth/realms/<YOUR_REALM_ID>",
"auth_uri": "http://<YOUR_DOMAIN>/auth/realms/<YOUR_REALM_ID>/protocol/openid-connect/auth",
"client_id": "<YOUR_CLIENT_ID>",
"client_secret": "<YOUR_SECRET_KEY>",
"redirect_urls": [
"http://<YOUR_DOMAIN>/*"
],
"userinfo_uri": "http://<YOUR_DOMAIN>/auth/realms/<YOUR_REALM_ID>/protocol/openid-connect/userinfo",
"token_uri": "http://<YOUR_DOMAIN>/auth/realms/<YOUR_REALM_ID>/protocol/openid-connect/token",
"token_introspection_uri": "http://<YOUR_DOMAIN>/auth/realms/<YOUR_REALM_ID>/protocol/openid-connect/token/introspect"
}
}
Flask-oidc 期望配置在文件中。我已经将我的存储在client_secret.json
. 您可以在superset_config.py
.
扩展安全管理器
首先,您需要确保烧瓶停止使用 flask-openid 广告开始使用 flask-oidc。为此,您需要创建自己的安全管理器,将 flask-oidc 配置为其身份验证提供程序。我已经像这样实现了我的安全管理器:
from flask_appbuilder.security.manager import AUTH_OID
from flask_appbuilder.security.sqla.manager import SecurityManager
from flask_oidc import OpenIDConnect
class OIDCSecurityManager(SecurityManager):
def __init__(self,appbuilder):
super(OIDCSecurityManager, self).__init__(appbuilder)
if self.auth_type == AUTH_OID:
self.oid = OpenIDConnect(self.appbuilder.get_app)
self.authoidview = AuthOIDCView
要在 Superset 中启用 OpenID,您之前必须将身份验证类型设置为 AUTH_OID。我的安全管理器仍然执行超类的所有行为,但是用 OpenIDConnect 对象覆盖了 oid 属性。此外,它将默认的 OpenID 身份验证视图替换为自定义视图。我已经像这样实现了我的:
from flask_appbuilder.security.views import AuthOIDView
from flask_login import login_user
from urllib import quote
class AuthOIDCView(AuthOIDView):
@expose('/login/', methods=['GET', 'POST'])
def login(self, flag=True):
sm = self.appbuilder.sm
oidc = sm.oid
@self.appbuilder.sm.oid.require_login
def handle_login():
user = sm.auth_user_oid(oidc.user_getfield('email'))
if user is None:
info = oidc.user_getinfo(['preferred_username', 'given_name', 'family_name', 'email'])
user = sm.add_user(info.get('preferred_username'), info.get('given_name'), info.get('family_name'), info.get('email'), sm.find_role('Gamma'))
login_user(user, remember=False)
return redirect(self.appbuilder.get_url_for_index)
return handle_login()
@expose('/logout/', methods=['GET', 'POST'])
def logout(self):
oidc = self.appbuilder.sm.oid
oidc.logout()
super(AuthOIDCView, self).logout()
redirect_url = request.url_root.strip('/') + self.appbuilder.get_url_for_login
return redirect(oidc.client_secrets.get('issuer') + '/protocol/openid-connect/logout?redirect_uri=' + quote(redirect_url))
我的观点覆盖了 /login 和 /logout 端点的行为。登录时,将运行 handle_login 方法。它要求用户通过 OIDC 提供商的身份验证。在我们的例子中,这意味着用户将首先被重定向到 Keycloak 进行登录。
在身份验证时,用户被重定向回 Superset。接下来,我们查看我们是否识别出用户。如果没有,我们会根据他们的 OIDC 用户信息创建用户。最后,我们将用户登录到 Superset 并将他们重定向到登录页面。
注销时,我们需要使这些 cookie 无效:
- 超集会议
- OIDC 令牌
- Keycloak设置的cookies
默认情况下,Superset 只会处理第一个。扩展注销方法处理所有三点。
配置超集
最后,我们需要在我们的superset_config.py
. 这就是我配置我的方式:
'''
AUTHENTICATION
'''
AUTH_TYPE = AUTH_OID
OIDC_CLIENT_SECRETS = 'client_secret.json'
OIDC_ID_TOKEN_COOKIE_SECURE = False
OIDC_REQUIRE_VERIFIED_EMAIL = False
CUSTOM_SECURITY_MANAGER = OIDCSecurityManager
AUTH_USER_REGISTRATION = True
AUTH_USER_REGISTRATION_ROLE = 'Gamma'