如何向 Keycloak 用户添加编程的自定义字段

问题描述 投票:0回答:2

我想为Keycloak用户添加一个自定义字段,即创建用户后用户电子邮件的MD5哈希。

我也搜索了Keycloak用户的自定义字段,但似乎无法对其进行编程。我正在考虑开发一个 Keycloak 包装器,但如果已经有一个内置的解决方案那就太好了。

可以吗?

keycloak
2个回答
0
投票

用户的

Attribute
可以保存用户邮箱的MD5哈希值。 Keycloak API 也支持按哈希值搜索。

用户更新API

PUT {Keycloak URL}/admin/realms/{realm}/users/{user-id}

体内

{
  "id": <user id>,
  "username": <user name>,
  "attributes": { "MD5": [ <user email MD5 hash >] }
}

按属性搜索用户

GET {Keycloak URL}/admin/realms/{realm}/users?q={attribute key}:{attribute value}

例子,按用户的MD5值搜索

GET http://localhost:8080/auth/admin/realms/test/users?q=MD5:3b7c8c7791f4f4c7cdd712635277a1f2

使用 node.js 的演示

const axios = require('axios')
const crypto = require('crypto')

const getMasterToken = async () => {
    try {
        const response = await axios.post(
            url = 'http://localhost:8080/auth/realms/master/protocol/openid-connect/token',
            data = new URLSearchParams({
                'client_id': 'admin-cli',
                'username': 'admin',
                'password': 'admin',
                'grant_type': 'password'
            }),
            config = {
                headers:
                {
                    'Content-Type': 'application/x-www-form-urlencoded'
                }
            })
        return Promise.resolve(response.data.access_token)
    } catch (error) {
        return Promise.reject(error)
    }
}

const getUser = async (token, username) => {
    try {
        const response = await axios.get(
            url = `http://localhost:8080/auth/admin/realms/test/users?username=${username}`,
            config = {
                headers: {
                    'Accept-Encoding': 'application/json',
                    'Authorization': `Bearer ${token}`,
                }
            }
        );
        return Promise.resolve(response.data[0])
    } catch (error) {
        return Promise.reject(error)
    }
}


const addUserAttribute = async (token, user_data) => {
    try {
        const MD5 = crypto.createHash('md5').update(`${user_data.email}`).digest("hex")
        const newUserData = {
            "id": user_data.id,
            "username": user_data.username,
            "attributes": { "MD5": [MD5] }
        }
        const response = await axios.put(
            url = `http://localhost:8080/auth/admin/realms/test/users/${user_data.id}`,
            data = newUserData,
            config = {
                headers:
                {
                    'Content-Type': 'application/json',
                    'Authorization': `Bearer ${token}`,
                }
            })
        // response.status = 204 No Content. it means success to update
        return Promise.resolve(MD5)
    } catch (error) {
        return Promise.reject(error)
    }
}

const getUserByMD5 = async (token, MD5) => {
    try {
        const response = await axios.get(
            url = `http://localhost:8080/auth/admin/realms/test/users?q=MD5:${MD5}`,
            config = {
                headers: {
                    'Accept-Encoding': 'application/json',
                    'Authorization': `Bearer ${token}`,
                }
            }
        );
        return Promise.resolve(response.data)
    } catch (error) {
        return Promise.reject(error)
    }
}

getMasterToken()
    .then((token) => {
        getUser(token, 'user2')
            .then((user_data) => {
                console.log(JSON.stringify(user_data, null, 4))
                addUserAttribute(token, user_data)
                    .then((MD5) => {
                        console.log(`${user_data.username}'s MD5:` + MD5)
                        getUserByMD5(token, MD5)
                            .then((user_update_data) => {
                                console.log(JSON.stringify(user_update_data, null, 4))
                            })
                    })
            })
    })
    .catch(error => console.log(error));

结果

