- Red Hat에서 개발한 오픈소스 Identity and Access Management(IAM) 솔루션입니다. 현대적인 애플리케이션과 서비스를 위한 인증 및 권한 부여 기능을 제공하는 인증 서버(Authentication Server)의 기능을 수행합니다.
- Keycloack에서는 여러 플랫폼에서 중앙 집중식 인증 서버로 동작을 합니다. 주요한 기능은 서로 다른 도메인에서 실행되는 애플리케이션 간의 SSO를 지원하거나 REST API 기반에 접근제어 토큰에 대한 인증 제공 및 세션 타임아웃, 동시 로그인 제한과 같은 다양한 세션 기능을 담당합니다.
- OAuth 2.0 프로토콜을 확장하여 만든 사용자 인증을 위한 표준화된 인증(Authentication) 프로토콜입니다. - 기존 OAuth 2.0은 인가(Authorization)에 중점을 두고 있어 사용자 인증에 대한 표준이 부족했습니다. 이러한 한계를 해결하기 위해 OIDC에서는 OAuth 2.0 위에 '표준화된 인증 계층'을 추가한 프로토콜입니다.
1. OIDC 인증 흐름(OpenID Connect Authentication flow)
💡 OIDC 인증 흐름(OpenID Connect Authentication flow)
- OIDC(OpenID Connect) 인증 프로토콜을 이용하여 구현하는 구체적인 인증 방법들을 의미합니다. 즉, 애플리케이션(Client)을 기준으로 OIDC 프로토콜을 통해서 Keycloak과 통신하고 인증하는 방법들을 의미합니다.
- 이러한 OIDC 인증 흐름은 한 가지 방법으로 인증 과정을 수행할 수 있고, 여러 가지 다양한 방법으로 인증과정을 구현할 수 있습니다.
2. 인증 흐름(Authentication flow) 종류
인증 흐름 종류
설명
사용처
Standard Flow
- OAuth 2.0의 Authorization Code Flow 기반으로 인증을 수행하며, 사용자는 Keycloak 로그인 페이지로 리다이렉트 되어서 인증을 수행하는 인증방식을 의미합니다.
웹 애플리케이션
Direct Access Grants
- 직접 REST API를 통해 자격 증명(아이디/비밀번호)을 담아서 통신하며 별도의 리다이렉션 없이 즉시 토큰을 받을 수 있는 인증방식을 의미합니다.
신뢰할 수 있는 애플리케이션(모바일 애플리케이션, 백엔드 시스템에서 직접 인증이 필요한 경우)
Implicit Flow
- 간소화된 인증흐름으로 인가 코드(Auth Code) 없이 직접 액세스 토큰을 받아서 인증을 수행하는 방식을 의미합니다.
모바일 앱, 단일 페이지 애플리케이션(SPA)
Service Accounts Roles
- 애플리케이션 시스템 간에 인증 기반의 통신을 위한 방법으로, 클라이언트 자체적으로 API를 호출하여 다른 서비스와의 통신을 할 때 사용하는 인증방식입니다.
마이크로서비스 간 통신(서버-서버간 통신), 자동화된 프로세스
OAuth 2.0 Device Authorization Grant
- 스마트 TV나 IoT 기기와 같이 제한된 입력 기능을 가진 디바이스를 위한 인증 방식입니다.
스마트 TV 애플리케이션, IoT 디바이스, 게임 콘솔, 프린터 및 스캐너
OIDC CIBA Grant
- 클라이언트가 사용자의 직접적인 상호작용 없이 인증을 시작하는 인증 방식입니다.- 사용자가 인증을 요청한 디바이스와 실제 인증을 수행하는 디바이스가 물리적으로 분리된 방식입니다.
금융 서비스, 공유 디바이스, 스마트홈 시스템
3) 사전 작업
💡 사전 작업
- Keycloak의 구현 방식을 구현하고자 할때, 기본적으로 Keyclaok 인증 서버와 Realm, Client, User에 대한 구성을 진행합니다.
1. Keycloak 인증 서버 구축
💡 Keycloak 인증 서버 구축
- Spring Boot App 내에서 접근이 가능한 Keycloak 인증 서버 구축은 Docker를 기반으로 컨테이너로 구성하였습니다. - 아래의 글을 참고하시면 이를 확인할 수 있습니다.
- OAuth 2.0의 Authorization Code Flow를 기반으로 하는 가장 일반적이고 안전한 인증 방식을 의미하며, 주로 웹 애플리케이션에서 주로 사용되는 방식입니다. - 해당 방식은 OAuth 2.0을 기반으로 하여 동작 방식은 비슷하지만, OIDC(OpenID Connect) 프로토콜이 추가되어서 인증(Authentication) 기능이 강화되었습니다.
1. 처리 과정
💡 처리 과정 1. 로그인 (Login) - 사용자가 클라이언트 애플리케이션에서 로그인을 시도하는 단계입니다. - 예를 들어서, 사용자는 로그인 페이지를 클릭하는 단계입니다.
2. 인증 요청 생성(Generate authentication request) - 클라이언트가 client_id, redirect_uri, scope 등의 필요한 파라미터를 포함한 인증 요청을 생성합니다.
3. Keycloak으로 리다이렉트(Redirect to Keycloak) - 클라이언트가 사용자를 Keycloak의 인증 엔드포인트로 리다이렉트 시킵니다.
4. Keycloak으로 인증 요청 전송 (Send authentication request to Keycloak) - 생성된 인증 요청이 Keycloak 서버로 전송됩니다. - 해당 단계에서는 실제 Keycloak에서 구성한 로그인 페이지가 사용자에게 표시되는 단계입니다.
5. 사용자 인증(Authenticate user) - 사용자가 Keycloak의 로그인 페이지에서 자신의 자격 증명을 입력하여 인증을 수행합니다. - 해당 단계에서 사용자는 keycloak에서 구성한 로그인 페이지로 자격 증명(아이디/패스워드)을 입력하여 로그인을 수행하는 단계입니다.
6. 인가 코드 반환(Return authorization code) - 인증이 성공적으로 완료되면, Keycloak은 인가 코드를 생성하여 클라이언트의 리다이렉트 URI로 전송합니다. - 해당 단계에서는 로그인이 성공하고, 인가코드(AuthCode)를 발급받습니다.
7. Keycloak으로 토큰 요청 전송(Send token request to Keycloak)
- 클라이언트는 받은 인가 코드를 사용하여 Keycloak의 토큰 엔드포인트에 액세스 토큰을 요청합니다. - 해당 단계에서는 이전 단계에서 발급받은 인가코드(AuthCode)를 기반으로 Access Token을 요청하며, 최종적으로 사용자에게 반환이 됩니다.
- Realm 선택 > Clients 탭 > Client 선택 > Settings 탭 > Access settings 메뉴 중에서 Valid redirect URL로 리다이렉트 될 URL을 지정합니다. - 해당 설정은 사용자에게 Keycloak 로그인 페이지를 제공하고, 로그인에 성공하였을 때 지정한 Valid redirect URIs를 통해 리턴되어서 결과 값을 반환받는 형태로 사용이 됩니다.
3. HTTP 호출 : 인가 코드(Authentication Code) 발급
💡 HTTP 호출 : 인가 코드(Authentication Code) 발급
- 형식에 맞는 URL을 접근하여서 Keycloak 로그인을 수행하고 redirect_url을 통해서 인증 코드(Authentication Code)를 반환하는 방법을 확인해 봅니다.
- Spring Boot Cloud OpenFeign 방식을 통해서 Keycloak 서버가 통신을 하여서 값을 반환해 옵니다.
- http://localhost:9001/realms/dev-realm//protocol/openid-connect/auth 엔드포인트로 “application/x-www-form-urlencoded" 데이터 형태로 전달합니다. - 해당 서비스 호출을 최종적으로 HTML 페이지가 출력이 됩니다.
package com.blog.springbootkeycloak.service;
import com.blog.springbootkeycloak.dto.DirectAccessTokenDto;
import com.blog.springbootkeycloak.dto.StandardFlowDto;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
/**
* Keycloak 서버와 통신하여서 데이터를 수신합니다.
*
* @author : jonghoon
* @fileName : AuthFlowService
* @since : 25. 1. 28.
*/@FeignClient(
name = "keycloak-auth-service",
url = "<http://localhost:9001/realms/dev-realm>"
)publicinterfaceAuthFlowService{
/**
* Standard Flow : 로그인 페이지 출력 이후 로그인 성공 시 리다이렉트 경로로 데이터 리턴
*
* @param standardFlowDto 전송 객체 값 (form 데이터 형태로 전송)
* @return 토큰 값 반환
*/@GetMapping(
value = "/protocol/openid-connect/auth",
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE
)String getStandardFlowLoginView(@ModelAttribute StandardFlowDto standardFlowDto);
}
4. KeycloakController
💡 KeycloakController
- 위에 엔드포인트를 통해서 로그인 페이지가 출력하고, requestParam 내에 redirect_uri을 입력한 부분이 아래의 엔드포인트입니다. - 이는 로그인을 성공하였을 경우 리다이렉트 되어서 값을 전달받은 엔드포인트를 구성합니다. - 최종적으로, 로그인이 성공되면 아래와 같이 인증 코드(Authentication Code)를 발급 받습니다.추후 해당 값을 기반으로 접근 토큰(Access Toekn)을 클라이언트에게 반환해줄 수 있습니다.
- 추가적으로 토큰 발급을 위한 Keyclaok 엔드포인트 /protocol/openid-connect/token에 대해 요청을 하여서 결괏값을 반환받습니다.
package com.blog.springbootkeycloak.service;
import com.blog.springbootkeycloak.dto.TokenRequestDto;
import com.blog.springbootkeycloak.dto.StandardFlowDto;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
/**
* Keycloak 서버와 통신하여서 데이터를 수신합니다.
*
* @author : jonghoon
* @fileName : AuthFlowService
* @since : 25. 1. 28.
*/@FeignClient(
name = "keycloak-auth-service",
url = "<http://localhost:9001/realms/dev-realm>"
)publicinterfaceAuthFlowService{
/**
* Direct Access Flow : 토큰을 즉시 요청하는 방법
*
* @param tokenRequestDto 전송 객체 값 (form 데이터 형태로 전송)
* @return 토큰 값 반환
*/@PostMapping(
value = "/protocol/openid-connect/token",
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE
)Object getAccessToken(@ModelAttribute TokenRequestDto tokenRequestDto);
/**
* Standard Flow : 로그인 페이지 출력 이후 로그인 성공 시 리다이렉트 경로로 데이터 리턴
*
* @param standardFlowDto 전송 객체 값 (form 데이터 형태로 전송)
* @return 토큰 값 반환
*/@GetMapping(
value = "/protocol/openid-connect/auth",
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE
)String getStandardFlowLoginView(@ModelAttribute StandardFlowDto standardFlowDto);
}
4. 결과 확인 : 브라우저 로그인 출력
💡 결과 확인 : 브라우저 로그인 출력
- 이전 수행과정을 다시 수행하여서 로그인 > Auth Code > Access Token 발급과정에 대해 알아봅니다.
5. 결과 확인 : 로그인 성공 이후 접근 토큰(Access Token) 발급
💡 결과 확인 : 로그인 성공 이후 접근 토큰(Access Token) 발급
- 로그인을 성공하게 되면 인증 토큰(auth token)을 기반으로 서버 내에서 keycloak과 통신하여서 accessToken을 발급해 줍니다. - Direct Access Grants에서는 직접 자격증명을 수행하지만, Standard flow에서는 로그인 수행 → auth token 발급 → access token을 발급해 주는 과정을 통해 보안이 강화가 됩니다.