IT/보안

Keycloak 리뷰(Spring-Security 연동, 테마, SPI, User Federation)

캥거루 2021. 5. 27. 11:02
728x90
반응형

서론

이 글은 Docker 이미지 jboss/keycloak:12.0.1 기준으로 작성되었습니다.

Spring boot 에서 Keycloak 을 이용하여 몇 가지 기능을 구현해보고, 활용할 수 있는 기능들을 리서치 해보았습니다.

Spring security 와 Keycloak 을 연동하여 인증, 인가, 접근주체 확인 정도를 구현을 해보았고, Keycloak 에서 제공하는 테마 변경, SPI, User Federation, REST API 기능들을 살펴보았습니다.

들어가기에 앞서 용어만 간단히 설명하고, 본론으로 넘어가겠습니다.

기본용어

  • 접근 주체(Principal) : 보호된 리소스에 접근하는 대상
  • 인증(Authentication) : 보호된 리소스에 접근한 대상에 대해 검증
  • 인가(Authorize) : 해당 리소스에 대해 접근 가능한 권한을 가지고 있는지 확인하는 과정 (즉, 인증이후 어떤 것을 할 수 있는지?)
  • 권한(Authority) : 어떠한 리소스에 대한 접근할 수 있는 권한
  • 역할(Role) : 권한의 일부로 실질적으로 인가를 위해 확인하는 정보
  • 접근 제어(Access control) 또는 접근 통제: 접근하는 사람, 시스템(주체)이 접근 대상(객체)인 시스템에 접근할 때, 보안상의 위험들로부터 객체와 제반 환경을 보호하기 위한 보안 대책을 의미한다.
    접근 제어의 절차는 식별, 인증, 인가로 구성된다.

본론

Keycloak 에서 제공하는 기능목록

여기서 밑줄 그은 부분을 좀 더 알아보았습니다.

Spring security 와 Keycloak 연동성

Keycloak 은 Client adapter 라고 플랫폼 또는 프레임워크에 긴밀한 통합을 제공함으로써 라이브러리가 아닌 어댑터라고 설명합니다.

무슨 말인가 했는데 사용해보니 그 의미를 조금은 이해가 갔습니다.

우선 Keyclaok 을 연동하기에 클라이언트 프로토콜에 따라 지원되는 플랫폼이 다릅니다.

Java 를 기준으로 OIDC 를 지원하는 플랫폼은 Spring boot, Spring security, Tomcat v7v9 정도이고, SAML 을 지원하는 플랫폼은 Tomcat v7v9, Java servlet filter 정도입니다.

저는 OIDC 를 사용하여 Spring security 와 Keycloak 을 연동해보았습니다.

이유는 Spring security 를 사용하여 개발을 해본적이 있어 어느정도 익숙했으며, Keycloak 은 OIDC 를 사용하기를 권장하고 있기 때문입니다. (아래 참고)

그럼 아래에서 어떻게 연동하여 구현하였는지 알아보겠습니다.

Spring security 와 Keycloak 을 이용한 Single sign on, Single sign out 구현

test realm 생성 (아래 이미지 참고)

  • 부가설정으로 Login 탭에서 위와 같이 회원가입, pw찾기, id저장 등의 기능을 사용하도록 설정할 수도 있다.

client 생성 (아래 이미지 참고)

  • Keycloak 을 연동하고자 하는 어플리케이션이 Client 라고 보면 된다.
  • 옵션들에 대한 설명은 생략

role 생성 (아래 이미지 참고)

  • 해당 메뉴에서 생성하는 Role 는 Test realm 내에서 global 하게 사용할 수 있는 Role 이다.
  • 개별 Client 에만 적용하고자 하는 Role 은 해당 Client 를 선택하여 Roles 탭에서 생성할 수 있다.

user 생성 (아래 이미지 참고)

  • 마찬가지로 크게 어려울 거 없이 사용자 정보를 입력해주고 저장해주면 생성된다.
  • 상단에 Attributes 탭에서 해당 사용자의 기타 정보를 추가로 생성해줄수도 있다. (ex: 주소, 연락처, 부서 등)
  • Credentials 탭에서 초기 PW 를 설정해준다.
  • Role Mappings 탭에서 어떤 Role 을 매핑해줄지 설정해준다. (본인은 user 라는 Role 을 매핑해주었다.)

여기까지 하여 Keycloak 어드민 콘솔에서의 작업은 끝이고, 다음은 Spring security 와 Keycloak 을 연결하는 방법을 알아본다.

Gradle dependencies

