[스탠다드] Spring Security + JWT

토리·2025년 2월 26일
0

1. 강의 목표

  • Spring Security의 기본 개념 이해
  • JWT(Json Web Token)의 개념과 필요성 이해
  • JWT를 활용한 인증 및 인가 구현
  • 실전 예제를 통해 JWT 기반 인증 시스템 구축

2. Spring Security

1) 인증(Authentication) vs 인가(Authorization)

  • Authentication(인증): 사용자의 신원을 확인하는 과정 (예: 로그인)
  • Authorization(인가): 인증된 사용자가 특정 리소스에 접근할 수 있는 권한이 있는지 확인하는 과정

2) Spring Security의 구조

  • Security Filter Chain: 요청이 들어오면 여러 개의 필터를 거쳐 보안 검사를 수행함
  • UserDetailsService: 사용자 정보를 로드하는 인터페이스
  • AuthenticationManager: 인증을 처리하는 핵심 컴포넌트
  • PasswordEncoder: 비밀번호를 암호화 및 비교하는 기능 제공

3) 기본적인 Security 설정

ex) 버전 마다 다름
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
        .csrf().disable()
        .authorizeHttpRequests(auth -> auth
            .requestMatchers("/api/public").permitAll()
            .requestMatchers("/api/private").authenticated()
        )
        .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
    return http.build();
}

4) Spring Security 특징

  1. 서블릿 API 통합
  2. Spring Web MVC와의 선택적 통합
  3. 인증과 권한 부여를 확장 가능하게 지원
  4. 세션 고정, CSRF(사이트 간 요청 위조) , ClickJacking 등을 보호

5) Servlet Filter Chain

  1. 서블릿 필터 체인(Servlet Filter Chain) 흐름

    →Spring Security의 필터 체인(Filter Chain) 동작 방식

  • 사용자의 Request(요청)이 들어오면 여러 개의 Servlet Filter를 거침
  • 각 필터는 doFilter() 메서드를 호출하며 체인을 따라 다음 필터로 요청을 전달
  • 마지막으로 Servlet이 요청을 처리한 후, 다시 반대 방향으로 Response(응답)을 반환
  • Spring Security에서는 이러한 필터 체인을 활용하여 JWT 검증, 인증(Authentication), 권한 확인(Authorization) 등의 작업을 수행

6) Spring Security 필터 흐름도


2. Spring Security 필터 흐름 및 구성 요소

-> Spring Security의 필터 및 보안 처리 흐름

  • DelegatingFilterProxy
    • Spring Security의 핵심 필터인 FilterChainProxy를 위임하는 역할
    • 실제 필터 체인의 시작점이며, Spring Security의 여러 보안 필터를 실행
  • FilterChainProxy
    • 여러 개의 보안 필터(Security Filters)를 관리하는 Spring Security의 중심 필터 체인
    • 사용자의 요청이 들어오면, 설정된 WebSecurity 설정을 기반으로 필터 체인을 구성하여 실행
  • 인증(Authentication) 관련 필터
    • UsernamePasswordAuthenticationFilter: 사용자의 아이디와 비밀번호를 검증하는 필터 (기본 로그인 처리)
    • JwtAuthenticationFilter (추가 필요): JWT 기반 인증을 처리하는 커스텀 필터
    • SecurityContextPersistenceFilter: 보안 컨텍스트(SecurityContext)를 유지하는 역할
  • 인가(Authorization) 및 기타 보안 처리
    • SessionManagementFilter: 세션을 관리하며, JWT를 사용하는 경우 STATELESS로 설정
    • FilterSecurityInterceptor: 최종적으로 접근 권한을 체크하고 요청을 허용할지 결정하는 필터
  • Spring Security 필터 체인의 핵심 개념
    1. 요청이 들어오면 DelegatingFilterProxy → FilterChainProxy가 이를 관리
    2. 각 필터를 순차적으로 실행하며 인증(Authentication)과 인가(Authorization) 수행
    3. 최종적으로 요청이 허용되면 Servlet에서 처리 후 응답을 반환
    4. JWT 기반 인증을 적용할 경우, JwtAuthenticationFilter를 추가하여 Access Token 검증

7) Spring Security 인증 과정 흐름도


1. 사용자의 HTTP 요청 (Http Request)
- 사용자가 로그인 요청을 보내거나 보호된 리소스에 접근하려 할 때 Spring Security의 Security Filter Chain이 이를 가로챔
2. AuthenticationFilter (인증 필터)
- Spring Security의 첫 번째 방어선으로, 들어오는 요청에서 인증 정보를 확인하는 역할
- JWT 기반 인증을 사용할 경우, JwtAuthenticationFilter를 여기에서 실행하여 토큰을 검증
- 인증 정보가 있다면 AuthenticationManager로 넘김
3. AuthenticationManager (인증 관리자)
- 실제 인증을 처리하는 역할을 담당
- 여러 개의 AuthenticationProvider 중 적절한 것을 찾아서 인증을 위임
4. AuthenticationProvider (인증 제공자)
- UserDetailsService를 사용하여 데이터베이스에서 사용자 정보를 가져오고, 비밀번호를 검증
- 여기에 PasswordEncoder 위치
- 인증이 성공하면, 인증된 사용자 정보를 반환
5. UserDetailsService (사용자 정보 로드)
- 데이터베이스에서 사용자 정보를 조회하고, UserDetails 객체로 반환
- 일반적으로 loadUserByUsername(String username) 메서드를 통해 사용자 정보를 가져옴
6. SecurityContextHolder (보안 컨텍스트 저장)
- 인증이 성공하면, SecurityContextHolder에 인증 정보를 저장
- 이후 요청에서 인증이 필요할 때마다 여기에서 인증 정보를 가져옴
7. FilterChain을 거쳐 컨트롤러(Spring Controller)로 요청 전달
- 인증이 완료되면, 요청이 정상적으로 필터 체인을 통과하고 Spring Controller로 전달
- 컨트롤러에서는 @AuthenticationPrincipal 어노테이션을 사용하여 사용자 정보를 가져옴

