IT

[Security] Keycloak 설치 및 설정

data-cloud 2025. 1. 19. 12:49
반응형

 

 

 

🔐 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 생성] 단계에서 입력한 값을 사용하면 된다.

클라이언트 ID 및 Secret 정보

 

 

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