반응형

Spring Security전자정부프레임워크를 활용하여 OAuth 2.0의 커스텀 인증 필터를 구현하는 방법을 학습합니다. 이 과정은 사용자 인증을 맞춤화하고, Access Token을 기반으로 API 요청을 처리하는 데 유용합니다.


1. 커스텀 인증 필터란?

Spring Security의 **필터(Filter)**는 요청(Request)이 애플리케이션에 도달하기 전에 인증 및 권한 부여를 처리하는 중요한 역할을 합니다.

  • 커스텀 인증 필터는 기본 인증 필터를 확장하여 요구사항에 맞는 인증 로직을 추가로 구현할 수 있습니다.
  • 이 필터를 통해 Access Token을 검증하거나 사용자 정보를 로드할 수 있습니다.

2. 커스텀 인증 필터 설계

2-1. 요구사항 정의

  • 모든 요청에서 Authorization 헤더에 포함된 Access Token을 확인합니다.
  • Access Token의 유효성을 검증하고, 유효한 경우 사용자 정보를 SecurityContext에 저장합니다.

2-2. 필터 구현 개요

  1. GenericFilterBean 또는 OncePerRequestFilter를 확장합니다.
  2. doFilter() 메서드를 오버라이드하여 토큰 검증 로직을 추가합니다.
  3. Spring Security의 FilterChain에 필터를 등록합니다.

3. 커스텀 인증 필터 구현

3-1. 필터 클래스 작성

import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class CustomAuthenticationFilter extends OncePerRequestFilter {

    private final TokenValidationService tokenValidationService;
    private final UserDetailsService userDetailsService;

    public CustomAuthenticationFilter(TokenValidationService tokenValidationService, UserDetailsService userDetailsService) {
        this.tokenValidationService = tokenValidationService;
        this.userDetailsService = userDetailsService;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        String authorizationHeader = request.getHeader("Authorization");

        if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
            String token = authorizationHeader.substring(7);

            if (tokenValidationService.validateAccessToken(token)) {
                String username = tokenValidationService.extractUsername(token);

                UserDetails userDetails = userDetailsService.loadUserByUsername(username);
                CustomAuthenticationToken authentication = new CustomAuthenticationToken(
                    userDetails, null, userDetails.getAuthorities());
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        }

        chain.doFilter(request, response);
    }
}

3-2. TokenValidationService 클래스 구현

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import org.springframework.stereotype.Service;

@Service
public class TokenValidationService {

    private static final String SECRET_KEY = "mysecretkey";

    public boolean validateAccessToken(String token) {
        try {
            Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public String extractUsername(String token) {
        Claims claims = Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
        return claims.getSubject();
    }
}

3-3. Spring Security 설정

커스텀 필터를 Spring Security의 필터 체인에 추가합니다.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
public class SecurityConfig {

    private final CustomAuthenticationFilter customAuthenticationFilter;

    public SecurityConfig(CustomAuthenticationFilter customAuthenticationFilter) {
        this.customAuthenticationFilter = customAuthenticationFilter;
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeRequests()
            .antMatchers("/public/**").permitAll()
            .anyRequest().authenticated()
            .and()
            .addFilterBefore(customAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }
}

4. 테스트

  1. API 호출 테스트
    • Authorization 헤더 없이 요청: 401 Unauthorized 반환.
    • 올바른 Access Token 포함: 요청 성공 및 데이터 반환.
  2. 로그 테스트
    • Access Token이 만료된 경우, 로그에 오류 메시지를 출력합니다.
  3. 유효한 사용자 확인
    • Access Token에서 추출된 사용자 정보가 SecurityContext에 저장되는지 확인합니다.

5. 마무리

이번 학습에서는 커스텀 인증 필터를 설계하고 구현했습니다. 이 필터는 Spring Security의 인증 과정을 확장하여 Access Token 검증과 사용자 인증 로직을 맞춤화할 수 있습니다.
다음에서는 다중 인증 방식 (JWT, OAuth 2.0)의 통합 구현을 학습합니다.

반응형

+ Recent posts