栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Keycloak自定义REST扩展-通过用户属性进行用户搜索

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Keycloak自定义REST扩展-通过用户属性进行用户搜索

Keycloak自定义REST扩展-通过用户属性进行用户搜索 需求背景

项目中用户和组织架构管理都是依托以Keycloak,但是Keycloak内置的用户搜索功能不满足需求,需要根据用户的属性值进行搜索,比如手机号;

Keycloak内置API能力代码走读

Keycloak 官方文档


看官方文档,貌似提供了search搜索查询

  • servicessrcmainjavaorgkeycloakservicesresourcesadminUsersResource.java
  • modeljpasrcmainjavaorgkeycloakmodelsjpaJpaUserProvider.java

所以只是提供了内置这几个参数的模糊搜索;
所以只能自己去定制扩展了;

官方的定制扩展方案

Keycloak定制开发文档

主要以下三步:

  • 实现一个 KeyCloakUserApiProviderFactory
  • 实现 KeyCloakUserApiProvider进行自己的业务逻辑书写
  • 在srcmainresourcesmeta-INFservicesorg.keycloak.services.resource.RealmResourceProviderFactory文件注册自己的提供器

本文参考的代码为:
https://dev.to/silentrobi/keycloak-custom-rest-api-search-by-user-attribute-keycloak-3a8c

在此基础上:

  • 去除了 userMapper的映射,因为我需要全量的用户信息
  • 实现了自己的Representation映射
  • 添加了 briefRepresentation 特性
  • 添加 token校验和角色校验
代码示意截图
  • srcmainjavakeycloakapiextensionKeyCloakUserApiProviderFactory.java
public class KeyCloakUserApiProviderFactory implements RealmResourceProviderFactory {
    public static final String ID = "userapi-rest";

    public RealmResourceProvider create(KeycloakSession session) {
        return new KeyCloakUserApiProvider(session);
    }

    public void init(Scope config) {
    }

    public void postInit(KeycloakSessionFactory factory) {
    }

    public void close() {
    }

    public String getId() {
        return ID;
    }
}
  • srcmainjavakeycloakapiextensionKeyCloakUserApiProvider.java
public class KeyCloakUserApiProvider implements RealmResourceProvider {
    private final KeycloakSession session;
    private final AuthenticationManager.AuthResult auth;
    private final String defaultAttr = "merchent_id";

    public KeyCloakUserApiProvider(KeycloakSession session) {
        this.session = session;
        this.auth = new AppAuthManager.BearerTokenAuthenticator(session).authenticate();
    }

    public void close() {
    }

    public Object getResource() {
        return this;
    }

    @GET
    @Path("users/search-by-attr")
    @NoCache
    @Produces({ MediaType.APPLICATION_JSON })
    @Encoded
    public List searchUsersByAttribute(@DefaultValue(defaultAttr) @QueryParam("attr") String attr,
            @QueryParam("value") String value, @QueryParam("briefRepresentation") Boolean briefRepresentation) {
                checkRealmAdmin();
        boolean briefRepresentationB = briefRepresentation != null && briefRepresentation;
        RealmModel realm = session.getContext().getRealm();
        Stream userModels = session.users()
                .searchForUserByUserAttributeStream(session.getContext().getRealm(), attr, value);

        return userModels.map(user -> {
            UserRepresentation userRep = briefRepresentationB ? ModelToRepresentation.toBriefRepresentation(user)
                    : ModelToRepresentation.toRepresentation(session, realm, user);
            return userRep;
        }).collect(Collectors.toList());
    }

    private void checkRealmAdmin() {
        if (auth == null) {
            throw new NotAuthorizedException("Bearer");
        } else if (auth.getToken().getRealmAccess() == null || !auth.getToken().getRealmAccess().isUserInRole("admin")) {
            throw new ForbiddenException("Does not have realm admin role");
        }
    }
}
Installation
  1. Run mvn clean install command from CLI. This will generate a target folder. Under the target folder there will be {project artifact id}-*.jar file.
  2. Copy that jar file to the Keycloak standalone/deployments/ directory. For an example, If you run your Keycloak in docker container, you can use the following command:
docker cp  keycloak:/opt/jboss/keycloak/standalone/deployments/
测试验证 获取访问令牌
 curl --location --request POST 'http://localhost:8080/auth/realms/austintest/protocol/openid-connect/token' --header 'Content-Type: application/x-www-form-urlencoded' --data-urlencode 'client_id=admin-cli' --data-urlencode 'username=admin1' --data-urlencode 'password=123456' --data-urlencode 'grant_type=password' 
不带令牌访问

Request:

curl --location --request GET 'http://localhost:8080/auth/realms/austintest/userapi-rest/users/search-by-attr?attr=phone&value=14255633'

