source

키클로크와 스프링을 사용하여 모든 사용자를 읽으려면 어떻게 해야 합니까?

lovecheck 2023. 9. 4. 20:25
반응형

키클로크와 스프링을 사용하여 모든 사용자를 읽으려면 어떻게 해야 합니까?

는 중용사를 합니다.keycloak 3.4그리고.spring boot웹 앱을 개발하는 것.Active Directory를 사용자 연합으로 사용하여 모든 사용자 정보를 검색하고 있습니다.

하지만 이러한 정보를 웹 앱 내에서 사용하려면 "로컬 웹 앱" 데이터베이스에 저장해야 한다고 생각합니다.

사용자가 로그에 기록된 후 데이터베이스에 저장하려면 어떻게 해야 합니까?

저는 다음과 같은 시나리오에 대해 생각하고 있습니다. "저는 사용자 B를 가리키는 A 객체를 가지고 있기 때문에 그들 사이에 관계를 두어야 합니다.그래서 저는 외국 키를 추가합니다.

그런 경우에는 DB에 사용자가 있어야 합니다. 아닌가요?

편집

DB에 모든 사용자를 저장하지 않기 위해 관리자 API를 사용하려고 합니다. 그래서 컨트롤러에 다음 코드를 추가했습니다.

나는 또한 다른 고객을 만들었습니다.Test모든 사용자를 가져오기 위해, 이 방법으로 사용할 수 있습니다.client-id그리고.client-secret.또는 사용할 수 있는 방법이 있습니까?JWT관리 API를 사용하시겠습니까?

고객:

     Keycloak keycloak2 = KeycloakBuilder.builder()
                         .serverUrl("http://localhost:8080/auth/admin/realms/MYREALM/users")
                         .realm("MYREALMM")
                         .username("u.user")
                         .password("password")
                         .clientId("Test")
                         .clientSecret("cade3034-6ee1-4b18-8627-2df9a315cf3d")
                         .resteasyClient(new ResteasyClientBuilder().connectionPoolSize(20).build())
                         .build();

 RealmRepresentation realm2 = keycloak2.realm("MYREALMM").toRepresentation();

오류:

2018-02-05 12:33:06.638 ERROR 16975 --- [nio-8080-exec-7] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler dispatch failed; nested exception is java.lang.Error: Unresolved compilation problem: 
    The method realm(String) is undefined for the type AccessTokenResponse
] with root cause

java.lang.Error: Unresolved compilation problem: 
    The method realm(String) is undefined for the type AccessTokenResponse

내가 어디서 잘못하고 있나요?

편집 2

저도 해봤어요.

@Autowired
private HttpServletRequest request;

