当前位置: 首页 > 知识库问答 >
问题:

在Apache超集中使用KeyCloak(OpenID Connect)

薛华容
2023-03-14

我从使用openid/keycloak和Superset开始,并按照说明做了所有事情。然而,这是一个古老的岗位,并不是所有的工作。我还试图通过将自定义安全管理器安装为FAB插件来实现它,以便在我的应用程序中实现它,而不必编辑现有的超集代码

我运行的是KeyCloak 4.8.1.Final和Apache SuperSet V0.28.1

正如文章中所解释的,SuperSet不能很好地使用KeyCloak开箱即用,因为它使用OpenID 2.0,而不是OpenID Connect,而OpenID Connect正是KeyCloak提供的。

第一个区别是,在合并请求4565之后,您不能再执行以下操作:

from flask_appbuilder.security.sqla.manager import SecurityManager

相反,您现在必须使用:(根据updating.md文件)

from superset.security import SupersetSecurityManager

在上面提到的帖子中,海报展示了如何分别创建manager和view文件,但没有说放在哪里。我将manager和view类放在同一个名为manager.py的文件中,并将其放在FAB add-on结构中。

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))
{
    "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"
    }
}
    null

最后,我的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'
}]

在最初的文章中,没有提到openid_providers环境变量,所以我不确定在这里应该为URL添加什么。我把它放在KeyCloak上,因为它是登录到客户端控制台的URL。

当我运行超集时,我不会得到任何错误。我可以看到自定义安全管理器加载。当我导航到登录屏幕时,我必须选择我的提供商,我不会得到一个登录表单。我选择KeyCloak,因为显然没有其他内容,然后单击Login。当我单击登录时,我可以看到浏览器的地址栏中加载了一些东西,但什么也没有发生。根据我的理解,我应该被重定向到KeyCloak登录表单,然后在成功登录后返回到我的应用程序,但什么也没有发生。我是不是漏掉了什么?

因此,经过更多的挖掘,我的自定义视图类似乎加载了,但类中的方法并没有重写默认行为。不知道为什么会发生这种情况,也不知道如何修复它。

共有1个答案

杨经武
2023-03-14

最后我自己想通了。

我最后得到的解决方案没有使用FAB插件,但您也不必编辑现有的代码/文件。

我已经将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)
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
 类似资料:
  • 我有一个Java应用程序的问题。此应用程序使用Apache QPid broker(6.0.1)发送消息。 我在两台计算机上运行它:我用来工作的计算机,以及我们在将应用程序提供给客户端之前用于测试应用程序的计算机。 在我的电脑上,没有问题,应用程序发送或接收消息。 问题是我们换了另一台电脑。我安装qpid的方式与我的一样:我提取了。焦油gz,将$QPID\u WORK设置为工作目录,并设置我用于w

  • 我想使用kafka-clients作为我的keycloak模块,以便将每个登录事件发布到它中。 我应该造个耳朵吗?一场战争?一个罐子够吗?

  • 我正在使用POI 3.8使用Java创建Excel工作表 我已经成功地使用Prashant Jadhav在如何使用Apache poi在Java中创建超链接到其他工作表上的过滤器中解释的方法创建了超链接? 不过我想设置屏幕提示以及 我试过了 但是没有用。 这有可能吗? 谢谢!

  • 我是Apache Camel新手,我需要使用Apache Camel集成两个使用REST API的系统。我将在我的apache camel rest apiendpoint(来自源系统)上收到一个JSON消息。这个JSON将包含数组,我必须提取每个数组的内容并发布到另一个外部apiendpoint(目标)。因此,最初我尝试将传入消息按原样发送到camel rest api到目标外部apiendpo

  • 问题内容: 我有一个使用netbeans开发的现有项目,我想将Apache Ivy集成到该项目中。我更新了由netbeans生成的build.xml,以下载ivy(如果需要的话)并使用它来检索依赖项。 有谁知道我如何将下载的依赖项添加到项目的构建路径中,以便可以正常编译,并且不会在界面中显示丢失的库错误。 如果可能的话,我宁愿不使用netbeans插件就这样做。如果没有,您建议使用什么插件。 编辑

  • 对于Apache Spark日志记录,我尝试用Log4j2替换Log4j(到目前为止没有成功)。到目前为止,我已经设法将Log4j2用于我的应用程序日志,但我还想将它用于Spark内部日志(以避免同时存在两个不同的配置和框架)。