implementation 'org.springframework.boot:spring-boot-starter-security'
implementation group: 'org.keycloak', name: 'keycloak-spring-security-adapter', version: '13.0.0'

Keycloak 설정파일 (Keycloak 연결을 위한 설정)

{
 "realm": "test",
 "auth-server-url": "http://{keycloak ip}:{keycloak port}/auth",
 "ssl-required": "external",
 "resource": "springboot-test-app",
 "credentials": {
   "secret": "a8cb80da-393d-465c-98f6-61c4265a04b9"
 },
 "confidential-port": 0
}
  • 위와 같이 설정을 입력하여 파일을 생성한다. (path: src/main/resoures/keycloak.json)
  • 이 설정파일은 아래에 설명이 있지만, KeycloakConfigResolver Bean 내용에 따라 application.yml 에서 작성할 수도 있다.
  • ssl-required 기본값은 external 이며, 유효값은 all, external, none 이 있다.
  • 여기서 credentials 은 Client 를 생성할 때 Access Type 을 confidential 로 하였기에, Keycloak 어드민 콘솔에서 해당 Client 선택 -> Credentials 에 있는 Secret 값을 넣어준다.

Security Config

@KeycloakConfiguration
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {

   // 인증관리 객체에 소유권한 프로바이더 추가
    @Autowired
   public void configureGlobal(AuthenticationManagerBuilder auth) {
       // GrantedAuthoritesMapper 에서 오는 Role 을 Spring Security 가 인식할 수 있도록 Role Mapping 을 지원
       KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
       // SimpleAutorityMapper 는 "ROLE_" 을 삽입 및 대문자로 변환하는데 사용 (Spring Security Core 모듈의 일부)
       keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
       auth.authenticationProvider(keycloakAuthenticationProvider);
    }

   // 세션인증전략 생성
    @Bean
    @Override
   protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
       return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    }

   // 시큐리티 설정
    @Override
   protected void configure(HttpSecurity http) throws Exception {
       super.configure(http);
       http
            .authorizeRequests()
                .antMatchers("/view/user", "/view/admin").authenticated()
                .antMatchers("/view/user").hasRole("user")
                .antMatchers("/view/admin").hasRole("admin")
                .anyRequest().permitAll()
                .and()
            .logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                .logoutSuccessUrl("/view/customer")
                .invalidateHttpSession(true)
                .deleteCookies("JSESSIONID")
                .and()
            .csrf()
                .disable()
                .httpBasic();


    }

   // 키클락 설정 리졸버 생성
    @Bean
   public KeycloakConfigResolver keycloakConfigResolver() {
//        return new KeycloakSpringBootConfigResolver();    // application.yml 로 keycloak 설정
       return new KeycloakConfigResolver() {
           private KeycloakDeployment keycloakDeployment;
            @Override
           public KeycloakDeployment resolve(HttpFacade.Request facade) {
               if (keycloakDeployment != null) {
                   return keycloakDeployment;
                }
               InputStream configInputStream = getClass().getResourceAsStream("/keycloak.json");
               return KeycloakDeploymentBuilder.build(configInputStream);
            }
        };
    }
}
  • 해당 class 는 KeycloakWebSecurityConfigurerAdapter 를 상속받아 구현하는데, 이 파일은 WebSecurityConfigurerAdapter 파일을 상속받아 작성되었기에 결국 Spring Security 와 긴밀한 통합이라고 말하는 것 같다.
  • CSRF Protection 은 disable 로 설정해두었다. (사용 시 모든 요청 header 에 csrf token 을 붙이면 된다.)
  • configure 메소드에서 인가와 로그아웃 등의 설정을 작성할 수 있다.

접근주체 확인

 

// 접근주체 확인
    
@PostMapping("/getUserInfo")
    
public void getUserInfo(HttpServletRequest request, HttpServletResponse response){
        
// 안전한 호출시에 사용
        
Object securityContext1 = request.getAttribute(KeycloakSecurityContext.class.getName());
        
// 안전하지 않은 요청시에 사용
        
Object securityContext2 = request.getSession().getAttribute(KeycloakSecurityContext.class.getName());
    
}

  • securityContext 를 디버깅하면 token 정보 등 접근주체에 대한 모든정보를 가지고 있다.

여기까지하여 Single sign on, Single sign out 이 끝나게 된다.
별도로 웹페이지를 만들어 해당 페이지를 응답해주는 URI 를 만들고, 페이지 이동 테스트를 해보면 인증이 필요한 페이지는 Keycloak 로그인 페이지로 이동하게 되고, 인증 성공 시 인가 된 리소스 사용이 가능하게 된다.

