해당 포스팅은 spring-security-web의 IpAddressMatcher와 spring의 interceptor를 활용하여 지정된 ip만 해당 서비스에 접근 가능하도록 설정하는 ip 접근제어를 구현한 내용입니다.
(spring-security-web은 org.springframework.boot:spring-boot-starter-security:2.7.3에 포함되어 있습니다.)
Interceptor
@Component
@RequiredArgsConstructor
public class IpAddressAccessControlInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// ip 접근제어 로직 구현 예정
}
}
(IpAddressAccessControlInterceptor class)
HandlerInterceptor interface를 구현한 Interceptor를 구현합니다.
해당 Interceptor에서는 컨트롤러가 호출되기 전에 실행되는 preHandle() method를 통해 ip 접근제어 로직을 구현할 예정이며, 해당 인터셉터를 Bean으로 등록하여 사용할 것이기 때문에 @Component 어노테이션을 붙여줍니다.
WebMvcConfiguration
@Configuration
@RequiredArgsConstructor
public class WebMvcConfiguration implements WebMvcConfigurer {
private final IpAddressAccessControlInterceptor ipAddressAccessControlInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(ipAddressAccessControlInterceptor)
.order(1)
.addPathPatterns("/url1", "/url2")
.excludePathPatterns("/resources/**", "/error/**");
}
}
(WebMvcConfiguration class)
위에서 구현한 Interceptor는 WebMvcConfigurer 인터페이스의 구현체에서 addInterceptors() 메서드를 override 하여 등록하게 되는데요.
addInterceptor() 메서드는 interceptor를 등록하는 메서드이며, order()의 경우 인터셉터가 여러 개 있을 때 순서를 정의하기 위한 메서드입니다. addPathPatterns() 메서드를 통해 해당 인터셉터를 적용시킬 경로를 설정할 수 있으며, excludePathPatterns() 메서드를 통해 인터셉터를 적용하지 않고자 하는 경로를 설정할 수 있습니다.
getClientIp
public static String getClientIp(HttpServletRequest request) {
String clientIp = null;
boolean isIpInHeader = false;
List<String> headerList = new ArrayList<>();
headerList.add("X-Forwarded-For");
headerList.add("HTTP_CLIENT_IP");
headerList.add("HTTP_X_FORWARDED_FOR");
headerList.add("HTTP_X_FORWARDED");
headerList.add("HTTP_FORWARDED_FOR");
headerList.add("HTTP_FORWARDED");
headerList.add("Proxy-Client-IP");
headerList.add("WL-Proxy-Client-IP");
headerList.add("HTTP_VIA");
headerList.add("IPV6_ADR");
for (String header : headerList) {
clientIp = request.getHeader(header);
if (StringUtils.hasText(clientIp) && !clientIp.equals("unknown")) {
isIpInHeader = true;
break;
}
}
if (!isIpInHeader) {
clientIp = request.getRemoteAddr();
}
return clientIp;
}
(Helper class에 구현된 클라이언트의 ip 주소를 가져오는 getClientIp 메서드)
ip 접근제어를 위해서 요청하는 클라이언트의 ip를 가져오는 메서드입니다. getClientIp() 메서드에 관한 자세한 내용은 바로 아래 참고 자료를 통해 좀 더 자세하게 살펴보실 수 있습니다.
2022.06.07 - [Programming/Java] - Java 클라이언트 요청 IP 가져오는 방법(HttpServletRequest)
IpAddressMatcherManager
@Component
public class IpAddressMatcherManager {
@Getter
private List<IpAddressMatcher> ipAddressMatchers;
public IpAddressMatcherManager() {
this.ipAddressMatchers = Arrays.asList(
new IpAddressMatcher("192.168.1.0/24"),
new IpAddressMatcher("192.168.2.0/24")
);
}
public boolean isAccessible(String ipAddress) {
//List<IpAddressMatcher> ipAddressMatchers = this.getIpAddressMatchers();
return ipAddressMatchers.stream().anyMatch(matcher -> matcher.matches(ipAddress));
}
}
(IpAddressMatcherManager class)
IpAddressMatcher는 Spring Security에서 제공하는 IP 매칭 여부를 판단하기 위한 객체인데요. 원격 주소를 ip 주소나 서브넷 마스크를 기반으로 매칭 하며, IPv4 및 IPv6 모두를 지원합니다.
(IPv4와 IPv6 서로 간의 매칭은 지원되지 않습니다.)
접근 가능한 ip로 생성된 IpAddressMatcher의 리스트인 ipAddressMatchers 같은 경우는 상황에 따라 위 예시와 같은 하드코딩이 아니라 properties에 저장된 list를 가져오거나 DB에 저장된 list를 가져오는 방식을 적용할 수 있습니다.
매칭 여부를 확인하는 방법은 IpAddressMatcher 객체의 matches() 메서드를 통해 확인할 수 있습니다.
Interceptor
@Slf4j
@RequiredArgsConstructor
@Component
public class IpAddressAccessControlInterceptor implements HandlerInterceptor {
private final IpAddressMatcherManager ipAddressMatcherManager;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//getClientIp
String clientIpAddress = Helper.getClientIp(request);
if (!ipAddressMatcherManager.isAccessible(clientIpAddress)) {
String requestURI = request.getRequestURI();
log.warn("Forbidden access. request uri={}, client ip={}", requestURI, clientIpAddress);
response.sendError(HttpServletResponse.SC_FORBIDDEN);
return false;
}
return true;
}
}
(IpAddressControlInterceptor class)
최종적으로 구현된 Interceptor입니다. isAccessible() 메서드의 결과에 대한 응답은 프로세스 로직에 따라 수정해서 사용하면 될 것 같습니다.
TEST
WARN 1840 --- [nio-8082-exec-7] .t.l.i.IpAddressAccessControlInterceptor : Forbidden access. request uri=/test, client ip=192.168.3.0
허용되지 않은 ip의 경우 해당 인터셉터를 통해 접근이 불가능한 것을 확인할 수 있습니다.
< 참고 자료 >
'Programming > Spring Boot' 카테고리의 다른 글
JPA에서 Native SQL Function 사용하는 방법 (MetadataBuilder) (0) | 2022.12.07 |
---|---|
(Spring Boot) flyway 데이터 마이그레이션 설정 방법 (0) | 2022.12.04 |
MultipartFile to File 차이점과 변환 방법(Java) (0) | 2022.10.17 |
Spring Boot + GraphQL 기본적인 사용법 정리 (0) | 2022.10.11 |
Spring Boot 부트스트랩(bootstrap) 템플릿 적용하는 방법 (0) | 2022.10.05 |