Keycloak自定义实现第三方登录

Keycloak自定义实现第三方登录

第三方Oauth登录

  • 由于对接的第三方IDP不一定都是标准的openid connect实现,所以都需要根据第三方的Oauth文档进行定制;
    Keycloak对于新增Social IDP的实现,都是标准,以及灵活的;
    我们完全可以参照 Keycloak 本身已实现的Github LinkedIn等,快速实现我们的需求;

我们这里以酷家乐的Oauth2 接口进行说明

酷家乐 Oauth2接口分析

  • 请求code的参数及返回都是标准的,这里无需进行修改在这里插入图片描述
  • 换取token接口,由于返回参数进行data的wrap,以及字段名为驼峰的,所以需要对这部分进行修改定制

在这里插入图片描述

  • 获取用户信息

由于获取用户信息,还需要额外请求用户的OpenId,所以需要对该部分进行修改

在这里插入图片描述

在这里插入图片描述

定制Provider

提供工程类: KujialeIdentityProviderFactory

工厂类完全可以从GitHubIdentityProviderFactory 拷贝过来,修改成自己 PROVIDER_ID, NAME 以及create自己的Provider

public class GitHubIdentityProviderFactory extends AbstractIdentityProviderFactory<GitHubIdentityProvider> implements SocialIdentityProviderFactory<GitHubIdentityProvider> {

    public static final String PROVIDER_ID = "github";

    @Override
    public String getName() {
        return "GitHub";
    }

    @Override
    public GitHubIdentityProvider create(KeycloakSession session, IdentityProviderModel model) {
        return new GitHubIdentityProvider(session, new OAuth2IdentityProviderConfig(model));
    }

    @Override
    public OAuth2IdentityProviderConfig createConfig() {
        return new OAuth2IdentityProviderConfig();
    }

    @Override
    public String getId() {
        return PROVIDER_ID;
    }
}

解析accessToken 以及获取用户信息

在这里插入图片描述

解析用户信息

在这里插入图片描述

KujialeIdentityProvider 完整代码

public class KujialeIdentityProvider extends AbstractOAuth2IdentityProvider implements SocialIdentityProvider {

    public static final String AUTH_URL = "https://oauth.kujiale.com/oauth2/show";
    public static final String TOKEN_URL = "https://oauth.kujiale.com/oauth2/auth/token";
    public static final String OPENID_URL = "https://oauth.kujiale.com/oauth2/auth/user";
    public static final String PROFILE_URL = "https://oauth.kujiale.com/oauth2/openapi/user";
    public static final String DEFAULT_SCOPE = "get_user_info";

    public static final String OAUTH2_PARAMETER_ACCESS_TOKEN = "accessToken";

    public KujialeIdentityProvider(KeycloakSession session, OAuth2IdentityProviderConfig config) {
        super(session, config);
        config.setAuthorizationUrl(AUTH_URL);
        config.setTokenUrl(TOKEN_URL);
        config.setUserInfoUrl(PROFILE_URL);
    }

    @Override
    protected boolean supportsExternalExchange() {
        return true;
    }

    @Override
    protected String getProfileEndpointForValidation(EventBuilder event) {
        return PROFILE_URL;
    }

    @Override
    protected String getDefaultScopes() {
        return DEFAULT_SCOPE;
    }

    /**
     * 提取酷家乐用户信息,转换为keycloak用户实体
     *
     * @param event
     * @param profile
     * @return
     */
    @Override
    protected BrokeredIdentityContext extractIdentityFromProfile(EventBuilder event, JsonNode profile) {

        if (!profile.has("d") || profile.get("d").isEmpty()) {
            throw new NullPointerException("kujiale idp user info response is null " + profile.toString());
        }

        JsonNode userNode = profile.get("d");

        String openId = getJsonProperty(userNode, "openId");
        if (openId == null || openId.isEmpty()) {
            throw new NullPointerException("kujiale idp user info is null " + userNode.asText());
        }

        BrokeredIdentityContext user = new BrokeredIdentityContext(openId);
        String userName = userNode.get("userName").asText();

        user.setUsername(userName);
        user.setFirstName(userName);
        user.setIdpConfig(getConfig());
        user.setIdp(this);
        AbstractJsonUserAttributeMapper.storeUserProfileForMapper(user, userNode, getConfig().getAlias());
        return user;
    }

    /**
     * 构建获取酷家乐用户信息的请求 暂不支持
     *
     * @param subjectToken
     * @param userInfoUrl
     * @return
     */
    @Override
    protected SimpleHttp buildUserInfoRequest(String subjectToken, String userInfoUrl) {
        return SimpleHttp.doGet(userInfoUrl, session)
                .header("Authorization", "Bearer " + subjectToken);
    }

