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 로 리디렉션됩니다 .
비밀번호 zaq1!QAZ 를 사용하여 initial1 이라는 초기 관리 사용자를 생성해 보겠습니다 . Create 를 클릭하면 User Created 메시지가 표시 됩니다.
이제 관리 콘솔로 진행할 수 있습니다. 로그인 페이지에서 초기 관리 사용자 자격 증명을 입력합니다.
3.2. 영역 만들기
성공적으로 로그인하면 콘솔로 이동하여 기본 마스터 영역이 열립니다.
여기서는 사용자 지정 영역을 만드는 데 중점을 둘 것입니다.
영역 추가 버튼 을 찾기 위해 왼쪽 상단 모서리로 이동해 보겠습니다 .
다음 화면에서 SpringBootKeycloak 라는 새 영역을 추가해 보겠습니다 .
만들기 버튼을 클릭하면 새 영역이 생성되고 해당 영역으로 리디렉션됩니다. 다음 섹션의 모든 작업은 이 새로운 SpringBootKeycloak 영역에서 수행됩니다.
3.3. 클라이언트 만들기
이제 클라이언트 페이지로 이동합니다. 아래 이미지에서 볼 수 있듯이 Keycloak은 이미 내장된 클라이언트와 함께 제공됩니다 .
여전히 애플리케이션에 새 클라이언트를 추가해야 하므로 Create 를 클릭 합니다. 우리는 새로운 Client login-app 를 호출할 것입니다 :
다음 화면에서는 이 사용방법(예제)의 목적을 위해 유효한 리디렉션 URI 필드를 제외한 모든 기본값을 그대로 둡니다. 이 필드에는 인증을 위해 이 클라이언트를 사용할 애플리케이션 URL이 포함되어야 합니다 .
나중에 이 클라이언트를 사용할 포트 8081에서 실행되는 Spring Boot 애플리케이션을 생성할 것입니다. 따라서 위의 http://localhost:8081/ * 리디렉션 URL을 사용했습니다.
3.4. 역할 및 사용자 생성
Keycloak은 역할 기반 액세스를 사용합니다. 따라서 각 사용자에게는 역할이 있어야 합니다.
그렇게 하려면 역할 페이지 로 이동해야 합니다 .
그런 다음 사용자 역할을 추가합니다.
이제 사용자에게 할당할 수 있는 역할이 있지만 아직 사용자가 없으므로 사용자 페이지로 이동하여 역할을 추가해 보겠습니다 .
user1 이라는 사용자를 추가합니다 .
사용자가 생성되면 세부 정보가 포함된 페이지가 표시됩니다.
이제 자격 증명 탭으로 이동할 수 있습니다. 초기 암호를 xsw2@WSX 로 설정합니다 .
마지막으로 역할 매핑 탭으로 이동합니다. user1 에게 사용자 역할을 할당할 것입니다 .
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_token 및 refresh_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_token 및 refresh_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 을 방문 하면 다음이 표시됩니다.
이제 민감한 정보의 위치인 인트라넷에 들어가기 위해 고객 을 클릭합니다.
이 콘텐츠를 볼 수 있는 권한이 있는지 확인하기 위해 Keycloak을 통해 인증하도록 리디렉션되었습니다.
user1 로 로그인하면 Keycloak은 사용자 역할 이 있는지 인증을 확인하고 제한된 고객 페이지 로 리디렉션됩니다 .
이제 Spring Boot와 Keycloak을 연결하고 작동 방식을 보여주는 설정을 마쳤습니다.
보시다시피 Spring Boot 는 Keycloak Authorization Server를 호출하는 전체 프로세스를 원활하게 처리했습니다 . 액세스 토큰을 직접 생성하기 위해 Keycloak API를 호출하거나 보호된 리소스에 대한 요청에서 Authorization 헤더를 명시적으로 보낼 필요가 없었습니다.
다음으로 기존 애플리케이션과 함께 Spring Security를 사용하는 방법을 검토할 것입니다.
7. 결론
이 기사에서는 Keycloak 서버를 구성하고 이를 Spring Boot 애플리케이션과 함께 사용했습니다.
또한 Spring Security를 설정하고 Keycloak과 함께 사용하는 방법도 배웠습니다. 이 문서에 표시된 코드의 작업 버전은 Github에서 사용할 수 있습니다 .