테마 설정

모든 사용자용 페이지는 테마를 수정하거나, 만들고, 배포할 수도 있다.
테마는 기본적인 java 기반 템플릿 엔진인 FreeMaker (확장자 ftl) 를 통해 구현되어 있고, 테마를 커스터마이즈 하기 위해서는 아래와 같은 기술에 대한 이해가 필요하다.

  • html, freemaker
  • css
  • javascript

테마는 만들면서 변경된 테마를 재기동없이 바로 확인해 볼 수 있도록 기능을 제공한다. (설정필요)
또한, 웹페이지에 대한 리소스들이 컴포넌트별로 나누어서 관리되기에 테마에 손을 대기 위해서는 어느정도 초기 분석이 필요해보인다.
컴포넌트는 웹페이지에서 기본적으로 사용되는 템플릿, 스크립트, 이미지, 스타일시트 외에도 메시지번들, 다국어에 대한 컴포넌트도 나누어져 있다.

Keycloak 서버 디렉토리 구성

  • Docker container 내에서는 /opt/jboss/keycloak 이 Docker root path 가 된다.

캐시 설정

테마를 수정하는 작업을 할 때 캐시를 비활성화하면 Keycloak 을 재기동하지 않고도 편집할 수 있다. /opt/jboss/keycloak/standalone/configuration/standalone.xml 이 파일에서 아래 항목을 찾아 수정하면 된다.

 

<theme>
   <staticMaxAge>-1</staticMaxAge>
   <cacheThemes>false</cacheThemes>
   <cacheTemplates>false</cacheTemplates>
    ...
</theme>

테마 만들기

사용가능한 테마 유형

  • Account - Account management
  • Admin - Admin console
  • Email - Emails
  • Login - Login forms
  • Welcome - Welcome page

custom 이라는 테마명으로 login 테마 생성

/opt/jboss/keycloak/themes 위치에 원하는 테마명으로 디렉토리를 생성한다. (ex: custom 이라고 디렉토리를 생성)
/opt/jboss/keycloak/themes/custom/login/theme.properties 해당 파일을 생성하여 아래 테마속성을 작성한다.

parent-base
import-common/keycloak

 

테마 속성

테마 배포

테마는 아카이브로 만들어서 배포가 가능한데, 예를들면 아래와 같이 파일 구성을 할 수 있다.

  • META-INF/keycloak-themes.json
  • theme/mytheme/login/theme.properties
  • theme/mytheme/login/login.ftl
  • theme/mytheme/login/resources/css/styles.css
  • theme/mytheme/login/resources/img/image.png
  • theme/mytheme/login/messages/messages_en.properties
  • theme/mytheme/email/messages/messages_en.properties

META-INF/keycloak-themes.json 의 경우 아래와 같이 작성할 수 있다.

{
   "themes": [{
       "name" : "mytheme",
       "types": [ "login", "email" ]
   }]
}

 

아카이브를 Keycloak 에 배포하려면 Keycloak 의 standalone/deployments/ 디렉토리에 놓기만 하면 자동으로 로드된다.

참고자료

Keycloak themes

SPI

SPI 는 Service Provider Interface 로 Keycloak 의 use-cases 를 코드로 직접 구현을 가능하도록 한다.
Spring boot 에서 SPI 를 구현이 가능하다.

참고자료: Keycloak SPI

User Federation

외부 사용자 데이터베이스를 연합하는 기능이다. 기본적으로 LDAP 및 Active Directory 를 지원하지만, User Storage SPI 를 사용하여 사용자 데이터베이스에 대한 자체 확장을 코딩할 수도 있다. 연합방법은 외부 데이터베이스를 Keycloak 에 동기화(가져오기)하는 방법이다. 참고: User Storage Federation

REST API

Keycloak 은 관리 콘솔에서 제공하는 모든 기능을 REST API 와 함께 제공한다.
API 를 호출하려면 적절한 권한이 있는 액세스 토큰을 얻어야 한다.
참고1: Keycloak REST API Docs
참고2: Keycloak REST API 정의문서


결론

Keycloak 자체로도 부족한 기능없이 인증서버로서의 역할을 충분히 하는 것 같다.
하지만 사용 시 경우에 따라 약간의 추가구현이 필요할 수도 있다.
사용자페이지 테마 변경, SPI, User Federation 정도만 적절히 컨트롤할 수 있다면 충분할 거 같다.

728x90
반응형