1. 개요

이 예제에서는 Spring Security OAuth2.0을 사용하여 Keycloak 서버를 설정하고 Spring Boot 애플리케이션을 연결하는 기본 사항에 대해 설명합니다.

2. 키클로크란?

Keycloak 은 최신 애플리케이션 및 서비스를 대상으로 하는 오픈 소스 ID 및 액세스 관리 솔루션입니다.

Keycloak은 SSO(Single-Sign-On), ID 브로커링 및 소셜 로그인, 사용자 연합, 클라이언트 어댑터, 관리 콘솔 및 계정 관리 콘솔과 같은 기능을 제공합니다.

이 사용방법(예제)에서는 Keycloak의 관리 콘솔을 사용하여 Spring Security OAuth2.0을 사용하여 Spring Boot를 설정하고 연결합니다.

3. Keycloak 서버 설정

이 섹션에서는 Keycloak 서버를 설정하고 구성합니다.

3.1. Keycloak 다운로드 및 설치

선택할 수 있는 여러 분포가 있습니다. 그러나 이 사용방법(예제)에서는 독립 실행형 버전을 사용합니다.

공식 소스에서 Keycloak-19.0.1 Standalone 서버 배포판 을 다운로드합시다 .

Standalone 서버 배포판을 다운로드했으면 압축을 풀고 터미널에서 Keycloak을 시작할 수 있습니다.

$ unzip keycloak-legacy-19.0.1.zip 
$ cd keycloak-legacy-19.0.1/keycloak-19.0.1/bin
$ ./standalone.sh -Djboss.socket.binding.port-offset=100

./standalone.sh 를 실행 하면 Keycloak이 서비스를 시작합니다. Keycloak 19.0.1 (WildFly Core 18.1.1.Final) started 가 포함된 줄이 표시되면  시작이 완료되었음을 알 수 있습니다.

이제 브라우저를 열고 http://localhost:8180 을 방문합시다 . 관리 로그인을 생성하기 위해 http://localhost:8180/auth 로 리디렉션됩니다 .

 

키클로크1

비밀번호 zaq1!QAZ 를 사용하여 initial1 이라는 초기 관리 사용자를 생성해 보겠습니다 . Create 를 클릭하면 User Created 메시지가 표시 됩니다.

이제 관리 콘솔로 진행할 수 있습니다. 로그인 페이지에서 초기 관리 사용자 자격 증명을 입력합니다.

keycloak 관리 콘솔

3.2. 영역 만들기

성공적으로 로그인하면 콘솔로 이동하여 기본 마스터 영역이 열립니다.

여기서는 사용자 지정 영역을 만드는 데 중점을 둘 것입니다.

영역 추가 버튼 을 찾기 위해 왼쪽 상단 모서리로 이동해 보겠습니다 .

keycloak 영역 추가

다음 화면에서 SpringBootKeycloak 라는 새 영역을 추가해 보겠습니다 .

keycloak 새로운 영역

만들기 버튼을 클릭하면 새 영역이 생성되고 해당 영역으로 리디렉션됩니다. 다음 섹션의 모든 작업은 이 새로운 SpringBootKeycloak 영역에서 수행됩니다.

3.3. 클라이언트 만들기

이제 클라이언트 페이지로 이동합니다. 아래 이미지에서 볼 수 있듯이 Keycloak은 이미 내장된 클라이언트와 함께 제공됩니다 .

열쇠고리 클라이언트

여전히 애플리케이션에 새 클라이언트를 추가해야 하므로 Create 를 클릭 합니다. 우리는 새로운 Client login-app 를 호출할 것입니다 :

keycloak 클라이언트 1 추가

다음 화면에서는 이 사용방법(예제)의 목적을 위해 유효한 리디렉션 URI 필드를 제외한 모든 기본값을 그대로 둡니다. 이 필드에는 인증을 위해 이 클라이언트를 사용할 애플리케이션 URL이 포함되어야 합니다 .