Response:
返回无权限

{"error":"HTTP 401 Unauthorized"}
不带admin角色的用户

Request:

 curl --location --request GET 'http://localhost:8080/auth/realms/austintest/userapi-rest/users/search-by-attr?attr=merchant_id&value=1' --header 'Authorization: Bearer eyJhbGciOiJSUInR5cCIgOiAiSldUIiwia2lkIiA6ICJOdHBtTmtOV1dBLWs4TlNTeWpSZHBLX0RMLUdLVFR2WXdsTndPdzM5VXJRIn0.eyJleHAiOjE2MzQ2MzgxMjksImlhdCI6MTYzNDYzNzgyOSwianRpIjoiNDIyODBkYzgtOTkyNS00OGE4LWI4MTYtODcA1ZWZmIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL2F1c3RpbnRlc3QiLCJzdWIiOiI5Y2M5MjBhZC1jYjQ1LTQ4MzAtYmIxYS1kYTVmZDM0NWQ3Y2IiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJhZG1pbi1jbGkiLaW9uX3N0YXRlIjoiOTNlNWZiMWItMzEzOC00NmQ4LTg2ZTctNGZkZmQxNzY1MTcwIiwiYWNyIjoiMSIsInNjb3BlIjoiZW1haWwgcHJvZmlsZSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwibmFtZSI6ImFkbWluIGF1c3RpbiIsInByZWZlcnJVybmFtZSI6ImFkbWluMSIsImdpdmVuX25hbWUiOiJhZG1pbiIsImZhbWlseV9uYW1lIjoiYXVzdGluIiwiZW1haWwiOiJhZG1pbjFAcXEuY29tIn0.H2QwOG6LRN-TF1YCVpbaVU7ILd0OVNfCEtDZZ5zZnObArkphgaCd9BaHk9tbcGsH8OR55qUI3S1ZkZim0EHwaWluo9CVrE-orOccs3Tth_awJeOJMtRTBeNr5I5rYGi0aSP1YZEsyxvjigkekP4z82IizPdZjyfs9LjZJEKq5SKxUVL5LIAzfsE99aJp_AAGeITqswTsjkpN3wOZ4TcwEeb-XHMhTekEjAl1fQuE9eshPBe3qMdAXD2eN8mC21KBY8RzZq4bZjdwLAia_a2WxTjFr12pCUSuIVrv5kw2nqoPWxj5I0HHHyIMdUNDWvdc9wz9o2LA'

Response:
返回无权限

{"error":"Does not have realm admin role"}
令牌携带角色正确

Request:

 curl --location --request GET 'http://localhost:8080/auth/realms/austintest/userapi-rest/users/search-by-attr?attr=merchant_id&value=1' --header 'Authorization: Bearer eyJhbGciOiJSUInR5cCIgOiAiSldUIiwia2lkIiA6ICJOdHBtTmtOV1dBLWs4TlNTeWpSZHBLX0RMLUdLVFR2WXdsTndPdzM5VXJRIn0.eyJleHAiOjE2MzQ2MzgxMjksImlhdCI6MTYzNDYzNzgyOSwianRpIjoiNDIyODBkYzgtOTkyNS00OGE4LWI4MTYtODcA1ZWZmIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL2F1c3RpbnRlc3QiLCJzdWIiOiI5Y2M5MjBhZC1jYjQ1LTQ4MzAtYmIxYS1kYTVmZDM0NWQ3Y2IiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJhZG1pbi1jbGkiLaW9uX3N0YXRlIjoiOTNlNWZiMWItMzEzOC00NmQ4LTg2ZTctNGZkZmQxNzY1MTcwIiwiYWNyIjoiMSIsInNjb3BlIjoiZW1haWwgcHJvZmlsZSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwibmFtZSI6ImFkbWluIGF1c3RpbiIsInByZWZlcnJVybmFtZSI6ImFkbWluMSIsImdpdmVuX25hbWUiOiJhZG1pbiIsImZhbWlseV9uYW1lIjoiYXVzdGluIiwiZW1haWwiOiJhZG1pbjFAcXEuY29tIn0.H2QwOG6LRN-TF1YCVpbaVU7ILd0OVNfCEtDZZ5zZnObArkphgaCd9BaHk9tbcGsH8OR55qUI3S1ZkZim0EHwaWluo9CVrE-orOccs3Tth_awJeOJMtRTBeNr5I5rYGi0aSP1YZEsyxvjigkekP4z82IizPdZjyfs9LjZJEKq5SKxUVL5LIAzfsE99aJp_AAGeITqswTsjkpN3wOZ4TcwEeb-XHMhTekEjAl1fQuE9eshPBe3qMdAXD2eN8mC21KBY8RzZq4bZjdwLAia_a2WxTjFr12pCUSuIVrv5kw2nqoPWxj5I0HHHyIMdUNDWvdc9wz9o2LA'

Response:

[{"id":"d099df5f-286d-4b77-9911-f30a3da7cffb","createdTimestamp":1627290490426,"username":"测试用户","enabled":true,"totp":false,"emailVerified":false,"firstName":"杭州有限公司","lastName":"名称","email":"austin@qq.com","attributes":{"haha":["hall"],"avatar":["https://qhyxpicoss.kujiale.com/avatars/41.jpg"],"phone":["14255633"]},"disableableCredentialTypes":[],"requiredActions":[],"notBefore":0}]
带briefRepresentation=true参数

Request:

curl --location --request GET 'http://localhost:8080/auth/realms/austintest/userapi-rest/users/search-by-attr?attr=phone&value=14255633&briefRepresentation=true' --header 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJOdHBtTmtOV1dBLWs4TlNTeWpSZHBLX0RMLUdLVFR2WXdsTndPdzM5VXJRIn0.eyJleHAiOjE2MzQ2Mzg1NTEsImlhdCI6MTYzNDYzODI1MSwianRpIjoiNDQ1ZTIyOTEtMWIyMi00YTVhLThmMjgtYTc1OWQzOTU1NDMxIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL2F1c3RpbnRlc3QiLCJhdWQiOlsicmVhbG0tbWFuYWdlbWVudCIsImFjY291bnQiXSwic3ViIjoiOWNjOTIwYWQtY2I0NS00ODMwLWJiMWEtZGE1ZmQzNDVkN2NiIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiYWRtaW4tY2xpIiwic2Vzc2lvbl9zdGF0ZSI6IjE1ZWQ2MGEwLTQ1YWQtNDE4MC05M2QzLWYzZTMyYzcwNzFjOSIsImFjciI6IjEiLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiZGVmYXVsdC1yb2xlcy1hdXN0aW50ZXN0Iiwib2ZmbGluZV9hY2Nlc3MiLCJhZG1pbiIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsicmVhbG0tbWFuYWdlbWVudCI6eyJyb2xlcyI6WyJ2aWV3LWlkZW50aXR5LXByb3ZpZGVycyIsInZpZXctcmVhbG0iLCJtYW5hZ2UtaWRlbnRpdHktcHJvdmlkZXJzIiwiaW1wZXJzb25hdGlvbiIsInJlYWxtLWFkbWluIiwiY3JlYXRlLWNsaWVudCIsIm1hbmFnZS11c2VycyIsInF1ZXJ5LXJlYWxtcyIsInZpZXctYXV0aG9yaXphdGlvbiIsInF1ZXJ5LWNsaWVudHMiLCJxdWVyeS11c2VycyIsIm1hbmFnZS1ldmVudHMiLCJtYW5hZ2UtcmVhbG0iLCJ2aWV3LWV2ZW50cyIsInZpZXctdXNlcnMiLCJ2aWV3LWNsaWVudHMiLCJtYW5hZ2UtYXV0aG9yaXphdGlvbiIsIm1hbmFnZS1jbGllbnRzIiwicXVlcnktZ3JvdXBzIl19LCJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6ImVtYWlsIHByb2ZpbGUiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsIm5hbWUiOiJhZG1pbiBhdXN0aW4iLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJhZG1pbjEiLCJnaXZlbl9uYW1lIjoiYWRtaW4iLCJmYW1pbHlfbmFtZSI6ImF1c3RpbiIsImVtYWlsIjoiYWRtaW4xQHFxLmNvbSJ9.fTRmGM2mZmC_NQC5s_9s79oySWOSPpPsk2GF8lcBgDnV_BO54ebTQmImyzvpfx6RsaWCc1Cba85s5Qx1FKBjmEDYFjjahfojMN3fO2-fxhK5mcqGgTBLk3tZeIA6b_dcSwVjqNZSc9p7tvKGEatpF8Ll58dPGMut0fTr60A7pgo7FV42_9wmX-oAmcwERJqbBqgzIeb_-hdQPz2-NHBAJBb79xTuBrcKBLNhUagbTaIOJNVGmSksaR2G9svsqnhabrPalSOwVfTH5AHg869qbrPy1s-PyxQdyruI4RBL6aHWTXK-pd0wzEwOkdDDlt4Re8dhFyvuQU4VNcAGJ1-mfQ'

Response:

[{"id":"d099df5f-286d-4b77-9911-f30a3da7cffb","createdTimestamp":1627290490426,"username":"测试用户","enabled":true,"emailVerified":false,"firstName":"杭州有限公司","lastName":"名称","email":"austin@qq.com"}]
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/338096.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号