중요!!

  • Spring Security는 AuthenticationFilter → AuthenticationManager → AuthenticationProvider → UserDetailsService 순서로 요청을 처리함.
  • 사용자가 인증되면 SecurityContextHolder에 저장하여 이후 요청에서 재사용 가능.
  • JWT 기반 인증을 사용할 경우, AuthenticationFilter에서 JWT 검증을 수행하고, 인증이 완료되면 SecurityContext에 저장하여 이후 인증 요청 시 활용.

3. JWT + Spring Security

1) JWT란?

  • Json Web Token의 약자로, 인증 및 정보 교환을 위한 토큰 기반 인증 방식
  • 세션 기반 인증과 다르게 서버가 상태를 유지할 필요 없음 (Stateless)
  • HTTP 헤더에 포함되어 요청 시 전달됨

2) Access Token vs. Refresh Token

  • Access Token: 사용자가 인증된 상태임을 나타내는 짧은 수명의 토큰
    • 사용자가 인증된 상태임을 나타내는 토큰으로, 주로 짧은 만료 시간을 가짐 (예: 15~30분)
    • 클라이언트가 요청을 보낼 때 Authorization: Bearer <Access Token> 헤더에 포함하여 서버로 전송함
    • 서버는 토큰을 검증하여 사용자의 인증 정보를 확인함
    • 만료되면 더 이상 사용할 수 없음 (재발급 필요)
  • Refresh Token: Access Token이 만료되었을 때 새로운 Access Token을 발급하기 위한 토큰
    • Access Token이 만료되었을 때 새로운 Access Token을 발급받기 위한 토큰
    • 일반적으로 Access Token보다 긴 유효기간을 가짐 (예: 7일~30일)
    • 보안상 중요하므로 서버(DB)에서 관리하는 경우가 많음
    • Refresh Token을 사용해 새로운 Access Token을 요청하는 API (/refresh)를 따로 구현해야 함
  • JWT는 Stateless인데 왜 Refresh Token을 DB에서 관리할까?
    • Access Token은 서버에서 상태를 저장하지 않아도 클라이언트에서 보관하고 전송할 수 있기 때문에 JWT의 장점(Stateless)을 활용할 수 있음.
    • 하지만 Refresh Token은 보안성 문제로 인해 DB에 저장하는 것이 일반적
      • Refresh Token이 탈취되었을 경우: 공격자가 무한히 Access Token을 발급할 수 있음
      • 사용자가 로그아웃했을 경우: Refresh Token을 삭제하여 더 이상 Access Token을 갱신할 수 없도록 해야 함
      • 다중 기기 지원: 각 기기별 Refresh Token을 관리하여 특정 기기에서만 로그아웃이 가능하도록 할 수 있음

4. Spring Security와 JWT의 연관성 및 인증/인가 과정

1) Spring Security와 JWT 연관성

  • Spring Security는 기본적으로 세션 기반 인증을 사용하지만, JWT를 활용하면 세션 없이도 Stateless 인증이 가능
  • JWT를 활용하면 Security Filter Chain에서 인증 필터를 추가하여 요청마다 토큰을 검증하고, SecurityContextHolder에 인증 정보를 저장

2) JWT 인증 및 인가 과정

  1. 사용자가 로그인 요청 (/api/auth/login)
    • 사용자가 아이디와 비밀번호를 입력하여 로그인 요청을 보냄
    • AuthenticationManager가 UserDetailsService를 통해 사용자 정보를 조회하고 인증 수행
    • 인증이 성공하면 JWT Access Token과 Refresh Token을 생성하여 반환
  2. 클라이언트가 Access Token을 포함하여 API 요청
    • 클라이언트는 Authorization: Bearer 헤더를 포함하여 요청을 보냄
    • 요청이 Security Filter Chain을 거치면서 JwtAuthenticationFilter에서 JWT 검증 수행
  3. JWT 검증 및 SecurityContextHolder에 저장
    • JwtAuthenticationFilter에서 JWT의 서명을 검증하고, 토큰이 유효하면 SecurityContextHolder에 사용자 인증 정보 저장
    • 이후의 모든 요청은 SecurityContext에서 인증 정보를 가져와 처리됨
  4. 인가(Authorization) 처리
    • @PreAuthorize, @Secured, @RolesAllowed 등의 어노테이션을 활용하여 사용자의 권한을 확인하고 접근 제어 수행
    • 예: @PreAuthorize("hasRole('ADMIN')")
  5. Access Token 만료 시 Refresh Token을 이용한 재발급
    • 클라이언트가 Access Token이 만료되었을 경우, 저장된 Refresh Token을 이용하여 새로운 Access Token 요청 (/api/auth/refresh)
    • 서버는 Refresh Token을 검증하고 새로운 Access Token을 생성하여 반환
profile
안녕하세요. 토리입니다.

0개의 댓글