Saltar al contenido

¿Cómo puedo leer a todos los usuarios que usan keycloak y spring?

Bienvenido a nuestro sitio, en este lugar vas a encontrar la respuesta de lo que andabas buscando.

Solución:

Para acceder a la lista completa de usuarios, debe verificar que el usuario registrado contiene al menos el view-users papel de la realm-management cliente, vea esta respuesta que escribí hace algún tiempo. Una vez que el usuario tiene este rol, el JWT que recupera lo mantendrá.

Como puedo inferir de sus comentarios, parece que le faltan algunas bases sobre la Authorization encabezamiento. Una vez que el usuario inicia sesión, obtiene el JWT firmado de keycloak, para que todos los clientes del reino puedan confiar en él, sin necesidad de preguntar a Keycloak. Este JWT contiene el token de acceso, que luego se requiere en el Authorization encabezado para cada solicitud del usuario, con el prefijo Bearer palabra clave (ver Autenticación basada en token en https://auth0.com/blog/cookies-vs-tokens-definitive-guide/).

Entonces, cuando el usuario realiza la solicitud a su aplicación para ver la lista de usuarios, su token de acceso que contiene el view-users El rol ya entra en los encabezados de la solicitud. En lugar de tener que analizarlo manualmente, cree otra solicitud usted mismo para acceder al punto final del usuario de Keycloak y adjúntelo (como parece estar haciendo con KeycloakBuilder), el adaptador Keycloak Spring Security ya proporciona un KeycloakRestTemplate class, que puede realizar una solicitud a otro servicio para el usuario actual:

SecurityConfig.java

@Configuration
@EnableWebSecurity
@ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter 

    ...

    @Autowired
    public KeycloakClientRequestFactory keycloakClientRequestFactory;

    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public KeycloakRestTemplate keycloakRestTemplate() 
        return new KeycloakRestTemplate(keycloakClientRequestFactory);
    

    ...

Tenga en cuenta que el alcance de la plantilla es PROTOTYPE, por lo que Spring usará una instancia diferente para cada una de las solicitudes que se realicen.

Luego, conecte automáticamente esta plantilla y utilícela para realizar solicitudes:

@Service
public class UserRetrievalService

    @Autowired
    private KeycloakRestTemplate keycloakRestTemplate;

    public List getUsers() 
        ResponseEntity response = keycloakRestTemplate.getForEntity(keycloakUserListEndpoint, User[].class);
        return Arrays.asList(response.getBody());
    


Deberá implementar su propio User clase que coincide con la respuesta JSON devuelta por el servidor de capa de claves.

Tenga en cuenta que, cuando el usuario no tiene permiso para acceder a la lista, el servidor Keycloak devuelve un código de respuesta 403. Incluso podrías negarlo antes que tú mismo, usando algunas anotaciones como: @PreAuthorize("hasRole('VIEW_USERS')").

Por último, pero no menos importante, creo que la respuesta de @ dchrzascik está bien orientada. En resumen, diría que en realidad hay otra forma de evitar recuperar la lista completa de usuarios del servidor keycloak cada vez o que sus usuarios se almacenen en la base de datos de su aplicación: en realidad, podría almacenarlos en caché, para poder actualizar ese caché si hacer la gestión de usuarios desde su aplicación.


EDITAR

Implementé un proyecto de muestra para mostrar cómo obtener la lista completa de usuarios, cargada en Github. Está configurado para un cliente confidencial (cuando se usa un cliente público, el secreto debe eliminarse de la aplicación.propiedades).

Ver también:

  • https://github.com/keycloak/keycloak-documentation/blob/master/securing_apps/topics/oidc/java/spring-security-adapter.adoc

Sugiero que verifiques dos veces si realmente necesitas tener tu propia tienda de usuario. Debe confiar únicamente en la federación de usuarios de Keycloak para evitar la duplicación de datos y, por lo tanto, evitar los problemas que conlleva. Entre otros, Keycloak es responsable de administrar usuarios y debes dejar que haga su trabajo.

Dado que está utilizando OIDC, hay dos cosas de las que se beneficia:

  1. En el token de identidad que obtiene en forma de JWT, tiene un campo “sub”. Este campo identifica de forma única a un usuario. De la especificación de OpenID Connect:

    REQUERIDO. Identificador de sujeto. Un identificador local único y nunca reasignado dentro del Emisor para el Usuario final, que está destinado a ser consumido por el Cliente, por ejemplo, 24400320 o AItOawmwtWwcT0k51BayewNvutrJUqsvl6qs7A4. NO DEBE exceder los 255 caracteres ASCII de longitud. El subvalor distingue entre mayúsculas y minúsculas string.

    En keycloak, “sub” es solo un UUID. Puede utilizar este campo para correlacionar su “objeto A” con el “usuario B”. En su base de datos, esto sería solo una columna regular, no una extranjera key.

    En Java, puede acceder a estos datos JWT utilizando el contexto de seguridad. También puede echar un vistazo al inicio rápido authz-springboot de keycloak donde se muestra cómo puede acceder a KeycloakSecurityContext; desde allí puede obtener un IDToken que tiene un método getSubject.

  2. Keycloak proporciona una API REST de administración que tiene un recurso de usuario. Esta es una API compatible con OIDC, por lo que debe estar debidamente autenticado. Con esa API, puede realizar operaciones en los usuarios, incluido enumerarlos. Puede consumir esa API directamente o mediante el uso de Java SDK: cliente de administración de keycloak.

    En este escenario, debe usar el JWT que obtiene del usuario en la solicitud. Al usar JWT, está seguro de que alguien que realiza una solicitud puede enumerar todos los usuarios en ese ámbito. Por ejemplo, considere el siguiente código:

    @GetMapping("/users")
    public List check(HttpServletRequest request)
        KeycloakSecurityContext context = (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName());
    
        Keycloak keycloak = KeycloakBuilder.builder()
                                       .serverUrl("http://localhost:8080/auth")
                                       .realm("example")
                                       .authorization(context.getTokenString())
                                       .resteasyClient(new ResteasyClientBuilder().connectionPoolSize(20).build())
                                       .build();
    
       List list = keycloak.realm("example").users().list();
    
       return list;
    
    

    En ese caso, estamos usando HttpServletRequest y el token que contiene. Podemos obtener los mismos datos mediante el uso de org.springframework.security.core.Authentication de Spring Security o directamente obteniendo un encabezado de Autorización. El caso es que KeycloakBuilder espera un string como una ‘autorización’, no un AccessToken: esta es la razón por la que tiene ese error.

    Tenga en cuenta que para que esto funcione, el usuario que está creando una solicitud debe tener un rol de ‘ver-usuarios’ del cliente de ‘administración de dominios’. Puede asignarle ese rol en la pestaña ‘Asignación de roles’ para ese usuario o algún grupo al que pertenece.

    Además, debe estar debidamente autenticado para beneficiarse del contexto de seguridad; de lo contrario, obtendrá un null. La clase de configuración de keycloak de seguridad de primavera ejemplar es:

    @Configuration
    @EnableWebSecurity
    @ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
    class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter 
    
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception 
        KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
        keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
        auth.authenticationProvider(keycloakAuthenticationProvider);
    
    
    @Bean
    public KeycloakSpringBootConfigResolver KeycloakConfigResolver() 
        return new KeycloakSpringBootConfigResolver();
    
    
    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() 
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    
    
    @Override
    protected void configure(HttpSecurity http) throws Exception 
        super.configure(http);
        http.authorizeRequests()
            .antMatchers("/api/users/*")
            .hasRole("admin")
            .anyRequest()
            .permitAll();
    
    
    

valoraciones y reseñas

Si estás de acuerdo, tienes la libertad de dejar una crónica acerca de qué le añadirías a esta crónica.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *