将 KeyCloak(OpenID Connect) 与 Apache SuperSet 结合使用
Using KeyCloak(OpenID Connect) with Apache SuperSet
我从 开始,并按照说明进行了所有操作。然而,它是一个旧的 post,并不是所有的东西都有效。我还尝试通过将其安装为 FAB 附加组件来实现自定义安全管理器,以便在我的应用程序中实现它而无需编辑现有的超集代码。
我正在 运行宁 KeyCloak 4.8.1.Final 和 Apache SuperSet v 0.28.1
如 post 中所述,SuperSet 不能很好地与开箱即用的 KeyCloak 配合使用,因为它使用 OpenID 2.0 而不是 KeyCloak 提供的 OpenID Connect。
第一个区别是合并请求 4565 后,您不能再做:
from flask_appbuilder.security.sqla.manager import SecurityManager
相反,您现在必须使用:(根据 UPDATING.md 文件)
from superset.security import SupersetSecurityManager
在上面提到的post中,poster展示了如何单独创建管理器和查看文件,但没有说放在哪里。我将管理器和视图 classes 放在同一个文件中,命名为 manager.py
,并将其放在 FAB 附加结构中。
from flask_appbuilder.security.manager import AUTH_OID
from superset.security import SupersetSecurityManager
from flask_oidc import OpenIDConnect
from flask_appbuilder.security.views import AuthOIDView
from flask_login import login_user
from urllib.parse import quote
from flask_appbuilder.views import ModelView, SimpleFormView, expose
import logging
class OIDCSecurityManager(SupersetSecurityManager):
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
CUSTOM_SECURITY_MANAGER = OIDCSecurityManager
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))
我在这个文件中设置了 CUSTOM_SECURITY_MANAGER
变量,而不是在 superset_config.py
中。这是因为它在那里时不起作用,它没有加载自定义安全管理器。我在阅读 .
后将变量移到了那里
我的 client_secret.json
文件如下所示:
{
"web": {
"realm_public_key": "<PUBLIC_KEY>",
"issuer": "https://<DOMAIN>/auth/realms/demo",
"auth_uri": "https://<DOMAIN>/auth/realms/demo/protocol/openid-connect/auth",
"client_id": "local",
"client_secret": "<CLIENT_SECRET>",
"redirect_urls": [
"http://localhost:8001/*"
],
"userinfo_uri": "https://<DOMAIN>/auth/realms/demo/protocol/openid-connect/userinfo",
"token_uri": "https://<DOMAIN>/auth/realms/demo/protocol/openid-connect/token",
"token_introspection_uri": "https://<DOMAIN>/auth/realms/demo/protocol/openid-connect/token/introspect"
}
}
realm_public_key
:我在 Realm Settings > Keys > Active 然后在 table 的 "RS256" 行中获得了这个密钥。
client_id
:local(我本地测试用的客户端)
client_secret
:我在 Clients > local (from the table) > Credentials > Secret 得到了这个
所有的url/uri值都是根据第一个提到的post调整的,我以前都是这样设置的。 <DOMAIN>
是 AWS CloudFront 默认域,因为我在 EC2 上 运行ning KeyCloak 并且不想为了简单地启动它而设置自定义 HTTPS 域的麻烦 运行宁.
最后,我的 superset_config.py
文件的一部分如下所示:
ADDON_MANAGERS = ['fab_addon_keycloak.manager.OIDCSecurityManager']
AUTH_TYPE = AUTH_OID
OIDC_CLIENT_SECRETS = '/usr/local/lib/python3.6/site-packages/fab_addon_keycloak/fab_addon_keycloak/client_secret.json'
OIDC_ID_TOKEN_COOKIE_SECURE = False
OIDC_REQUIRE_VERIFIED_EMAIL = False
AUTH_USER_REGISTRATION = True
AUTH_USER_REGISTRATION_ROLE = 'Gamma'
OPENID_PROVIDERS = [{
'name': 'KeyCloak',
'url': 'https://<DOMAIN>/auth/realms/demo/account'
}]
在原来的 post 中,没有提到 OPENID_PROVIDERS
环境变量,所以我不太确定为 URL 放什么。我放那个是因为那是 URL 你会点击登录到 KeyCloak 上的客户端控制台。
当我 运行 SuperSet 时,我没有收到任何错误。我可以看到自定义安全管理器已加载。当我导航到登录屏幕时,我必须选择我的提供商,但我没有获得登录表单。我选择 KeyCloak,因为显然没有其他东西,然后单击“登录”。当我单击“登录”时,我可以看到浏览器的地址栏中加载了一些内容,但没有任何反应。据我了解,我应该被重定向到 KeyCloak 登录表单,然后在成功登录后返回到我的应用程序,但没有任何反应。我是不是遗漏了什么地方?
编辑
所以在进一步挖掘之后,我的自定义视图 class 似乎已加载,但是 class 中的方法不会覆盖默认行为。不确定为什么会发生这种情况或如何解决它。
最后我自己弄明白了。
我最终得到的解决方案没有使用 FAB 附加组件,但您也不必编辑现有的 code/files。
我已将 manager.py 文件重命名为 security.py,现在它看起来像这样:
from flask import redirect, request
from flask_appbuilder.security.manager import AUTH_OID
from superset.security import SupersetSecurityManager
from flask_oidc import OpenIDConnect
from flask_appbuilder.security.views import AuthOIDView
from flask_login import login_user
from urllib.parse import quote
from flask_appbuilder.views import ModelView, SimpleFormView, expose
import logging
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))
class OIDCSecurityManager(SupersetSecurityManager):
authoidview = AuthOIDCView
def __init__(self,appbuilder):
super(OIDCSecurityManager, self).__init__(appbuilder)
if self.auth_type == AUTH_OID:
self.oid = OpenIDConnect(self.appbuilder.get_app)
我将 security.py 文件放在我的 superset_config_py 文件旁边。
JSON 配置文件保持不变。
然后我更改了 superset_config.py 文件以包含以下行:
from security import OIDCSecurityManager
AUTH_TYPE = AUTH_OID
OIDC_CLIENT_SECRETS = <path_to_configuration_file>
OIDC_ID_TOKEN_COOKIE_SECURE = False
OIDC_REQUIRE_VERIFIED_EMAIL = False
AUTH_USER_REGISTRATION = True
AUTH_USER_REGISTRATION_ROLE = 'Gamma'
CUSTOM_SECURITY_MANAGER = OIDCSecurityManager
就是这样。
现在,当我导航到我的站点时,它会自动转到 KeyCloak 登录屏幕,成功登录后,我将被重定向回我的应用程序。
我从
我正在 运行宁 KeyCloak 4.8.1.Final 和 Apache SuperSet v 0.28.1
如 post 中所述,SuperSet 不能很好地与开箱即用的 KeyCloak 配合使用,因为它使用 OpenID 2.0 而不是 KeyCloak 提供的 OpenID Connect。
第一个区别是合并请求 4565 后,您不能再做:
from flask_appbuilder.security.sqla.manager import SecurityManager
相反,您现在必须使用:(根据 UPDATING.md 文件)
from superset.security import SupersetSecurityManager
在上面提到的post中,poster展示了如何单独创建管理器和查看文件,但没有说放在哪里。我将管理器和视图 classes 放在同一个文件中,命名为 manager.py
,并将其放在 FAB 附加结构中。
from flask_appbuilder.security.manager import AUTH_OID
from superset.security import SupersetSecurityManager
from flask_oidc import OpenIDConnect
from flask_appbuilder.security.views import AuthOIDView
from flask_login import login_user
from urllib.parse import quote
from flask_appbuilder.views import ModelView, SimpleFormView, expose
import logging
class OIDCSecurityManager(SupersetSecurityManager):
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
CUSTOM_SECURITY_MANAGER = OIDCSecurityManager
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))
我在这个文件中设置了 CUSTOM_SECURITY_MANAGER
变量,而不是在 superset_config.py
中。这是因为它在那里时不起作用,它没有加载自定义安全管理器。我在阅读
我的 client_secret.json
文件如下所示:
{
"web": {
"realm_public_key": "<PUBLIC_KEY>",
"issuer": "https://<DOMAIN>/auth/realms/demo",
"auth_uri": "https://<DOMAIN>/auth/realms/demo/protocol/openid-connect/auth",
"client_id": "local",
"client_secret": "<CLIENT_SECRET>",
"redirect_urls": [
"http://localhost:8001/*"
],
"userinfo_uri": "https://<DOMAIN>/auth/realms/demo/protocol/openid-connect/userinfo",
"token_uri": "https://<DOMAIN>/auth/realms/demo/protocol/openid-connect/token",
"token_introspection_uri": "https://<DOMAIN>/auth/realms/demo/protocol/openid-connect/token/introspect"
}
}
realm_public_key
:我在 Realm Settings > Keys > Active 然后在 table 的 "RS256" 行中获得了这个密钥。client_id
:local(我本地测试用的客户端)client_secret
:我在 Clients > local (from the table) > Credentials > Secret 得到了这个
所有的url/uri值都是根据第一个提到的post调整的,我以前都是这样设置的。 <DOMAIN>
是 AWS CloudFront 默认域,因为我在 EC2 上 运行ning KeyCloak 并且不想为了简单地启动它而设置自定义 HTTPS 域的麻烦 运行宁.
最后,我的 superset_config.py
文件的一部分如下所示:
ADDON_MANAGERS = ['fab_addon_keycloak.manager.OIDCSecurityManager']
AUTH_TYPE = AUTH_OID
OIDC_CLIENT_SECRETS = '/usr/local/lib/python3.6/site-packages/fab_addon_keycloak/fab_addon_keycloak/client_secret.json'
OIDC_ID_TOKEN_COOKIE_SECURE = False
OIDC_REQUIRE_VERIFIED_EMAIL = False
AUTH_USER_REGISTRATION = True
AUTH_USER_REGISTRATION_ROLE = 'Gamma'
OPENID_PROVIDERS = [{
'name': 'KeyCloak',
'url': 'https://<DOMAIN>/auth/realms/demo/account'
}]
在原来的 post 中,没有提到 OPENID_PROVIDERS
环境变量,所以我不太确定为 URL 放什么。我放那个是因为那是 URL 你会点击登录到 KeyCloak 上的客户端控制台。
当我 运行 SuperSet 时,我没有收到任何错误。我可以看到自定义安全管理器已加载。当我导航到登录屏幕时,我必须选择我的提供商,但我没有获得登录表单。我选择 KeyCloak,因为显然没有其他东西,然后单击“登录”。当我单击“登录”时,我可以看到浏览器的地址栏中加载了一些内容,但没有任何反应。据我了解,我应该被重定向到 KeyCloak 登录表单,然后在成功登录后返回到我的应用程序,但没有任何反应。我是不是遗漏了什么地方?
编辑
所以在进一步挖掘之后,我的自定义视图 class 似乎已加载,但是 class 中的方法不会覆盖默认行为。不确定为什么会发生这种情况或如何解决它。
最后我自己弄明白了。
我最终得到的解决方案没有使用 FAB 附加组件,但您也不必编辑现有的 code/files。
我已将 manager.py 文件重命名为 security.py,现在它看起来像这样:
from flask import redirect, request
from flask_appbuilder.security.manager import AUTH_OID
from superset.security import SupersetSecurityManager
from flask_oidc import OpenIDConnect
from flask_appbuilder.security.views import AuthOIDView
from flask_login import login_user
from urllib.parse import quote
from flask_appbuilder.views import ModelView, SimpleFormView, expose
import logging
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))
class OIDCSecurityManager(SupersetSecurityManager):
authoidview = AuthOIDCView
def __init__(self,appbuilder):
super(OIDCSecurityManager, self).__init__(appbuilder)
if self.auth_type == AUTH_OID:
self.oid = OpenIDConnect(self.appbuilder.get_app)
我将 security.py 文件放在我的 superset_config_py 文件旁边。
JSON 配置文件保持不变。
然后我更改了 superset_config.py 文件以包含以下行:
from security import OIDCSecurityManager
AUTH_TYPE = AUTH_OID
OIDC_CLIENT_SECRETS = <path_to_configuration_file>
OIDC_ID_TOKEN_COOKIE_SECURE = False
OIDC_REQUIRE_VERIFIED_EMAIL = False
AUTH_USER_REGISTRATION = True
AUTH_USER_REGISTRATION_ROLE = 'Gamma'
CUSTOM_SECURITY_MANAGER = OIDCSecurityManager
就是这样。
现在,当我导航到我的站点时,它会自动转到 KeyCloak 登录屏幕,成功登录后,我将被重定向回我的应用程序。