Spring Boot + Keycloak server

2022. 10. 24. 23:51CheatSheet

기존 레퍼런스들은 WildFly에 의해 동작하는 Keycloak Server를 기준으로 동작한다. 그러나, 19.0.3 버전 이래로, WildFly는 keycloak server를 지원하지 않아 DEPRECATE 처리되었다.
그래서, Quarkus가 제공하는 keycloak server를 사용 해야하는데, GUI 구성부터, 애플리케이션 설정 및 빌드과정 까지 달라진 부분이 많아서, 해당 내용들을 종합하고 정리하여, 다음 작업때는 덜 헤메고자 한다.

Requirements

  • jdk 11+ (17 recommended)

Index

  1. keycloak server 다운로드
  2. keycloak bin configuration
  3. keycloak realms 생성
  4. keycloak client 생성
  5. keycloak client roles 생성
  6. keycloak user 생성 (admin과 user)
  7. Keycloak curl 테스트
  8. spring 구성하기

1. keycloak server 다운로드

keycloak 라이브러리는 3가지 형태(Server, docker image, operator)로 배포를 하는데, 이번 게시글에서는 Keycloak server를 다운로드하여, 직접 설정하는 방식을 채택하겠다.

    1. https://www.keycloak.org/downloads.html 를 방문하여, Server -> keycloak (powered by Quarks)을 다운 받는다.
    2. 압축을 풀고, 적절한 디렉토리에 배치한 후, 다음과 같은 명령어를 실행한다.
      # 유닉스 계열
      $ cd ${설치경로}/keycloak-19.0.3/bin
      $ kc.sh start-dev
      
      # windows
      > ${설치경로}\keycloak-19.0.3\bin
      > kc.bat start-dev
    3.  

키클록을 프로덕션 환경에서 실행하려면, 별도의 설정을 추가적으로 해야하는데, 내용이 너무 많아지므로 해당 게시글에서는 시도하진 않겠다.

  1.  
  2. http://localhost:${port} 로 들어가면, 키클록 관리자 계정 로그인 화면이 실행될 것이다.
  3. Administration Console에 username 입력 -> password 입력 -> password confirmation 입력 -> create 클릭을 하여, 관리자 계정을 생성한다.

2. Keycloak configuration

키클록 서버의 http 기본 포트번호는 8080이다. 대부분 애플리케이션 서버를 8080으로 하는 경우가 많기 때문에, 키클록 서버의 포트번호를 바꾸고 싶을 수 있다.

여러가지 방법이 있겠지만, 가장 보편적인 방법은 conf 파일에 프로퍼티를 전달하는 것이다.

https://www.keycloak.org/server/all-config 를 참고하면, http 포트 번호를 바꾸는 설정값을 제시해준다.

${설치경로}/conf/keycloak.conf에 들어가서 다음과 같은 설정값을 추가한다.

http-port=8180

이 게시글에서는 8180을 기준으로 진행하도록 하겠다.

keycloak.conf 는 http 포트번호 말고도, 개발, 프로덕션 환경에 필요한 다양한 설정값들을 전달할 수 있으므로, 틈틈이 친해지도록 하자.

3. Keycloak realms 생성

  1. 종전에 생성한 관리자 계정으로 로그인하고 왼쪽 상단에 Master 를 클릭하여, 바로 하단에 Create Realm 버튼을 클릭한다.
  2. create_realm01
  3. Realm name 항목에 SpringBootKeycloak(취향에 따라 다르게 써도 무방하다) 이라고 입력하고 Create 버튼을 누른다.
  4. create_realm02
  5. (Optional) 개발 편의를 위해 Realm settings -> Tokens 탭으로 이동하여, Access Token Lifespan을 1시간으로 변경한다.
  6. create_realm03

4. Keycloak client 생성

  1. Clients 메뉴 -> Create client를 클릭하여, Client ID 항목에 my_client라고 입력하여 next 버튼을 클릭한다.
    01_create_client02
  2. 01_create_client01
  3. Client details 페이지로 이동할텐데, Settings 탭으로 이동하여, 다음과 같이 입력 값들을 맞춰주고 save를 클릭한다.
  4. 01_create_client03
  5. Clients 메뉴 -> Roles 메뉴로 가서 다음과 같이 role 2개를 생성한다.
  6. 01_create_client04
    참고로, Realm roles는 관리자가 생성한 realm에 한해, 클라이언트의 종류에 상관없이 공유하는 role을 만들 수 있다.

5. keycloak user 생성

  1. Users 메뉴에 들어가서 Add user 버튼을 클릭해서 username 을 채운다음, create 버튼을 누른다.
  2. 02_create_user01
  3. 생성된 Username을 클릭하면, User details 페이지로 이동하는데, Credential 탭에서 비밀번호를 설정하고, Role mapping 에 전 단계에서 만든 client에 속하는 role 2개를 추가한다.
    02_create_user03
  4. 02_create_user02

7. Keycloak curl 테스트

6번까지의 작업이 잘되었는지 확인하는 방법은 다양하다. 그 중에서 비교적 간편한 방법은 curl을 통해 인증을 시도해보는 것이다.

    1. 테스트를 하려면, 인증과 관련된 api endpoint를 알아야 한다. Endpoints는 Realm settings 메뉴에 들어가서 Endpoints 항목을 참고하면 된다.

