diff --git a/web/src/main/java/org/springframework/security/web/util/matcher/IpAddressMatcher.java b/web/src/main/java/org/springframework/security/web/util/matcher/IpAddressMatcher.java index 788d107b76..a235b59dd9 100644 --- a/web/src/main/java/org/springframework/security/web/util/matcher/IpAddressMatcher.java +++ b/web/src/main/java/org/springframework/security/web/util/matcher/IpAddressMatcher.java @@ -18,6 +18,7 @@ package org.springframework.security.web.util.matcher; import java.net.InetAddress; import java.net.UnknownHostException; +import java.util.Objects; import java.util.regex.Pattern; import jakarta.servlet.http.HttpServletRequest; @@ -33,16 +34,17 @@ import org.springframework.util.StringUtils; * IPv4 address will never match a request which returns an IPv6 address, and vice-versa. * * @author Luke Taylor + * @author Steve Riesenberg * @since 3.0.2 */ public final class IpAddressMatcher implements RequestMatcher { private static Pattern IPV4 = Pattern.compile("\\d{0,3}.\\d{0,3}.\\d{0,3}.\\d{0,3}(/\\d{0,3})?"); - private final int nMaskBits; - private final InetAddress requiredAddress; + private final int nMaskBits; + /** * Takes a specific IP address or a range specified using the IP/Netmask (e.g. * 192.168.1.0/24 or 202.24.0.0/14). @@ -52,18 +54,22 @@ public final class IpAddressMatcher implements RequestMatcher { public IpAddressMatcher(String ipAddress) { Assert.hasText(ipAddress, "ipAddress cannot be empty"); assertNotHostName(ipAddress); + + String requiredAddress; + int nMaskBits; if (ipAddress.indexOf('/') > 0) { - String[] addressAndMask = StringUtils.split(ipAddress, "/"); - ipAddress = addressAndMask[0]; - this.nMaskBits = Integer.parseInt(addressAndMask[1]); + String[] parts = Objects.requireNonNull(StringUtils.split(ipAddress, "/")); + requiredAddress = parts[0]; + nMaskBits = Integer.parseInt(parts[1]); } else { - this.nMaskBits = -1; + requiredAddress = ipAddress; + nMaskBits = -1; } - this.requiredAddress = parseAddress(ipAddress); - String finalIpAddress = ipAddress; + this.requiredAddress = parseAddress(requiredAddress); + this.nMaskBits = nMaskBits; Assert.isTrue(this.requiredAddress.getAddress().length * 8 >= this.nMaskBits, () -> String - .format("IP address %s is too short for bitmask of length %d", finalIpAddress, this.nMaskBits)); + .format("IP address %s is too short for bitmask of length %d", requiredAddress, this.nMaskBits)); } @Override @@ -71,14 +77,14 @@ public final class IpAddressMatcher implements RequestMatcher { return matches(request.getRemoteAddr()); } - public boolean matches(String address) { + public boolean matches(String ipAddress) { // Do not match null or blank address - if (!StringUtils.hasText(address)) { + if (!StringUtils.hasText(ipAddress)) { return false; } - assertNotHostName(address); - InetAddress remoteAddress = parseAddress(address); + assertNotHostName(ipAddress); + InetAddress remoteAddress = parseAddress(ipAddress); if (!this.requiredAddress.getClass().equals(remoteAddress.getClass())) { return false; } @@ -88,26 +94,31 @@ public final class IpAddressMatcher implements RequestMatcher { byte[] remAddr = remoteAddress.getAddress(); byte[] reqAddr = this.requiredAddress.getAddress(); int nMaskFullBytes = this.nMaskBits / 8; - byte finalByte = (byte) (0xFF00 >> (this.nMaskBits & 0x07)); for (int i = 0; i < nMaskFullBytes; i++) { if (remAddr[i] != reqAddr[i]) { return false; } } + byte finalByte = (byte) (0xFF00 >> (this.nMaskBits & 0x07)); if (finalByte != 0) { return (remAddr[nMaskFullBytes] & finalByte) == (reqAddr[nMaskFullBytes] & finalByte); } return true; } - private void assertNotHostName(String ipAddress) { - boolean isIpv4 = IPV4.matcher(ipAddress).matches(); - if (isIpv4) { - return; - } - String error = "ipAddress " + ipAddress + " doesn't look like an IP Address. Is it a host name?"; - Assert.isTrue(ipAddress.charAt(0) == '[' || ipAddress.charAt(0) == ':' - || (Character.digit(ipAddress.charAt(0), 16) != -1 && ipAddress.contains(":")), error); + private static void assertNotHostName(String ipAddress) { + Assert.isTrue(isIpAddress(ipAddress), + () -> String.format("ipAddress %s doesn't look like an IP Address. Is it a host name?", ipAddress)); + } + + private static boolean isIpAddress(String ipAddress) { + // @formatter:off + return IPV4.matcher(ipAddress).matches() + || ipAddress.charAt(0) == '[' + || ipAddress.charAt(0) == ':' + || Character.digit(ipAddress.charAt(0), 16) != -1 + && ipAddress.indexOf(':') > 0; + // @formatter:on } private InetAddress parseAddress(String address) {