Skip to content

Instantly share code, notes, and snippets.

@benelog
Last active February 27, 2019 13:00
Show Gist options
  • Save benelog/4943718 to your computer and use it in GitHub Desktop.
Save benelog/4943718 to your computer and use it in GitHub Desktop.
IpFilter
package net.benelog.markerboard.filter;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
/**
*
* 허락된 IP만 접근할 수 있게 걸러준다. IP는 CIDR 형식으로 지정가능하다. (예: 10.0.0.0/16)
* Spring Security의 IpAddressMatcher의 코드를 배낌.
* (http://www.jarvana.com/jarvana/view/org/springframework/security/spring-security-web/3.0.3.RELEASE/spring-security-web-3.0.3.RELEASE-sources.jar!/org/springframework/security/web/util/IpAddressMatcher.java)
*
* @author sanghyuk.jung
*
*/
public class IpFilter extends HandlerInterceptorAdapter {
private final Logger log = LoggerFactory.getLogger(IpFilter.class);
private final CidrNotation[] permittedIpRanges;
public IpFilter(String... ipList) {
permittedIpRanges = new CidrNotation[ipList.length];
for (int i=0,n = ipList.length;i<n; i++) {
String ip = ipList[i];
int maskBits = -1;
if (ipList[i].indexOf('/') > 0) {
String[] addressAndMask = StringUtils.split(ip, "/");
ip = addressAndMask[0];
maskBits = Integer.parseInt(addressAndMask[1]);
}
permittedIpRanges[i] = new CidrNotation(parseAddress(ip),maskBits);
}
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String remoteAddr = request.getRemoteAddr();
log.debug("Request from {} ", remoteAddr);
for (CidrNotation ip: permittedIpRanges ) {
if (matches(remoteAddr, ip)){
return true;
}
}
log.warn("IP {} is not allowed.", remoteAddr);
return false;
}
public boolean matches(String clientAddr, CidrNotation permittedRange) {
InetAddress permittedAddr = permittedRange.getAddress();
InetAddress remoteAddr = parseAddress(clientAddr);
if (!permittedAddr.getClass().equals(remoteAddr.getClass())) {
return false;
}
int maskBits = permittedRange.getMaskBits();
if (maskBits < 0) {
return remoteAddr.equals(permittedAddr);
}
byte[] clientAddrBytes = remoteAddr.getAddress();
byte[] permittedAddrBytes = permittedAddr.getAddress();
int oddBits = maskBits % 8;
int maskBytes = maskBits / 8 + (oddBits == 0 ? 0 : 1);
byte[] mask = new byte[maskBytes];
Arrays.fill(mask, 0, oddBits == 0 ? mask.length : mask.length - 1, (byte) 0xFF);
if (oddBits != 0) {
int finalByte = (1 << oddBits) - 1;
finalByte <<= 8 - oddBits;
mask[mask.length - 1] = (byte) finalByte;
}
for (int i = 0; i < mask.length; i++) {
if ((clientAddrBytes[i] & mask[i]) != (permittedAddrBytes[i] & mask[i])) {
return false;
}
}
return true;
}
private InetAddress parseAddress(String address) {
try {
return InetAddress.getByName(address);
} catch (UnknownHostException e) {
throw new IllegalArgumentException("Failed to parse address" + address, e);
}
}
static class CidrNotation {
private final InetAddress address;
private final int maskBits;
public int getMaskBits() {
return maskBits;
}
public CidrNotation(InetAddress address, int maskBits){
this.address = address;
this.maskBits = maskBits;
}
public InetAddress getAddress() {
return address;
}
}
}
package net.benelog.markerboard.filter;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
public class IpFilterTest {
Object handler = new Object();
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
IpFilter filter;
@Test
public void passedWithSimple() throws Exception {
request.setRemoteAddr("192.168.0.1");
filter = new IpFilter("192.168.0.1");
boolean passed = filter.preHandle(request, response, handler);
assertThat(passed, is(true));
}
@Test
public void passedWith1IpList() throws Exception {
request.setRemoteAddr("192.168.0.1");
filter = new IpFilter("127.0.0.1/16");
boolean passed = filter.preHandle(request, response, handler);
assertThat(passed, is(false));
}
@Test
public void nonpassedWith1IpList() throws Exception {
request.setRemoteAddr("127.0.100.12");
filter = new IpFilter("127.0.0.1/16");
boolean passed = filter.preHandle(request, response, handler);
assertThat(passed, is(true));
}
@Test
public void nonpassedWith12IpList() throws Exception {
request.setRemoteAddr("127.0.1.12");
filter = new IpFilter("192.0.0.1/16", "125.0.0.1/16");
boolean passed = filter.preHandle(request, response, handler);
assertThat(passed, is(false));
}
@Test
public void passedWith12IpList1() throws Exception {
request.setRemoteAddr("127.0.1.12");
filter = new IpFilter("127.0.1.12/32", "192.0.0.1/16");
boolean passed = filter.preHandle(request, response, handler);
assertThat(passed, is(true));
}
@Test
public void passedWith12IpList2() throws Exception {
request.setRemoteAddr("127.0.1.12");
filter = new IpFilter("192.0.0.1/16", "127.0.1.12/32");
boolean passed = filter.preHandle(request, response, handler);
assertThat(passed, is(true));
}
@Test
public void passedWith2IpList3() throws Exception {
request.setRemoteAddr("127.0.1.13");
filter = new IpFilter("192.0.0.1/16", "127.0.1.12/31");
boolean passed = filter.preHandle(request, response, handler);
assertThat(passed, is(true));
}
@Test
public void passedWith2IpList4() throws Exception {
request.setRemoteAddr("127.0.1.12");
filter = new IpFilter("192.0.0.1/16", "127.0.1.13/31");
boolean passed = filter.preHandle(request, response, handler);
assertThat(passed, is(true));
}
@Test
public void passedWith3IpList3() throws Exception {
request.setRemoteAddr("210.94.41.89");
filter = new IpFilter("192.0.0.1/16", "127.0.1.12/31", "210.94.41.88/31");
boolean passed = filter.preHandle(request, response, handler);
assertThat(passed, is(true));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment