경계의 경계

Spring Security의 OAuth2 본문

Spring Security

Spring Security의 OAuth2

gigyesik 2024. 4. 26. 03:23

들어가며

Spring Security의 OAuth2 라이브러리는 웹 어플리케이션을 위한 authorization code 방식의 인증과 SPA(Single Page Application)를 위한 implicit 방식의 인증을 지원한다.

또한 Spring Security를 사용하여 서비스할 웹 어플리케이션을 OAuth2 리소스 서버로서 동작하게 할 수 있다.

OAuth2 인증 과정을 복잡하고 일반 로그인에 비해 자원도 많이 소모되지만, Spring Security OAuth2 라이브러리로 설정을 비교적 편하게 진행할 수 있다.

OAuth2란 무엇인가

OAuth 2.0은 IETF(Internet Engineering Task Force. 국제 인터넷 표준화 기구)에서 2012년 8월에 발표한 프로토콜이다.

이 프로토콜의 목적은 인증 과정을 제3자 어플리케이션으로부터 진행하기 위함이다.

예를 들어 생각해보자.

  • 우리는 ‘client.com’에 ‘구글 아이디’로 로그인하고 싶다.
  • 로그인 버튼을 선택하면 구글의 로그인 페이지로 이동한다.
  • 로그인이 성공하면, ‘client.com’에 제공하고 싶은 정보에 대한 동의를 받는다.
  • cllient.com’으로 이동한다.

이것이 OAuth2 인증의 기본적인 흐름이다. 이 흐름을 만들기 위해 OAuth2 프로토콜에서는 다음과 같은 구성 요소가 필요하다.

  • User, Resource Owner : 인증을 시도하는 유저. 클라이언트에 정보 제공을 동의
  • User-Agent : 브라우저
  • Client : 토큰을 요청하는 어플리케이션
  • Authorization Server : 유저와 클라이언트를 인증하는 서버. 토큰을 발행하고 라이프사이클을 관리
  • Resource Server : 요청되는 리소스를 제공하는 API가 있는 서버. 토큰을 검증하고 인가 과정 처리

예시 확인하기

이제 Spring Boot 를 사용한 OAuth2 인증 구현 살펴본다. 참고 소스가 구버전이므로 소스를 따라가면서 개념만 익혀본다.

Build.gradle

인증 서버에 필요한 의존성은 아래와 같다.

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-oauth2-authorization-server'
    implementation 'org.springframework.boot:spring-boot-starter-security'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
}

UserConfig

이제 설정 파일을 하나씩 구현해보자. 유저 인증 관련 설정을 담담하는 UserConfig는 아래와 같다.

@Configuration
public class UserSecurityConfig {
    @Bean
    public UserDetailsService userDetailsService() {
        return new InMemoryUserDetailsManager(User
                .withUsername("gigyesik")
                .password("1234")
                .authorities("read")
                .build());
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }
    
}

  • UserDetailsService는 유저의 인증 정보와 권한 정보를 조회하기 위한 Bean이다.
    • UserDetailsManager는 UserDetailsService의 상속체로 createUser(), updateUser() 같은 메서드를 포함하고 있다.
    • 간단한 구현을 위해 In-Memory 구현체를 사용하였지만, 실제 환경에서는 JdbcUserDetailsManager를 사용해야 할 것이다.
  • Spring Security Context에서 유저 인증을 진행하기 위한 PasswordEncoder를 Bean으로 등록한다.
    • 여기서도 간단한 구현을 위해 No-Op 구현체를 사용하였다. 실제 환경에서는 기본값으로 Bcrypt가 사용된다.

AuthServerConfig

인증 서버에 등록될 클라이언트에 관한 설정을 위한 AuthServerConfig를 작성한다.

@Configuration
@EnableWebSecurity
public class AuthServerConfig {
    @Bean
    public RegisteredClientRepository registeredClientRepository() {
        return new InMemoryRegisteredClientRepository(
                RegisteredClient
                    .withId("client1")
                    .clientId("client1")
                    .clientName("client1")
                    .clientSecret("secret")
                    .scope("read")
                    .authorizationGrantType(AuthorizationGrantType.PASSWORD)
                    .build()
        );
    }
}

인증을 요청하는 클라이언트로 등록하기 위해서는 아래 정보가 필요하다.

  • clientId : 클라이언트 ID
  • secret : 클라이언트 비밀 키
  • scope : 클라이언트의 권한 범위. 공백이나 undefined로 설정한 경우 모든 권한을 획득한다.
  • grantType : 클라이언트 인증 방식
    • 위 소스에서는 password를 사용했지만, 실제 환경에서는 다른 방식을 사용해야 한다.

마치며

구현을 하려고 했던 포스팅이었는데, 덩치가 너무 커질까 싶어 개념정리만 하고 마무리하게 되었다.

서비스를 구축하면서 OAuth2 관련 정리는 꾸준히 해야 하겠다.

Resources