앞서 생성한 user는 OpenID 프로토콜을 통해 인증을 시도하므로, OpenID Endpoint Configuration을 클릭하면 된다.

    1.  
    2. 03_test_curl01
    3. 클릭하면, endpoint에 활용되는 값들을 조회할 수 있는데, 보기좋게 정리하자면 다음과 같다.
    4. { "issuer": "http://localhost:8180/realms/SpringBootKeycloak", "authorization_endpoint": "http://localhost:8180/realms/SpringBootKeycloak/protocol/openid-connect/auth", "token_endpoint": "http://localhost:8180/realms/SpringBootKeycloak/protocol/openid-connect/token", "introspection_endpoint": "http://localhost:8180/realms/SpringBootKeycloak/protocol/openid-connect/token/introspect", "userinfo_endpoint": "http://localhost:8180/realms/SpringBootKeycloak/protocol/openid-connect/userinfo", "end_session_endpoint": "http://localhost:8180/realms/SpringBootKeycloak/protocol/openid-connect/logout", "frontchannel_logout_session_supported": true, "frontchannel_logout_supported": true, "jwks_uri": "http://localhost:8180/realms/SpringBootKeycloak/protocol/openid-connect/certs", "check_session_iframe": "http://localhost:8180/realms/SpringBootKeycloak/protocol/openid-connect/login-status-iframe.html", ... "mtls_endpoint_aliases": { "token_endpoint": "http://localhost:8180/realms/SpringBootKeycloak/protocol/openid-connect/token", "revocation_endpoint": "http://localhost:8180/realms/SpringBootKeycloak/protocol/openid-connect/revoke", "introspection_endpoint": "http://localhost:8180/realms/SpringBootKeycloak/protocol/openid-connect/token/introspect", "device_authorization_endpoint": "http://localhost:8180/realms/SpringBootKeycloak/protocol/openid-connect/auth/device", "registration_endpoint": "http://localhost:8180/realms/SpringBootKeycloak/clients-registrations/openid-connect", "userinfo_endpoint": "http://localhost:8180/realms/SpringBootKeycloak/protocol/openid-connect/userinfo", "pushed_authorization_request_endpoint": "http://localhost:8180/realms/SpringBootKeycloak/protocol/openid-connect/ext/par/request", "backchannel_authentication_endpoint": "http://localhost:8180/realms/SpringBootKeycloak/protocol/openid-connect/ext/ciba/auth" } }
    5. 이 중에서 테스트 용으로는 "token_endpoint"에 해당하는 http://localhost:8180/realms/SpringBootKeycloak/protocol/openid-connect/token을 활용하겠다. (이 부분은 postman을 활용하면 더 간단하다.)

참고로 client_secret은 Clients -> ${your_client_id} 클릭 -> credential 탭에서 확인할 수 있다.

    1.  

03_test_curl02

    1.  

03_test_curl03

  1.  
  2. curl --location --request POST 'http://localhost:8180/realms/SpringBootKeycloak/protocol/openid-connect/token' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'client_id=${your_client_id}' \ --data-urlencode 'username=${your_username}' \ --data-urlencode 'password=${your_password}' \ --data-urlencode 'grant_type=password' \ --data-urlencode 'client_secret=${your_client_secret}'

8. Spring구성하기

만약에 7번에서 테스트를 완료했다면, keycloak에 적용할 간단한 애플리케이션을 구현해보자. 예제로 구현할 프로젝트 구조는 다음과 같다.

  • SpringBoot
  • Maven
  • JDK 11+ (17 recommended)

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>me.spring</groupId>
    <artifactId>keycloak</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>keycloak_demo</name>
    <description>keycloak_demo</description>
    <properties>
        <java.version>17</java.version>
        <keycloak-adapter-bom.version>19.0.3</keycloak-adapter-bom.version>
    </properties>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.keycloak.bom</groupId>
                <artifactId>keycloak-adapter-bom</artifactId>
                <version>${keycloak-adapter-bom.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

application.properties

#Keycloak Configuration
keycloak.enabled=true
keycloak.auth-server-url=http://localhost:8180
keycloak.realm=SpringBootKeycloak
keycloak.resource=my_client
keycloak.public-client=false
keycloak.principal-attribute=preferred_username
keycloak.credentials.secret=${your_client_secrete}
keycloak.use-resource-role-mappings=true

#Oauth2 Configuration
spring.security.oauth2.client.registration.keycloak.client-id=my_client
spring.security.oauth2.client.registration.keycloak.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.keycloak.client-secret=${your_client_secrete}
spring.security.oauth2.client.registration.keycloak.scope=openid
spring.security.oauth2.client.provider.keycloak.issuer-uri=http://localhost:8180/realms/SpringBootKeycloak
spring.security.oauth2.client.provider.keycloak.user-name-attribute=preferred_username

해당 게시글은 스프링 애플리케이션 코드를 학습하는 페이지는 아니기에 소스코드는 아래 주소로 대체하겠다.

https://github.com/wanniDev/keycloakDemo

출처