$ node update-user.js
{
    "id": "a3831b6a-63e5-471d-b71c-6c7d9f49ee47",
    "createdTimestamp": 1677063973333,
    "username": "user2",
    "enabled": true,
    "totp": false,
    "emailVerified": false,
    "firstName": "Tom",
    "lastName": "Cruise",
    "email": "[email protected]",
    "disableableCredentialTypes": [],
    "requiredActions": [],
    "notBefore": 0,
    "access": {
        "manageGroupMembership": true,
        "view": true,
        "mapRoles": true,
        "impersonate": true,
        "manage": true
    }
}
user2's MD5:fa7c3fcb670a58aa3e90a391ea533c99
[
    {
        "id": "a3831b6a-63e5-471d-b71c-6c7d9f49ee47",
        "createdTimestamp": 1677063973333,
        "username": "user2",
        "enabled": true,
        "totp": false,
        "emailVerified": false,
        "firstName": "Tom",
        "lastName": "Cruise",
        "email": "[email protected]",
        "attributes": {
            "MD5": [
                "fa7c3fcb670a58aa3e90a391ea533c99"
            ]
        },
        "disableableCredentialTypes": [],
        "requiredActions": [],
        "notBefore": 0,
        "access": {
            "manageGroupMembership": true,
            "view": true,
            "mapRoles": true,
            "impersonate": true,
            "manage": true
        }
    }
]

在 Keycloak 用户界面中

参考文献

通过属性搜索 Keycloak 用户 - searchForUserByUserAttribute - 速度如何?

Keycloak v.18:如何使用 Keycloak API 与用户进行操作


0
投票

在 Java 中我们有一个名为 keycloak-admin-client 的依赖,首先你应该在你的 pom.xml 文件中添加这个依赖。我们使用弹簧靴。

    <dependency>
        <groupId>org.keycloak</groupId>
        <artifactId>keycloak-admin-client</artifactId>
        <version>${keycloak.version}</version>
    </dependency>

    <dependency>
        <groupId>org.keycloak.bom</groupId>
        <artifactId>keycloak-adapter-bom</artifactId>
        <version>${keycloak.version}</version>
        <type>pom</type>
        <scope>import</scope>
    </dependency>

Keycloak-admin-client 提供了一些使用 keycloak 的 api 首先,您应该创建一个名为 KeycloakService 的类和一个名为 UserDTO 的 Dto,如下所示

       @Data   
       public class UserDTO  {

          private static final long serialVersionUID = 1L;

          private String username;

          private String emailAddress;

          private String firstName;

          private String lastName;

          private String password;

          private String telephone;

          private String preferredLocal;

          private String accessibleViaMobile;

          private String fax;

          private String mobile;

          private String profile;

          private String accessProfile;

          private String notificationPreference;

          }

而 keycloakService 就像:

        @Service
        public class KeycloakService {

          @Autowired
          private final Keycloak keycloak;

          public UserDTO createUserInKeyCloak(UserDTO userDTO) {
          int statusId = 0;
          RealmResource realmResource = keycloak.realm(REALM);
          UsersResource userRessource = realmResource.users();
          UserRepresentation user = new UserRepresentation();
          user.setUsername(userDTO.getUsername());
          user.setEmail(userDTO.getEmailAddress());
          user.setFirstName(userDTO.getFirstName());
          user.setLastName(userDTO.getLastName());
          user.setEnabled(true);
          Map<String, List<String>> attr = new HashMap<>;
          List<String> attrString = new ArrayList<>;
          attrString.add("YOUR_MD5");
          attr.put("MD5",attrString)
          user.setAttributes(attr);
          // Create user
          Response result = userRessource.create(user);
          statusId = result.getStatus();
          if (statusId == 201) {
          String userId = 
          result.getLocation().getPath().replaceAll(".*/([^/]+)$", 
          "$1");
          System.out.println("User created with userId:" + 
          userId);
          // Define password credential
          CredentialRepresentation passwordCred = new 
          CredentialRepresentation();
          passwordCred.setTemporary(false);
          passwordCred.setType(CredentialRepresentation.PASSWORD);
          passwordCred.setValue(userDTO.getPassword());
          // Set password credential
          userRessource.get(userId).resetPassword(passwordCred);
          System.out.println("Username==" + userDTO.getUsername() 
          + " created in keycloak successfully");
          return userDTO;
          } else if (statusId == 409) {
            throw new CustomException(409, "the user is currently 
            exists");
          } else {
        throw new CustomException(statusId, "the user could not be 
         created in keycloak");
    }
 }
 }
© www.soinside.com 2019 - 2024. All rights reserved.