keycloak 유효한 리디렉션 URI

나중에 이 클라이언트를 사용할 포트 8081에서 실행되는 Spring Boot 애플리케이션을 생성할 것입니다. 따라서 위의 http://localhost:8081/ * 리디렉션 URL을 사용했습니다.

3.4. 역할 및 사용자 생성

Keycloak은 역할 기반 액세스를 사용합니다. 따라서 각 사용자에게는 역할이 있어야 합니다.

그렇게 하려면 역할 페이지 로 이동해야 합니다 .

열쇠고리 역할

그런 다음 사용자 역할을 추가합니다.

keycloak 역할 추가

이제 사용자에게 할당할 수 있는 역할이 있지만 아직 사용자가 없으므로 사용자 페이지로 이동하여 역할을 추가해 보겠습니다 .

키클로크 사용자

user1 이라는 사용자를 추가합니다 .

키클로크 애드유저

사용자가 생성되면 세부 정보가 포함된 페이지가 표시됩니다.

키클록 사용자

이제 자격 증명 탭으로 이동할 수 있습니다. 초기 암호를 xsw2@WSX 로 설정합니다 .

keycloak 자격 증명

마지막으로 역할 매핑 탭으로 이동합니다. user1 에게 사용자 역할을 할당할 것입니다 .

keycloak 사용자 역할

4. Keycloak의 API로 액세스 토큰 생성

Keycloak은 액세스 토큰을 생성하고 새로 고치기 위한 REST API를 제공합니다. 이 API를 사용하여 쉽게 자체 로그인 페이지를 만들 수 있습니다.

먼저 다음 URL에 POST 요청을 전송하여 Keycloak에서 액세스 토큰을 획득해야 합니다.

http://localhost:8180/auth/realms/SpringBootKeycloak/protocol/openid-connect/token

요청에는 x-www-form-urlencoded 형식 의 본문이 있어야 합니다.

client_id:<your_client_id>
username:<your_username>
password:<your_password>
grant_type:password

이에 대한 응답으로 access_tokenrefresh_token 을 받게 됩니다.

액세스 토큰은 단순히 Authorization 헤더 에 배치하여 Keycloak으로 보호되는 리소스에 대한 모든 요청에 ​​사용해야 합니다 .

headers: {
    'Authorization': 'Bearer' + access_token
}

액세스 토큰이 만료되면 위와 동일한 URL에 POST 요청을 보내서 새로 고칠 수 있지만 사용자 이름과 암호 대신 새로 고침 토큰을 포함합니다.

{
    'client_id': 'your_client_id',
    'refresh_token': refresh_token_from_previous_request,
    'grant_type': 'refresh_token'
}

Keycloak은 새로운 access_tokenrefresh_token으로 이에 응답합니다.

5. Spring Boot 애플리케이션 생성 및 구성

이 섹션에서는 Spring Boot 애플리케이션을 생성하고 Keycloak 서버와 상호 작용하도록 OAuth 클라이언트로 구성합니다.

5.1. 의존성

Spring Security OAuth2.0 클라이언트를 사용하여 Keycloak 서버에 연결합니다 .

pom.xml 의 Spring Boot 애플리케이션에서 spring-boot-starter-oauth2-client  의존성 을 선언하여 시작하겠습니다 .

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>

또한 Spring Boot와 함께 Spring Security를 ​​사용해야 하므로 다음 의존성 을 추가해야 합니다 .

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

이제 Spring Boot 애플리케이션은 Keycloak과 상호 작용할 수 있습니다.

5.2. 키클로크 구성

우리는 Keycloak 클라이언트를 OAuth 클라이언트로 간주합니다. 따라서 OAuth 클라이언트를 사용하도록 Spring Boot 애플리케이션을 구성해야 합니다 .

ClientRegistration 클래스 는 클라이언트에 대한 모든 기본 정보를 보유합니다. Spring 자동 구성은 spring.security.oauth2.client.registration.[registrationId] 스키마가 있는 속성을 찾고 OAuth 2.0 또는 OpenID Connect(OIDC) 로 클라이언트를 등록합니다 .