    /**
     * 获取酷家乐用户信息
     *
     * @param response
     * @return
     */
    @Override
    public BrokeredIdentityContext getFederatedIdentity(String response) {

        String accessToken;
        try {
            JsonNode resNode = asJsonNode(response);
            accessToken = extractTokenFromResponse(resNode.get("d").toString(), OAUTH2_PARAMETER_ACCESS_TOKEN);
        } catch (Exception ex) {
            throw new IdentityBrokerException("No access token available in OAuth server response: " + response);
        }

        if (accessToken == null) {
            throw new IdentityBrokerException("No access token available in OAuth server response: " + response);
        }

        BrokeredIdentityContext context = doGetFederatedIdentity(accessToken);
        context.getContextData().put(FEDERATED_ACCESS_TOKEN, accessToken);
        return context;
    }

    protected BrokeredIdentityContext doGetFederatedIdentity(String accessToken) {
        BrokeredIdentityContext context = null;

        try {
            JsonNode openidResponse = generateOpenIdRequest(accessToken).asJson();
            String openId = openidResponse.get("d").asText();
            if (openId == null) {
                throw new Exception("Can not get openId from kujiale IDP");
            }
            JsonNode profile = generateUserInfoRequest(accessToken, openId).asJson();
            context = extractIdentityFromProfile(null, profile);
        } catch (Exception ex) {
            logger.warn("Cannot GetFederatedIdentity from kujiale IDP, error " + ex.getMessage());
        }

        return context;
    }

    public SimpleHttp generateOpenIdRequest(String accessToken) {
        SimpleHttp openIdRequest = SimpleHttp.doGet(OPENID_URL, session)
                .param("access_token", accessToken);
        return openIdRequest;
    }

    public SimpleHttp generateUserInfoRequest(String accessToken, String openId) {
        SimpleHttp openIdRequest = SimpleHttp.doGet(PROFILE_URL, session)
                .param("open_id", openId)
                .param("access_token", accessToken);
        return openIdRequest;
    }

}

定制属性映射: IdentityProviderMapper

其实属性映射,本质上只是新增个说明,我们的Provider支持 Import Attribute,
所以代码都是模板代码

public class KujialeUserAttributeMapper extends AbstractJsonUserAttributeMapper {

    private static final String[] cp = new String[]{KujialeIdentityProviderFactory.PROVIDER_ID};

    @Override
    public String[] getCompatibleProviders() {
        return cp;
    }

    @Override
    public String getId() {
        return "kujiale-user-attribute-mapper";
    }

}

添加META-INF

添加
org.keycloak.broker.social.SocialIdentityProviderFactory

内容为:自己的ProviderFactory全类名

添加
org.keycloak.broker.provider.IdentityProviderMapper

内容为 自己Mapper的全类名

NoClassDefFoundError报错

在这里插入图片描述

相关问题链接
https://stackoverflow.com/questions/57778240/noclassdeffounderror-in-a-provider-jar-when-using-a-class-from-org-keycloak-auth

解决方法

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <configuration>
        <archive>
            <manifestEntries>
                <Dependencies>org.keycloak.keycloak-services</Dependencies>
            </manifestEntries>
        </archive>
    </configuration>
</plugin>

部署Provider及配置使用

部署

拷贝到 /standalone/deployments中,重启服务

重启服务后,到 Admin ==> server info 确认新增的Provider生效

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

配置

新增IDP在这里插入图片描述

设置属性mapper

在这里插入图片描述

可以设置默认使用的IDP

在这里插入图片描述

第一次登录,会自动创建用户已经,导入对应的属性信息

在这里插入图片描述

属性:

在这里插入图片描述

关联的IDP信息
在这里插入图片描述

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
Kubernetes(简称k8s)是一个开源的容器编排引擎,可以用于自动化部署、扩展和管理容器化应用程序。Keycloak是一个开源的身份和访问管理解决方案,它提供了诸如单点登录(SSO)、多因素身份验证、授权等功能。在k8s环境中部署和自定义Keycloak可以帮助我们更好地管理和保护我们的应用程序。 首先,在k8s集群中部署Keycloak的步骤包括创建一个Deployment来运行Keycloak容器、创建一个Service将Keycloak容器暴露出来、配置Ingress来管理对Keycloak的访问。可以通过编写YAML文件来定义Deployment、Service和Ingress资源,然后使用kubectl命令将其部署到k8s集群中。 其次,自定义Keycloak可以包括配置主题、引入自定义的身份提供者、定义客户端等。通过在Keycloak的管理后台进行相应的配置,我们可以定制化Keycloak的外观和行为,以满足具体的业务需求。 最后,在部署和自定义Keycloak时需要注意的问题包括配置数据库、管理用户、设置安全策略等。我们需要选择合适的数据库作为Keycloak的持久化存储,并根据实际情况管理用户的访问权限。此外,为了保证应用程序的安全性,我们还需要设置适当的安全策略和监控机制。 总之,通过在k8s集群中部署和自定义Keycloak,我们可以实现统一的身份和访问管理,并且能够满足不同应用程序的特定需求。这不仅可以提高系统的安全性和可靠性,还可以简化用户的身份管理和访问控制流程。
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值