public ResponseEntity listUsers() {
    KeycloakAuthenticationToken token = (KeycloakAuthenticationToken) request.getUserPrincipal();        
    KeycloakPrincipal principal=(KeycloakPrincipal)token.getPrincipal();
    KeycloakSecurityContext session = principal.getKeycloakSecurityContext();
        
    Keycloak keycloak = KeycloakBuilder.builder()
                                        .serverUrl("http://localhost:8080/auth")
                                        .realm("MYREALMM")
                                        .authorization(session.getToken().getAuthorization().toString())
                                        .resteasyClient(new ResteasyClientBuilder().connectionPoolSize(20).build())
                                        .build();
    
    RealmResource r = keycloak.realm("MYREALMM");
    List<org.keycloak.representations.idm.UserRepresentation> list = keycloak.realm("MYREALMM").users().list();
    return ResponseEntity.ok(list);

그러나 권한은 항상 있습니다.null 왜요?

EDIT 3 내 스프링 보안 구성을 확인할 수 있습니다.

    @Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
@ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
@KeycloakConfiguration
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
         super.configure(http);
     
        http.httpBasic().disable();
        http
        .csrf().disable()
        .authorizeRequests()
            .antMatchers("/webjars/**").permitAll()
            .antMatchers("/resources/**").permitAll()
            .anyRequest().authenticated()
        .and()
        .logout()
            .logoutUrl("/logout")
            .logoutRequestMatcher(new AntPathRequestMatcher("/logout", "GET"))
            .permitAll()
            .logoutSuccessUrl("/")
            .invalidateHttpSession(true);
    }

      @Autowired
        public KeycloakClientRequestFactory keycloakClientRequestFactory;

        @Bean
        public KeycloakRestTemplate keycloakRestTemplate() {
            return new KeycloakRestTemplate(keycloakClientRequestFactory);
        }
        
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) {
  
        KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
        SimpleAuthorityMapper simpleAuthorityMapper = new SimpleAuthorityMapper();
        simpleAuthorityMapper.setPrefix("ROLE_");
        simpleAuthorityMapper.setConvertToUpperCase(true);
        keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(simpleAuthorityMapper);
        auth.authenticationProvider(keycloakAuthenticationProvider);
    }
 
    @Bean
    public KeycloakSpringBootConfigResolver keycloakConfigResolver() {
        return new KeycloakSpringBootConfigResolver();
    }
 
    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    }
    
    @Override
    public void configure(WebSecurity web) throws Exception {
        web
           .ignoring()
           .antMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/images/**", "/webjars/**");
    }
    
     @Bean
     @Scope(scopeName = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
     public AccessToken accessToken() {
         HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
         return ((KeycloakSecurityContext) ((KeycloakAuthenticationToken) request.getUserPrincipal()).getCredentials()).getToken();
     }
     
}

EDIT 4

은 이들은내다속니 안에 입니다.applicatoin.properties

#######################################
#             KEYCLOAK                #
#######################################

keycloak.auth-server-url=http://localhost:8181/auth
keycloak.realm=My Realm 
keycloak.ssl-required=external
keycloak.resource=AuthServer
keycloak.credentials.jwt.client-key-password=keystorePwd
keycloak.credentials.jwt.client-keystore-file=keystore.jks
keycloak.credentials.jwt.client-keystore-password=keystorePwd
keycloak.credentials.jwt.alias=AuthServer
keycloak.credentials.jwt.token-expiration=10
keycloak.credentials.jwt.client-keystore-type=JKS
keycloak.use-resource-role-mappings=true
keycloak.confidential-port=0
keycloak.principal-attribute=preferred_username

편집 5.

다음은 내 키클로크 구성입니다.

사용자 보기 권한으로 로그인하는 데 사용하는 사용자:

EDIT 6

로깅을 활성화한 후의 로그 양식 keyclock:

2018-02-12 08:31:00.274 3DEBUG 5802 --- [nio-8080-exec-1] o.k.adapters.PreAuthActionsHandler       : adminRequest http://localhost:8080/utente/prova4
2018-02-12 08:31:00.274 3DEBUG 5802 --- [nio-8080-exec-1] .k.a.t.AbstractAuthenticatedActionsValve : AuthenticatedActionsValve.invoke /utente/prova4
2018-02-12 08:31:00.274 3DEBUG 5802 --- [nio-8080-exec-1] o.k.a.AuthenticatedActionsHandler        : AuthenticatedActionsValve.invoke http://localhost:8080/utente/prova4
2018-02-12 08:31:00.274 3DEBUG 5802 --- [nio-8080-exec-1] o.k.a.AuthenticatedActionsHandler        : Policy enforcement is disabled.
2018-02-12 08:31:00.275 3DEBUG 5802 --- [nio-8080-exec-1] o.k.adapters.PreAuthActionsHandler       : adminRequest http://localhost:8080/utente/prova4
2018-02-12 08:31:00.275 3DEBUG 5802 --- [nio-8080-exec-1] o.k.a.AuthenticatedActionsHandler        : AuthenticatedActionsValve.invoke http://localhost:8080/utente/prova4
2018-02-12 08:31:00.275 3DEBUG 5802 --- [nio-8080-exec-1] o.k.a.AuthenticatedActionsHandler        : Policy enforcement is disabled.
2018-02-12 08:31:00.276 3DEBUG 5802 --- [nio-8080-exec-1] o.k.adapters.PreAuthActionsHandler       : adminRequest http://localhost:8080/utente/prova4
2018-02-12 08:31:00.276 3DEBUG 5802 --- [nio-8080-exec-1] o.k.a.AuthenticatedActionsHandler        : AuthenticatedActionsValve.invoke http://localhost:8080/utente/prova4
2018-02-12 08:31:00.276 3DEBUG 5802 --- [nio-8080-exec-1] o.k.a.AuthenticatedActionsHandler        : Policy enforcement is disabled.
2018-02-12 08:31:10.580 3DEBUG 5802 --- [nio-8080-exec-1] o.k.a.s.client.KeycloakRestTemplate      : Created GET request for "http://localhost:8181/auth/admin/realms/My%20Realm%20name/users"
2018-02-12 08:31:10.580 3DEBUG 5802 --- [nio-8080-exec-1] o.k.a.s.client.KeycloakRestTemplate      : Setting request Accept header to [application/json, application/*+json]
2018-02-12 08:31:10.592 3DEBUG 5802 --- [nio-8080-exec-1] o.k.a.s.client.KeycloakRestTemplate      : GET request for "http://localhost:8181/auth/admin/realms/My%20Realm%20name/users" resulted in 401 (Unauthorized); invoking error handler
2018-02-12 08:31:10.595 ERROR 5802 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.client.HttpClientErrorException: 401 Unauthorized] with root cause

org.springframework.web.client.HttpClientErrorException: 401 Unauthorized
    at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:85) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE]
    at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:707) ~[spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE]

된 사용자가 "" "" "" "" "" "" "" 이상 포함되어 .view-usersrealm-management고객님, 제가 얼마 전에 쓴 이 답변을 보세요.사용자에게 이 역할이 지정되면 사용자가 검색하는 JWT에 이 역할이 포함됩니다.

당신의 논평에서 유추할 수 있듯이, 당신은 약간의 근거가 부족한 것 같습니다.Authorization할 필요 .사용자가 로그인하면 Keyclock에서 서명된 JWT를 가져와 Keyclock에 문의할 필요 없이 영역의 모든 클라이언트가 신뢰할 수 있습니다.되어 있으며, 에 JWT Authorization로, 에 " " " ", ", ", "가 붙습니다.Bearer키워드(https://auth0.com/blog/cookies-vs-tokens-definitive-guide/) 의 토큰 기반 인증 참조).

그래서 사용자가 사용자 목록을 보기 위해 앱에 요청할 때, 그녀의 액세스 토큰은 다음을 포함합니다.view-users역할이 이미 요청 헤더에 들어갑니다. 분석할 필요 합니다. (이 Keyclock 사용자 끝점을 .)KeycloakBuilder), a), Keyclock Spring 를 제공합니다.KeycloakRestTemplate클래스 - 현재 사용자에 대해 다른 서비스에 대한 요청을 수행할 수 있습니다.

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);
    }

    ...
}

는 템릿의범다같습니다과음위는입니다.PROTOTYPESpring은 각 요청에 대해 다른 인스턴스를 사용합니다.

그런 다음 이 템플릿을 자동 배선하고 요청하는 데 사용합니다.

@Service
public class UserRetrievalService{

    @Autowired
    private KeycloakRestTemplate keycloakRestTemplate;

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

}

은 당신만의 ▁your합다▁own를 실행해야 할 입니다.User키클록 서버에서 반환한 JSON 응답과 일치하는 클래스입니다.

사용자가 목록에 액세스할 수 없는 경우 Keyclock 서버에서 403 응답 코드가 반환됩니다.다음과 같은 주석을 사용하여 자신 앞에서 이를 거부할 수도 있습니다.@PreAuthorize("hasRole('VIEW_USERS')").

마지막으로, @dchrzascik의 대답은 잘 지적되었다고 생각합니다.요약하자면, 매번 키클록 서버에서 전체 사용자 목록을 검색하거나 앱 데이터베이스에 사용자를 저장하지 않는 다른 방법이 있습니다. 실제로 사용자를 캐시할 수 있으므로 앱에서 사용자 관리를 수행할 경우 캐시를 업데이트할 수 있습니다.


편집

는 Github에 업로드된 전체 사용자 목록을 얻는 방법을 보여주는 샘플 프로젝트를 구현했습니다.기밀 클라이언트용으로 구성되어 있습니다(공개 클라이언트를 사용하는 경우 application.properties에서 암호를 삭제해야 합니다).

참고 항목:

저는 당신이 정말로 자신의 사용자 스토어를 가지고 있어야 하는지 다시 한번 확인하는 것을 제안합니다.데이터 중복을 방지하고 이에 따른 문제를 방지하려면 Keyclock의 사용자 연합에서만 릴레이해야 합니다.그 중에서도 Keyclock은 사용자 관리를 담당하고 있으며, 당신은 Keyclock이 제 역할을 하도록 해야 합니다.

OIDC를 사용하기 때문에 다음 두 가지 이점이 있습니다.

  1. JWT 형태로 제공되는 ID 토큰에는 "sub" 필드가 있습니다.이 필드는 사용자를 고유하게 식별합니다.오픈에서ID 연결 규격:

    필수의.제목 식별자.최종 사용자를 위한 발급자 내에서 지역적으로 고유하고 재할당되지 않은 식별자(예: 24400320 또는 AItoOwmwtWwcT0k51BayNewNvutr)JUqsvl6qs7A4.ASCII 문자는 255자를 초과할 수 없습니다.하위 값은 대소문자를 구분하는 문자열입니다.

    키클로크에서 "sub"는 UUID일 뿐입니다.이 필드를 사용하여 "객체 A"를 "사용자 B"와 연관시킬 수 있습니다.DB에서 이것은 외부 키가 아닌 일반 열일 뿐입니다.

    Java에서는 보안 컨텍스트를 사용하여 이 JWT 데이터에 액세스할 수 있습니다.또한 키클록의 authz-springboot 퀵스타트에서 키클록 보안 컨텍스트에 액세스하는 방법을 볼 수 있습니다. 여기서 getSubject 메서드가 있는 IDToken을 얻을 수 있습니다.

  2. Keyclock은 사용자 리소스가 있는 Admin REST API를 제공합니다.OIDC에서 지원하는 API이므로 인증을 제대로 받아야 합니다.이 API를 사용하여 사용자 목록을 포함한 사용자에 대한 작업을 수행할 수 있습니다.이 API는 직접 사용하거나 Java SDK: keyclock admin client를 사용하여 사용할 수 있습니다.

    이 시나리오에서는 사용자가 요청한 JWT를 사용해야 합니다.JWT를 사용하면 요청하는 사용자가 해당 영역의 모든 사용자를 나열할 수 있습니다.예를 들어, 다음 코드를 고려하십시오.

    @GetMapping("/users")
    public List<UserRepresentation> 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<UserRepresentation> list = keycloak.realm("example").users().list();
    
       return list;
    }
    

    이 경우 HttpServletRequest와 포함된 토큰을 사용합니다.다음을 사용하여 동일한 데이터를 얻을 수 있습니다.org.springframework.security.core.AuthenticationSpring 보안에서 또는 Authorization 헤더를 직접 가져옵니다.중요한 것은 KeyclockBuilder가 문자열을 액세스가 아닌 '권한'으로 기대한다는 것입니다.토큰 - 이것이 오류가 발생하는 이유입니다.

    이 기능이 작동하려면 요청을 생성하는 사용자가 'real-management' 클라이언트의 'view-users' 역할을 가지고 있어야 합니다.사용자 또는 사용자가 속한 일부 그룹의 '역할 매핑' 탭에서 해당 역할을 사용자에게 할당할 수 있습니다.

    또한 보안 컨텍스트의 이점을 누리려면 제대로 인증을 받아야 합니다. 그렇지 않으면 null을 얻게 됩니다.스프링 보안 키클로크 구성 클래스의 예는 다음과 같습니다.

    @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();
    }
    }
    

언급URL : https://stackoverflow.com/questions/48583361/how-can-i-read-all-users-using-keycloak-and-spring

반응형