🔐 Keycloak 이란?
ID 및 액세스 관리 솔루션을 제공하는 오픈소스이다.
쉽게 말하면 인증(Authentication)과 인가(Authorization)를 쉽게 해주고 SSO(Single-Sign-On)를 가능하게 해주는 오픈소스이다.
🔐 Keycloak 설치 및 실행
https://www.keycloak.org/downloads에 접속하여 설치하고자 하는 버전을 선택한 후 설치한다.
설치가 완료되었다면 bin 디렉터리로 이동하여 쉘을 실행한다.
- 윈도우 : standalone.bat
- 리눅스 / 맥 : standalone.sh
🔐 Keycloak 설정
쉘 실행이 완료된 후 'http://localhost:8080/auth'로 접속하면 다음과 같은 웰컴 페이지를 볼 수 있을 것이다.
이제 계정생성부터 토큰생성까지 단계별로 알아보자.
1. 계정생성
- 계정 및 패스워드를 입력하고 Create 버튼을 누른다.
2. 로그인
- 생성한 계정과 패스워드를 입력한 후 로그인한다.
3. Realm 생성
- Add realm 버튼을 클릭한다.
- Name을 입력하고 'Create' 버튼을 클릭한다.
4. Client 생성
- 좌측메뉴 중 'Clients'를 클릭 후 나오는 화면에서 'Create' 버튼을 클릭한다.
- 정보를 입력 후 'Save' 버튼을 클릭한다.
- 화면과 같이 정보를 수정한 후 'Save' 버튼을 클릭한다.
5. Role 생성
- 좌측메뉴 중 'Roles'를 클릭 후 나오는 화면에서 'Add Role' 버튼을 클릭한다.
- 정보를 입력 후 'Save' 버튼을 클릭한다.
6. User 생성
- 좌측메뉴 중 'Users'를 클릭 후 나오는 화면에서 'Add user' 버튼을 클릭한다.
- 정보를 입력 후 'Save' 버튼을 클릭한다.
- 생성이 완료되었으면, 'Credentials' 탭으로 이동하여 비밀번호를 설정한다.
- 'Details' 탭으로 이동 후 'Required User Actions'의 설정 값을 지운다.
- 'Role Mappings' 탭으로 이동 후, 알맞은 권한을 부여한다.
🔐 토큰 발급
설정한 정보를 토대로 토큰을 발급해 보자.
토큰을 발급하기 위해서는 앞서 설정한 client_id, client_secret, user_name, password 정보가 필요하다.
client_id와 client_sercret은 아래의 그림처럼 Clients 메뉴의 'Credentials' 탭에서 확인 가능하며, user_name과 password는 [6. User 생성] 단계에서 입력한 값을 사용하면 된다.
4가지 정보를 토대로 keycloak서버에 토큰생성 요청을 하면, Access토큰을 발급받을 수 있다.
🔐 SpringBoot 연동
이제 스프링부트와 Keycloak을 연동하는 방법에 대해서 알아보자.
1. 의존성 추가
Keycloak 관련 의존성을 추가한다.
dependencies {
...
implementation 'org.keycloak:keycloak-spring-boot-starter:15.1.1'
implementation 'org.keycloak.bom:keycloak-adapter-bom:15.1.1'
...
}
2. 설정 추가
application.yml에 keycloak 관련 설정을 추가한다.
keycloak:
enabled: true
realm: api #생성한 RealM명
auth-server-url: http://localhost:8080/auth
ssl-required: external
resource: boot
credentials:
secret: aiV1caApbRA9aZn0ujLWm1Fo2F976A63 #Clients의 credentials탭의secret 값
use-resource-role-mappings: true
bearer-only: true
3. 설정 클래스 추가
- KeycloakConfiguration.java
keycloak의 경우 keycloak.json을 통해 설정을 할 수 있지만 application.yml을 통해서도 설정을 할 수 있다. 바로 이전 단계에서 application.yml을 통해 설정을 했으니, keycloak.json이 아닌 application.yml의 값을 불러와야 하므로 해당 클래스를 추가하여 application.yml에서 값을 가져오도록 구현한다.
import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class KeycloakConfiguration {
/*
* keycloak.json대신에 application.yml 파일에서 설정 값을 가져온다.
*/
@Bean
public KeycloakSpringBootConfigResolver KeycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
}
- KeycloakSecurityConfig.java
스프링 security를 사용하여 keycloak에서 설정한 값에 따라 권한처리를 진행한다.
앞서 설정을 통해 사용자(user-a)를 생성한 후 'USER' Role을 부여했었다. 해당 사용자로 발급받은 토큰은 아래의 정책에 따라 '/user/**' 또는 '/all/**' 패턴으로 된 URL에만 접근할 수 있게 된다.
import org.keycloak.adapters.springsecurity.KeycloakConfiguration;
import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider;
import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
@KeycloakConfiguration // @EnableWebSecurity 포함
@EnableGlobalMethodSecurity(jsr250Enabled = true, prePostEnabled = true)
public class KeycloakSecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.authorizeRequests()
.antMatchers("/user/**").hasRole("USER")
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/all/**").permitAll()
.anyRequest().authenticated();
http.csrf().disable();
}
/*
* roles 앞에 ROLE_ 와 같이 접두사를 붙이지 않도록 도와준다.
* */
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
auth.authenticationProvider(keycloakAuthenticationProvider);
}
@Bean
@Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
}
즉 아래와 같은 컨트롤러가 있다고 가정한다면, user-a로 발급받은 토큰으로는 'http://localhost:8080/all/access'와 'http://localhost:8080/user/access'는 호출 가능하지만 'http://localhost:8080/admin/access'는 호출할 수 없게 된다.
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequiredArgsConstructor
@Slf4j
public class AuthController {
@GetMapping("/all/access")
public ResponseEntity<String> all() {
log.info("### access ALL");
return ResponseEntity.ok("모두 접근");
}
@GetMapping("/user/access")
public ResponseEntity<String> user() {
log.info("### access USER");
return ResponseEntity.ok("user만 가능");
}
@GetMapping("/admin/access")
public ResponseEntity<String> admin() {
log.info("### access ADMIN");
return ResponseEntity.ok("admin만 가능");
}
}
References.
1. https://caffeineoverflow.tistory.com/31
'IT' 카테고리의 다른 글
[Git] Author identity unknown (1) | 2025.01.20 |
---|---|
[Mybatis] 조회쿼리 성능 향상 (5) | 2025.01.19 |
[Security] Keycloak 소개 (1) | 2025.01.19 |
[SpringBoot] Versioning (1) | 2025.01.19 |
[SpringBoot] HATEOAS (1) | 2025.01.19 |