【问题标题】:How to add programmed custom field to Keycloak user如何将编程的自定义字段添加到 Keycloak 用户
【发布时间】:2023-02-22 21:29:42
【问题描述】:

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

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

有可能这样做吗?

【问题讨论】:

    标签: keycloak


    【解决方案1】:

    用户的Attribute可以保存用户邮箱的MD5哈希值。 Keycloak 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": "user2@gmail.com",
        "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": "user2@gmail.com",
            "attributes": {
                "MD5": [
                    "fa7c3fcb670a58aa3e90a391ea533c99"
                ]
            },
            "disableableCredentialTypes": [],
            "requiredActions": [],
            "notBefore": 0,
            "access": {
                "manageGroupMembership": true,
                "view": true,
                "mapRoles": true,
                "impersonate": true,
                "manage": true
            }
        }
    ]
    

    在 Keycloak 用户界面中

    参考

    Searching for Keycloak user via attribute - searchForUserByUserAttribute - how is it fast?

    Keycloak v.18: How to manipulate with users using Keycloak API

    【讨论】:

      猜你喜欢
      • 2018-08-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-26
      • 1970-01-01
      相关资源
      最近更新 更多