TIL

Spring Security & JWT Tutorial

hyunzxn 2023. 2. 1. 18:21

1. 들어가며

어떤 서비스가 됐든 회원가입, 로그인 기능은 필수라고 할 수 있을텐데요. 흔히 말하는 인증, 인가 개념이 이 때 사용되는데 이것을 하는 방법은 다양합니다. 그 중 Spring Security 를 사용하면 인증, 인가 뿐 아니라 다양한 보안 조치를 Spring으로 만든 애플리케이션에 적용할 수 있습니다.

 

개인적으로 Spring Security가 많이 어렵게 느껴지긴 했습니다. 설정할 것들도 많고 알아야 할 개념들도 많았기 때문입니다. 게다가 자료도 스프링 MVC, JPA에 비하여 너무 적었습니다 ㅠㅠ (스프링 시큐리티 제대로 강의를 찍으면 부자가 될 지도..! 실력자 분들 츄라이 츄라이)

 

이번 포스팅에서는 여전히 많이 부족하지만 Spring Security와 JWT를 사용하여 사용자 인증을 처리하는 가장 간단한 튜토리얼에 관하여 정리해보고자 합니다. 코드는 github를 참고해주시면 감사하겠습니다.

 

[참고 영상]

[예제 코드(github)]

 

 

2. Spring Security 적용방법

스프링 시큐리티를 적용하는 방법은 간단합니다. 그래들 혹은 메이븐에 의존성을 추가해주면 됩니다.

 

- Gradle (build.gradle)

implementation 'org.springframework.boot:spring-boot-starter-security'

 

- Maven (pom.xml)

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
</dependency>

 

3. Spring Security Config 설정하는 법(SecurityFilterChain)

 

Spring Security를 사용할 때 Config class를 만드는 법이 살짝 변경됐습니다. 기존에는 WebSecurityConfigurerAdapter 추상 클래스를 상속하여 사용했지만 현재는 이 WebSecurityConfigureAdapter 클래스가 Deprecated가 되었습니다.

 

현재 공식문서에 따르면 SecurityFilterChain을 Bean으로 등록해서 사용하기를 권장하고 있습니다. 

 

- SecurityFilterChain이란?

 

SecurityFilterChain을 보기에 앞서 Spring Security는 서블릿 필터와 이 필터들의 체인을 사용한다는 사실을 알아야 합니다. 

 

필터란 Http 요청이 클라이언트로부터 들어오면 서블릿에 도달하기 전에 그 앞에 Filter들을 놓고 쳐낼 건 쳐내고 통과되는 것들만 서블릿에서 로직을 탈 수 있게 하는 것입니다. 

 

스프링 부트는 DelegatingFilterProxy라는 것으로 서블릿 컨테이너와 스프링의 ApplicationContext를 연결해줍니다. DelegatingFilterProxy는 FilterChain 사이에 들어가서 Filter 역할을 수행합니다.

 

스프링 시큐리티는 FilterChainProxy로 서블릿을 지원합니다. SecurityFilterChain은 FilterChainProxy가 사용할 필터를 선택할 때 사용하는 것입니다.

 

 

 

SecurityFilterChain은 이렇게 서블릿에 도달하기 전에 필터에서 처리할 것들을 설정해주는 곳이라고 생각하면 좋습니다. Http 요청에 대한 필터, Web과 관련된 필터 등을 설정할 수 있습니다. 스프링 시큐리티에 관한 전반적인 설정은 securityFilterChain에서 해준다고 생각하면 됩니다.

 

4. Spring Security & JWT 구현 관련 알아둬야 할 개념

 

- UserDetails

 

UserDetails란 스프링 시큐리티에서 유저의 정보를 담는 인터페이스입니다. 

 

- UserDetailsService

 

스프링 시큐리티에서 유저의 정보를 불러오는 인터페이스입니다.

 

- OncePerRequestFilter

 

JWT 필터를 구현할 때 많이 사용되는 필터의 한 종류입니다. 이 필터의 중요한 점은 Http 요청 당 한 번의 실행을 보장한다는 것입니다. 이것을 이해하려면 일반 필터가 어떻게 작동하는지를 알면 좋습니다.

 

일반적인 필터는 인증과 접근 제어와 같은 기능이 서블릿으로 들어왔을 때 이 요청이 다른 서블릿으로 넘어갈 때 또 다른 필터를 거쳐서 가면서 필터가 두 번 실행될 수 있다는 문제가 있다. OncePerRequestFilter는 모든 서블릿에 일관된 요청을 처리하기 위한 필터이기 때문에 요청 당 한 번만 작동하도록 할 수 있습니다. 

 

- Authentication

 

현재 인증된 사용자의 정보를 담고 있는 인터페이스입니다. 스프링 시큐리티 전반에서 많이 등장하는 개념입니다. Authentication은 상황에 따라 다양한 구현체로 나타납니다.

 

- SecurityContextHolder

 

SecurityContexHolder에는 스프링 시큐리티로 인증한 사용자의 상세정보를 저장합니다. 다시 말해서 Authentication을 담고 있는 곳이라고 할 수 있습니다. 

 

이 때 이 정보에 접근하기 위해서는 Authentication.getContext().getAuthentication() 을 사용하면 됩니다. 

 

- UsernamePasswordAuthenticationToken

 

UsernamePasswordAuthenticationToken은 id,password를 통해 인증을 하는 경우에 Authentication의 구현체로서 나타나는 클래스입니다. 

 

- AuthenticationManager

 

스프링 시큐리티 인증 필터의 인증 수행 방식을 정의하는 API입니다. (인증을 하는 주체?)

 

- ProviderManger

 

가장 많이 쓰이는 AuthenticationManager의 구현체입니다. ProviderManagerAuthenticationProvider List를 가지고 있습니다. ProviderManager는 실제 작업을 AuthenticationProvider에 위임합니다. 각 AuthenticationProvider마다 담당하는 인증 유형이 다릅니다.

 

- AuthenticationProvider

 

ProviderManager에 주입되는 것으로서 각 AuthenticationProvider마다 담당하는 인증 유형이 다릅니다. 예를 들어 DaoAuthenticationProvider는 이름/비밀번호 기반 인증, JwtAuthenticationProvider는 JWT 토큰 기반 인증을 담당합니다. 

 

 

 

5. JWT 인증 요청 흐름

출처: 들어가며에 언급한 참고영상

1. 클라이언트가 요청을 보냅니다.

2. JWT를 검증하는 필터에서 JWT가 존재하는지 확인합니다.

3. 스프링 시큐리티의 UserDetailsService에 유저 정보를 요청합니다.

4. UserDetailsServices는 DB 등에서 유저 정보를 가지고 옵니다. 이것을 UserDetails에 넘겨줍니다.

5. 2번의 필터가 UserDetails를 확인합니다. 

6. JWT에 있는 정보와 비교합니다.

7. SecurityContextHolder를 업데이트합니다. 유저를 인증된 상태로 만듭니다.

8. 클라이언트의 요청을 디스패처 서블릿으로 보냅니다. 

9. 컨트롤러가 요청을 수행하고 클라이언트에 응답을 반환합니다.

 

 

 

 

 

[Thanks to]

- 도전하는 개발자, OncePerRequestFilter 와 Filter의 차이

- 스프링 시큐리티 공식문서

- blessing333, Spring Security 시리즈 -1 인증 처리

 

 

728x90