Last active
February 27, 2019 13:00
-
-
Save benelog/4943718 to your computer and use it in GitHub Desktop.
IpFilter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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