스프링 시큐리티 : 커스텀 필터 만들어보기

이진욱·2024년 12월 12일

이론

목록 보기
9/24

1. IP 주소 로깅 필터

사용자의 IP 주소를 로깅하고 특정 IP 주소를 차단하는 필터를 구현합니다.

import org.springframework.web.filter.OncePerRequestFilter;

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

public class IpLoggingFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        String clientIp = request.getRemoteAddr();
        System.out.println("Client IP: " + clientIp);

        // 특정 IP 차단 로직
        if ("192.168.1.1".equals(clientIp)) {
            response.sendError(HttpServletResponse.SC_FORBIDDEN, "Access Denied for this IP");
            return;
        }

        // 다음 필터로 요청 전달
        filterChain.doFilter(request, response);
    }
}

등록 방법

http.addFilterBefore(new IpLoggingFilter(), UsernamePasswordAuthenticationFilter.class);

2. 요청 시간 측정 필터

요청 처리에 걸리는 시간을 측정하여 성능 모니터링을 위한 데이터를 기록합니다.

import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class RequestTimingFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        long startTime = System.currentTimeMillis();

        // 요청 처리
        filterChain.doFilter(request, response);

        long duration = System.currentTimeMillis() - startTime;
        System.out.println("Request URI: " + request.getRequestURI() + " | Duration: " + duration + " ms");
    }
}

등록 방법

http.addFilterAfter(new RequestTimingFilter(), UsernamePasswordAuthenticationFilter.class);

3. CORS 헤더 추가 필터

CORS 설정을 위해 응답 헤더에 적절한 정보를 추가합니다.

import org.springframework.web.filter.OncePerRequestFilter;

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

public class CorsFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        // CORS 헤더 추가
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
        response.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type");

        // Preflight 요청 처리
        if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
            return;
        }

        // 다음 필터로 요청 전달
        filterChain.doFilter(request, response);
    }
}

등록 방법

http.addFilterBefore(new CorsFilter(), UsernamePasswordAuthenticationFilter.class);

4. Custom Header Validation 필터

특정 요청 헤더의 값이 올바른지 검증하는 필터를 구현합니다.

import org.springframework.web.filter.OncePerRequestFilter;

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

public class HeaderValidationFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        String customHeader = request.getHeader("X-Custom-Header");

        if (customHeader == null || !"ExpectedValue".equals(customHeader)) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid or missing header: X-Custom-Header");
            return;
        }

        // 다음 필터로 요청 전달
        filterChain.doFilter(request, response);
    }
}

등록 방법

http.addFilterAt(new HeaderValidationFilter(), BasicAuthenticationFilter.class);

5. API Key 인증 필터

API 요청에서 API 키를 검사하여 인증하는 필터를 구현합니다.

import org.springframework.web.filter.OncePerRequestFilter;

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

public class ApiKeyAuthFilter extends OncePerRequestFilter {

    private static final String API_KEY = "my-secure-api-key";

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        String apiKey = request.getHeader("X-API-KEY");

        if (apiKey == null || !API_KEY.equals(apiKey)) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid API Key");
            return;
        }

        // 다음 필터로 요청 전달
        filterChain.doFilter(request, response);
    }
}

등록 방법

http.addFilterBefore(new ApiKeyAuthFilter(), UsernamePasswordAuthenticationFilter.class);

6. 요청 Body 로깅 필터

요청의 내용을 로깅하거나, 특정 데이터가 포함되어 있는지 확인합니다.

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.BufferedReader;
import java.io.IOException;

public class RequestBodyLoggingFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        StringBuilder requestBody = new StringBuilder();
        try (BufferedReader reader = request.getReader()) {
            String line;
            while ((line = reader.readLine()) != null) {
                requestBody.append(line).append("\n");
            }
        }
        System.out.println("Request Body: " + requestBody);

        // 다음 필터로 요청 전달
        filterChain.doFilter(request, response);
    }
}
profile
열심히 하는 신입 개발자

0개의 댓글