001/* 002 * Copyright 2002-2016 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.apache.shiro.web.filter.authz; 017 018import java.net.InetAddress; 019import java.net.UnknownHostException; 020import java.util.Arrays; 021 022/** 023 * Matches a request based on IP Address or subnet mask matching against the remote 024 * address. 025 * <p> 026 * Both IPv6 and IPv4 addresses are supported, but a matcher which is configured with an 027 * IPv4 address will never match a request which returns an IPv6 address, and vice-versa. 028 * 029 * @see <a href="https://github.com/spring-projects/spring-security/blob/master/web/src/main/java/ 030 * org/springframework/security/web/util/matcher/IpAddressMatcher.java">Original Spring Security version</a> 031 * @since 2.0 032 */ 033public final class IpAddressMatcher { 034 private final int nMaskBits; 035 private final InetAddress requiredAddress; 036 037 /** 038 * Takes a specific IP address or a range specified using the IP/Netmask (e.g. 039 * 192.168.1.0/24 or 202.24.0.0/14). 040 * 041 * @param ipAddress the address or range of addresses from which the request must 042 * come. 043 */ 044 public IpAddressMatcher(String ipAddress) { 045 int i = ipAddress.indexOf('/'); 046 if (i > 0) { 047 nMaskBits = Integer.parseInt(ipAddress.substring(i + 1)); 048 ipAddress = ipAddress.substring(0, i); 049 } else { 050 nMaskBits = -1; 051 } 052 requiredAddress = parseAddress(ipAddress); 053 } 054 055 @SuppressWarnings("checkstyle:MagicNumber") 056 public boolean matches(String address) { 057 InetAddress remoteAddress = parseAddress(address); 058 059 if (!requiredAddress.getClass().equals(remoteAddress.getClass())) { 060 return false; 061 } 062 063 if (nMaskBits < 0) { 064 return remoteAddress.equals(requiredAddress); 065 } 066 067 byte[] remAddr = remoteAddress.getAddress(); 068 byte[] reqAddr = requiredAddress.getAddress(); 069 070 int oddBits = nMaskBits % 8; 071 int nMaskBytes = nMaskBits / 8 + (oddBits == 0 ? 0 : 1); 072 byte[] mask = new byte[nMaskBytes]; 073 074 Arrays.fill(mask, 0, oddBits == 0 ? mask.length : mask.length - 1, (byte) 0xFF); 075 076 if (oddBits != 0) { 077 int finalByte = (1 << oddBits) - 1; 078 finalByte <<= 8 - oddBits; 079 mask[mask.length - 1] = (byte) finalByte; 080 } 081 082 for (int i = 0; i < mask.length; i++) { 083 if ((remAddr[i] & mask[i]) != (reqAddr[i] & mask[i])) { 084 return false; 085 } 086 } 087 088 return true; 089 } 090 091 private InetAddress parseAddress(String address) { 092 try { 093 return InetAddress.getByName(address); 094 } catch (UnknownHostException e) { 095 throw new IllegalArgumentException("Failed to parse address" + address, e); 096 } 097 } 098}