클라이언트 등록 구성을 구성해 보겠습니다.

spring.security.oauth2.client.registration.keycloak.client-id=login-app
spring.security.oauth2.client.registration.keycloak.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.keycloak.scope=openid

client-id  에 지정한 값 은 관리 콘솔에서 지정한 클라이언트와 일치합니다.

Spring Boot 애플리케이션은 OAuth 2.0 또는 OIDC 공급자와 상호 작용하여 다양한 권한 부여 유형에 대한 실제 요청 논리를 처리해야 합니다. 따라서 OIDC 공급자를 구성해야 합니다. spring.security.oauth2.client.provider.[provider name] 스키마를 사용하여 속성 값을 기반으로 자동 구성할 수 있습니다 .

OIDC 공급자 구성을 구성해 보겠습니다.

spring.security.oauth2.client.provider.keycloak.issuer-uri=http://localhost:8180/auth/realms/SpringBootKeycloak
spring.security.oauth2.client.provider.keycloak.user-name-attribute=preferred_username

우리가 기억할 수 있듯이 포트 8180 에서 Keycloak을 시작했기 때문에 issuer-uri 에 경로가 지정되었습니다 . 이 속성은 권한 부여 서버의 기본 URI를 식별합니다. Keycloak 관리 콘솔에서 생성한 영역 이름을 입력합니다. 또한 user-name-attribute  를 preferred_username 으로 정의 하여 컨트롤러의 Principal 을 적절한 사용자로 채울 수 있습니다.

5.3. 구성 클래스

SecurityFilterChain 빈 을 생성하여 HttpSecurity 를 ​​구성합니다 . 또한 http.oauth2Login() 을 사용하여 OAuth2 로그인을 활성화해야 합니다 .

Security 구성을 생성해 보겠습니다.

@Configuration
@EnableWebSecurity
class SecurityConfig {

    private final KeycloakLogoutHandler keycloakLogoutHandler;

    SecurityConfig(KeycloakLogoutHandler keycloakLogoutHandler) {
        this.keycloakLogoutHandler = keycloakLogoutHandler;
    }

    @Bean
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/customers*", "/users*")
            .hasRole("USER")
            .anyRequest()
            .permitAll();
        http.oauth2Login()
            .and()
            .logout()
            .addLogoutHandler(keycloakLogoutHandler)
            .logoutSuccessUrl("/");
        return http.build();
    }
}

위의 코드 에서 oauth2Login() 메서드는 OAuth2LoginAuthenticationFilter 를 필터 체인에 추가합니다. 이 필터는 요청을 가로채고 OAuth 2 인증에 필요한 논리를 적용합니다 .

