【问题标题】:Searching for Keycloak user via attribute - searchForUserByUserAttribute - how is it fast?通过属性搜索 Keycloak 用户 - searchForUserByUserAttribute - 速度如何?
【发布时间】:2022-09-23 00:20:06
【问题描述】:

没有使用keycloak的经验,并且在大量的谷歌之后无法找到答案。快速提问 - 我有一个自定义属性 userOrg,它是一个 uuid。它映射到位于 keycloak 之外的另一个数据库中的用户组织,并包含有关该组织的完整详细信息(例如名称、位置)。

我正在查看一些代码,看到以前的队友编写了自定义 keycloak api 扩展,findUsersByAttribute,它使用 session.users().searchForUserByUserAttribute 找到具有指定userOrg 的所有用户。

我猜这实际上是一个完整的餐桌步行?

还是我错了,钥匙斗篷不知何故提供对属性的索引以允许快速查找?

下一个问题 - keycloak 是否提供了一种对用户组织的属性/想法进行索引的方法。或者该逻辑是否应该在 keycloak 之外(例如,在另一个数据库中有用户和组织的映射)。

它在文档中的任何地方都有说明吗?

谢谢

    标签: keycloak keycloak-rest-api


    【解决方案1】:

    您可以通过Admin REST API 按用户属性搜索用户。

    端点。这取决于您的 Keycloak 版本

    {keycloak URL}/auth/admin/realms/{my-realm}/users/?q=userOrg:{uuid}
    or
    {keycloak URL}/admin/realms/{my-realm}/users/?q=userOrg:{uuid}
    

    Get users Returns a stream of users, filtered according to query parameters. 部分

    我在本地 Keycloak v18.0.2 和 Postman 中对其进行了演示

    1 我通过 UI 创建了 3 个用户

    2 每个用户都有自己的 userOrg

    3Get master token

    4 通过 UUID 搜索用户

    http://localhost:8180/auth/admin/realms/my-realm/users/?q=userOrg:3db51e81-7569-4a05-aaf3-91058450c63e

    Keycloak 响应通过属性搜索 API 匹配用户

    我测试了 10K 用户。我的笔记本电脑需要 10~12 毫秒。

    这是我的用户由 python 创建的程序

    import json
    import admin
    import random
    import uuid
    
    admin = admin.Admin()
    token = admin.get_master_token()
    
    first_names = [ \
        "AARON","ADAM","ALAN","ALBERT","ANDREW","ANTHONY" \
        ,"ANTONIO","ARTHUR","BENJAMIN","BILLY","BOBBY" \
        ,"BRANDON","BRIAN","BRUCE","CARL","CARLOS" \
        ,"CHARLES","CHRIS","CHRISTOPHER","CLARENCE","CRAIG" \
        ,"DANIEL","DAVID","DENNIS","DONALD","DOUGLAS" \
        ,"EARL","EDWARD","ERIC","ERNEST","EUGENE" \
        ,"FRANK","FRED","GARY","GEORGE","GERALD" \
        ,"GREGORY","HAROLD","HARRY","HENRY","HOWARD" \
        ,"JACK","JAMES","JASON","JEFFREY","JEREMY" \
        ,"JERRY","JESSE","JIMMY","JOE","JOHN" \
        ,"JOHNNY","JONATHAN","JOSE","JOSEPH","JOSHUA" \
        ,"JUAN","JUSTIN","KEITH","KENNETH","KEVIN" \
        ,"LARRY","LAWRENCE","LOUIS","MARK","MARTIN" \
        ,"MATTHEW","MICHAEL","NICHOLAS","PATRICK","PAUL" \
        ,"PETER","PHILIP","PHILLIP","RALPH","RANDY" \
        ,"RAYMOND","RICHARD","ROBERT","ROGER","RONALD" \
        ,"ROY","RUSSELL","RYAN","SAMUEL","SCOTT" \
        ,"SEAN","SHAWN","STEPHEN","STEVE","STEVEN" \
        ,"TERRY","THOMAS","TIMOTHY","TODD","VICTOR" \
        ,"WALTER","WAYNE","WILLIAM","WILLIE"]
    
    last_names = [ \
        "Adams","Allen","Alvarez","Anderson","Bailey",\
        "Baker","Bennet","Brooks","Brown","Campbell",\
        "Carter","Castillo","Chavez","Clark","Collins",\
        "Cook","Cooper","Cox","Cruz","Davis",\
        "Diaz","Edwards","Evans","Flores","Foster",\
        "Garcia","Gomez","Gonzales","Gray","Green",\
        "Gutierrez","Hall","Harris","Hernandez","Hill",\
        "Howard","Hughes","Jackson","James","Jimenez",\
        "Johnson","Jones","Kelly","Kim","King"\
        "Lee","Lewis","Long","Lopez","Martin",\
        "Martinez","Mendoza","Miller","Mitchell","Moore",\
        "Morales","Morgan","Morris","Murphy","Myers",\
        "Nelson","Nguyen","Ortiz","Parker","Patel"\
        "Perez","Peterson","Phillips","Price","Ramirez",\
        "Ramos","Reed","Reyes","Richardson","Rivera",\
        "Roberts","Robinson","Rodriguez","Rogers","Ross",\
        "Ruiz","Sanchez","Sanders","Scott","Smith",\
        "Stewart","Taylor","Thomas","Thompson","Torres",\
        "Turner","Walker","Ward","Watson","White",\
        "Williams","Wilson","Wood","Wright","Young",]
    
    user_list = []
    index = 1
    user_data = {}
    access_item = {}
    user_list = []
    max_user_count = 10000
    size_first_name = len(first_names)
    size_last_name = len(last_names)
    for index in range(1,max_user_count+1):
        first_name_index = random.randint(0, size_first_name)
        last_name_index = random.randint(0, size_last_name)
        user_data['enabled'] = True
        user_data['groups'] = []
        user_data['emailVerified'] = ''
        user_data['firstName'] = first_names[first_name_index-1].capitalize()
        user_data['lastName'] = last_names[last_name_index-1]
        user_data['username'] = 'user'+str(index)
        user_data['email'] = 'user'+str(index)+'@test.com' 
        user_data['attributes'] = { 'userOrg' : [ str(uuid.uuid4())]}
        user_list.append(user_data)
        print(json.dumps(user_data))
        user_data = {}
    
    # add user if not exist
    for user in user_list:
        if (not admin.is_user_exist(token, 'test', user['username'])):
            admin.add_user(token, user, 'test')
            print('Add User', user['username'])
    
    

    获取令牌代码,名称应为admin.py

    from urllib.error import HTTPError
    
    import requests
    import ast
    import json
    
    class Admin:
        # Keycloak master realm URL
        url = 'http://localhost:8180/auth/realms/master/protocol/openid-connect/token'
    
        # Keycloak master credential
        params = {
            'client_id': 'admin-cli',
            'grant_type': 'password',
            'username' : 'admin',
            'password': 'admin'
        }
     
        def get_master_token(self):
            try:
                response = requests.post(self.url, self.params, verify=False).content.decode('utf-8')
            except HTTPError as http_err:
                print(f'HTTP error occurred: {http_err}')  # Python 3.6
            except Exception as err:
                print(f'Other error occurred: {err}')  # Python 3.6
                print('Keycloak container is not running, Please check your docker container!')
                raise SystemExit
            else:
                return ast.literal_eval(response)['access_token']
    
                
    
        def add_user(self, token, user, realm_name):
            url ='http://localhost:8180/auth/admin/realms/'+realm_name+'/users'
            headers = {
                'content-type': 'application/json',
                'Authorization' : 'Bearer '+ str(token)
            }
            params = {
                'username': user['username'],
                'enabled': True,
                'totp': False,
                'emailVerified': True,
                'firstName': user['firstName'],
                'lastName': user['lastName'],
                'email': user['email'],
                'attributes': user['attributes'],
                'disableableCredentialTypes': [],
                'requiredActions': [],
                'notBefore': 0,
                'access': {
                    'manageGroupMembership': True,
                    'view': True,
                    'mapRoles': True,
                    'impersonate': True,
                    'manage': True
                },
                'realmRoles': [ realm_name ]    
            }
            x = requests.post(url, headers=headers, json=params)
            return x.content
    
        def is_user_exist(self, token, realm_name, user_name):
            url ='http://localhost:8180/auth/admin/realms/'+realm_name+'/users/?username='+user_name.replace(" ", "%20")
            headers = {
                'content-type': 'application/json',
                'Authorization' : 'Bearer '+ str(token)
            }
            try:
                response = requests.get(url, headers=headers)
                response.raise_for_status()
            except HTTPError as http_err:
                print(f'HTTP error occurred: {http_err}')  # Python 3.6
            except Exception as err:
                print(f'Other error occurred: {err}')  # Python 3.6
            else:
                if len(response.content) == 2: # []
                    return False
                if (json.loads(response.text)[0]['username'] == user_name.lower()):
                    return True
                else:
                    return False
    

    【讨论】:

    • 感谢您的回复。我想你错过了我的问题。我知道你可以按属性过滤。我想了解的是它的速度/效率。是全表扫描还是索引?
    • 我认为这是 DB SQL 谓词搜索的全表扫描。搜索并不需要时间。您处理多少用户以及您期望多快?这是user attribute search的源代码
    • 我测试了 10K 用户并获得了响应时间。搜索需要 10~12 毫秒。
    猜你喜欢
    • 2021-03-22
    • 1970-01-01
    • 2019-10-15
    • 2021-05-26
    • 2018-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多