configure() 메서드 에서 권한 및 역할을 기반으로 액세스를 구성합니다 . 이러한 제약 조건은 /customers/* 에 대한 모든 요청이 USER 역할을 가진 인증된 사용자인 경우에만 승인되도록 합니다 .

마지막으로 Keycloak에서 로그아웃을 처리해야 합니다. 이를 위해 KeycloakLogoutHandler 클래스를 추가합니다.

@Component
public class KeycloakLogoutHandler implements LogoutHandler {

    private static final Logger logger = LoggerFactory.getLogger(KeycloakLogoutHandler.class);
    private final RestTemplate restTemplate;

    public KeycloakLogoutHandler(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @Override
    public void logout(HttpServletRequest request, HttpServletResponse response, 
      Authentication auth) {
        logoutFromKeycloak((OidcUser) auth.getPrincipal());
    }

    private void logoutFromKeycloak(OidcUser user) {
        String endSessionEndpoint = user.getIssuer() + "/protocol/openid-connect/logout";
        UriComponentsBuilder builder = UriComponentsBuilder
          .fromUriString(endSessionEndpoint)
          .queryParam("id_token_hint", user.getIdToken().getTokenValue());

        ResponseEntity<String> logoutResponse = restTemplate.getForEntity(
        builder.toUriString(), String.class);
        if (logoutResponse.getStatusCode().is2xxSuccessful()) {
            logger.info("Successfulley logged out from Keycloak");
        } else {
            logger.error("Could not propagate logout to Keycloak");
        }
    }

}

KeycloakLogoutHandler 클래스 는 LogoutHandler 클래스를 구현하고 Keycloak 에 로그아웃 요청을 보냅니다.

이제 인증 후 내부 고객 페이지에 액세스할 수 있습니다.

5.4. Thymeleaf 웹 페이지

우리는 웹 페이지에 Thymeleaf를 사용하고 있습니다.

세 페이지가 있습니다.

  • external.html – 대중을 위한 외부 웹 페이지
  • customers.html – user 역할을 가진 인증된 사용자에게만 액세스가 제한되는 내부용 페이지
  • layout.html – 외부 페이지와 내부 페이지 모두에 사용되는 두 개의 조각으로 구성된 간단한 레이아웃

Thymeleaf 템플릿에 대한 코드는 Github에서 사용할 수 있습니다 .

5.5. 제어 장치

웹 컨트롤러는 내부 및 외부 URL을 적절한 Thymeleaf 템플릿에 매핑합니다.

@GetMapping(path = "/")
public String index() {
    return "external";
}
    
@GetMapping(path = "/customers")
public String customers(Principal principal, Model model) {
    addCustomers();
    model.addAttribute("customers", customerDAO.findAll());
    model.addAttribute("username", principal.getName());
    return "customers";
}

/customers 경로 의 경우 리포지토리에서 모든 고객을 검색하고 그 결과를 Model 에 속성으로 추가합니다 . 나중에 Thymeleaf의 결과를 반복합니다.

사용자 이름을 표시할 수 있도록 Principal 도 주입합니다.

우리는 여기서 고객을 표시할 원시 데이터로만 사용하고 있으며 그 이상은 사용하지 않는다는 점에 유의해야 합니다.

6. 시연

이제 애플리케이션을 테스트할 준비가 되었습니다. Spring Boot 애플리케이션을 실행하려면 STS(Spring Tool Suite)와 같은 IDE를 통해 쉽게 시작하거나 터미널에서 다음 명령을 실행할 수 있습니다.

mvn clean spring-boot:run

http://localhost:8081 을 방문 하면 다음이 표시됩니다.

externalFacingKeycloak페이지 e1551248465138

이제 민감한 정보의 위치인 인트라넷에 들어가기 위해 고객 을 클릭합니다.

이 콘텐츠를 볼 수 있는 권한이 있는지 확인하기 위해 Keycloak을 통해 인증하도록 리디렉션되었습니다.

keycloak 사용자 로그인

user1 로 로그인하면 Keycloak은 사용자 역할 이 있는지 인증을 확인하고 제한된 고객 페이지 로 리디렉션됩니다 .

고객 페이지

이제 Spring Boot와 Keycloak을 연결하고 작동 방식을 보여주는 설정을 마쳤습니다.

보시다시피 Spring Boot 는 Keycloak Authorization Server를 호출하는 전체 프로세스를 원활하게 처리했습니다 . 액세스 토큰을 직접 생성하기 위해 Keycloak API를 호출하거나 보호된 리소스에 대한 요청에서 Authorization 헤더를 명시적으로 보낼 필요가 없었습니다.

다음으로 기존 애플리케이션과 함께 Spring Security를 ​​사용하는 방법을 검토할 것입니다.

7. 결론

이 기사에서는 Keycloak 서버를 구성하고 이를 Spring Boot 애플리케이션과 함께 사용했습니다.

또한 Spring Security를 ​​설정하고 Keycloak과 함께 사용하는 방법도 배웠습니다. 이 문서에 표시된 코드의 작업 버전은 Github에서 사용할 수 있습니다 .

Security footer banner