背景
web开发中出于安全方面的考虑,对于后台管理项目通常会对用户访问做限制,常见的做法是硬件上设置白名单,过滤掉不需要的IP访问来保证管理平台的安全。但是在硬件操作不方便的情形之下,就需要开发人员通过程序对访问的IP进行限制,具体的实现方式可能会受到实际环境的影响而稍有不同,本文针对直接部署在tomcat下web项目(且无nginx或其他形式做代理)做一下详细说明。
实现
思路梳理
首先在web.xml中配置相应的参数,然后自定义一个filter实现Filter接口:在子定义的filter里面的init方法和doFilter方法进行处理。init方法中获取web.xml中配置的允许访问的IP白名单,并对其进行有效性验证及格式处理,定义为集合allow;doFilter方法中从请求中获取请求来源的IP地址,验证地址有效性,遍历集合allow,比较获取的来源IP是否在集合allow中存在,存在就放行,不存在response用数据流的形式返回“您的IP被限制访问!”信息给访问者,至此,IP限制部分介绍完毕。文中剩余内容是对请求路由的限制实现,也可供参考。
代码实现
web.xml配置过滤器及IP白名单
Archetype Created Web Application
loginFilter
com.bjdata.oldsite.LoginFilter
AllowIPList
192.168.0.* ;
219.143.182.110 ;
192.168.20.140-192.168.20.205
loginFilter
/*
自定义过滤器init()方法获取定义白名单
package com.bjdata.oldsite;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
/**
* @Auther:
* @Date:
* @Description:
*/
public class LoginFilter implements Filter {
// 允许的IP访问列表
private Set ipList = new HashSet();
// IP的正则
private Pattern pattern = Pattern
.compile("(1\\d{1,2}|2[0-4]\\d|25[0-5]|\\d{1,2})\\."
+ "(1\\d{1,2}|2[0-4]\\d|25[0-5]|\\d{1,2})\\."
+ "(1\\d{1,2}|2[0-4]\\d|25[0-5]|\\d{1,2})\\."
+ "(1\\d{1,2}|2[0-4]\\d|25[0-5]|\\d{1,2})");
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 从web.xml读取参数
String allowIp = filterConfig.getInitParameter("AllowIPList");
// 转换IP地址
init(allowIp);
System.err.println("Allow IP list:" + ipList);
}
/**
* 转换IP地址的形式
* @param allowIp
*/
private void init(String allowIp) {
// 192.168.0.*转换为192.168.0.1-192.168.0.255
for (String allow : allowIp.replaceAll("\\s", "").split(";")) {
if (allow.indexOf("*") > -1) {
String[] ips = allow.split("\\.");
String[] from = new String[]{"0", "0", "0", "0"};
String[] end = new String[]{"255", "255", "255", "255"};
List tem = new ArrayList<>();
for (int i = 0; i < ips.length; i++) {
if (ips[i].indexOf("*") > -1) {
tem = complete(ips[i]);
from[i] = null;
end[i] = null;
}else {
from[i] = ips[i];
end[i] = ips[i];
}
}
StringBuilder fromIP = new StringBuilder();
StringBuilder endIP = new StringBuilder();
for (int i = 0; i < 4; i++) {
if (from[i] != null) {
fromIP.append(from[i]).append(".");
endIP.append(end[i]).append(".");
}else {
fromIP.append("[*].");
endIP.append("[*].");
}
}
fromIP.deleteCharAt(fromIP.length() - 1);
endIP.deleteCharAt(endIP.length() - 1);
for (String s : tem) {
String ip = fromIP.toString().replace("[*]",
s.split(";")[0])
+ "-"
+ endIP.toString().replace("[*]", s.split(";")[1]);
if (validate(ip)) {
ipList.add(ip);
}
}
}else {
if (validate(allow)) {
ipList.add(allow);
}
}
}
}
/**
* 在添加至白名单时进行格式校验
* @param allow
* @return
*/
private boolean validate(String allow) {
for (String s : allow.split("-")){
if (!pattern.matcher(s).matches()) {
return false;
}
}
return true;
}
/**
* 对单个IP节点进行范围限定
* @param ip
* @return 返回限定后的IP范围,格式为List[10;19, 100;199]
*/
private List complete(String ip) {
List com = new ArrayList<>();
if (ip.length() == 1) {
com.add("0;255");
}else if (ip.length() == 2) {
String s1 = complete(ip, 1);
if (s1 != null){
com.add(s1);
}
String s2 = complete(ip, 2);
if (s2 != null) {
com.add(s2);
}
}else {
String s1 = complete(ip, 1);
if (s1 != null) {
com.add(s1);
}
}
return com;
}
/**
* 字符串格式处理
* @param ip
* @param length
* @return
*/
private String complete(String ip, int length) {
String from = "";
String end = "";
if (length == 1) {
from = ip.replace("*", "0");
end = ip.replace("*", "9");
}else{
from = ip.replace("*", "00");
end = ip.replace("*", "99");
}
if (Integer.valueOf(from) > 255){
return null;
}
if (Integer.valueOf(end) > 255) {
end = "255";
}
return from + ";" + end;
}
}
自定义过滤器doFilter()方法获取请求IP并验证是否属于白名单
package com.bjdata.oldsite;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
/**
* @Auther:
* @Date:
* @Description:
*/
public class LoginFilter implements Filter {
// 允许的IP访问列表
private Set ipList = new HashSet();
// IP的正则
private Pattern pattern = Pattern
.compile("(1\\d{1,2}|2[0-4]\\d|25[0-5]|\\d{1,2})\\."
+ "(1\\d{1,2}|2[0-4]\\d|25[0-5]|\\d{1,2})\\."
+ "(1\\d{1,2}|2[0-4]\\d|25[0-5]|\\d{1,2})\\."
+ "(1\\d{1,2}|2[0-4]\\d|25[0-5]|\\d{1,2})");
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)servletRequest;
HttpServletResponse response = (HttpServletResponse)servletResponse;
// 登录IP控制
String ip = request.getRemoteAddr();
InetAddress inet = null;
try {
inet = InetAddress.getLocalHost();
if (ip.equals("127.0.0.1")){
ip = inet.getHostAddress();
}
} catch (UnknownHostException e) {
e.printStackTrace();
}
if (!checkLoginIP(ip)) {
response.getOutputStream().write(("您的IP被限制访问!").getBytes());
/*response.getOutputStream().write((ip + "ip forbidden.").getBytes());*/
response.getOutputStream().flush();
return;
}
// 登录路由控制
String requestURI = request.getRequestURI();
System.out.println(requestURI);
if(request.getRequestURI().indexOf("login.jsp")<0 && !requestURI.equals("/oldSite/getValidateCode.jsp")){
HttpSession session = request.getSession();
String sessionKey = (String)session.getAttribute("sessionKey");
if(sessionKey==null){
response.sendRedirect("login.jsp");
}
}
// 放行
filterChain.doFilter(request,response);
}
/**
* 检查IP是否是允许的IP
* @param ip
* @return
*/
private boolean checkLoginIP(String ip) {
if (ipList.isEmpty() || ipList.contains(ip)) {
return true;
}else{
for (String allow : ipList) {
if (allow.indexOf("-") > -1){
String[] from = allow.split("-")[0].split("\\.");
String[] end = allow.split("-")[1].split("\\.");
String[] tag = ip.split("\\.");
// 对IP从左到右进行逐段匹配
boolean check = true;
for (int i = 0; i < 4; i++) {
int s = Integer.valueOf(from[i]);
int t = Integer.valueOf(tag[i]);
int e = Integer.valueOf(end[i]);
if (!(s <= t && t <= e)) {
check = false;
break;
}
}
if (check) {
return true;
}
}
}
}
return false;
}
}
自定义Filter中所有代码摘录如下
package com.bjdata.oldsite;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
/**
* @Auther:
* @Date:
* @Description:
*/
public class LoginFilter implements Filter {
// 允许的IP访问列表
private Set ipList = new HashSet();
// IP的正则
private Pattern pattern = Pattern
.compile("(1\\d{1,2}|2[0-4]\\d|25[0-5]|\\d{1,2})\\."
+ "(1\\d{1,2}|2[0-4]\\d|25[0-5]|\\d{1,2})\\."
+ "(1\\d{1,2}|2[0-4]\\d|25[0-5]|\\d{1,2})\\."
+ "(1\\d{1,2}|2[0-4]\\d|25[0-5]|\\d{1,2})");
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 从web.xml读取参数
String allowIp = filterConfig.getInitParameter("AllowIPList");
// 转换IP地址
init(allowIp);
System.err.println("Allow IP list:" + ipList);
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)servletRequest;
HttpServletResponse response = (HttpServletResponse)servletResponse;
// 登录IP控制
String ip = request.getRemoteAddr();
InetAddress inet = null;
try {
inet = InetAddress.getLocalHost();
if (ip.equals("127.0.0.1")){
ip = inet.getHostAddress();
}
} catch (UnknownHostException e) {
e.printStackTrace();
}
if (!checkLoginIP(ip)) {
response.getOutputStream().write(("您的IP被限制访问!").getBytes());
/*response.getOutputStream().write((ip + "ip forbidden.").getBytes());*/
response.getOutputStream().flush();
return;
}
// 登录路由控制
String requestURI = request.getRequestURI();
System.out.println(requestURI);
if(request.getRequestURI().indexOf("login.jsp")<0 && !requestURI.equals("/oldSite/getValidateCode.jsp")){
HttpSession session = request.getSession();
String sessionKey = (String)session.getAttribute("sessionKey");
if(sessionKey==null){
response.sendRedirect("login.jsp");
}
}
// 放行
filterChain.doFilter(request,response);
}
@Override
public void destroy() {
}
/**
* 转换IP地址的形式
* @param allowIp
*/
private void init(String allowIp) {
// 192.168.0.*转换为192.168.0.1-192.168.0.255
for (String allow : allowIp.replaceAll("\\s", "").split(";")) {
if (allow.indexOf("*") > -1) {
String[] ips = allow.split("\\.");
String[] from = new String[]{"0", "0", "0", "0"};
String[] end = new String[]{"255", "255", "255", "255"};
List tem = new ArrayList<>();
for (int i = 0; i < ips.length; i++) {
if (ips[i].indexOf("*") > -1) {
tem = complete(ips[i]);
from[i] = null;
end[i] = null;
}else {
from[i] = ips[i];
end[i] = ips[i];
}
}
StringBuilder fromIP = new StringBuilder();
StringBuilder endIP = new StringBuilder();
for (int i = 0; i < 4; i++) {
if (from[i] != null) {
fromIP.append(from[i]).append(".");
endIP.append(end[i]).append(".");
}else {
fromIP.append("[*].");
endIP.append("[*].");
}
}
fromIP.deleteCharAt(fromIP.length() - 1);
endIP.deleteCharAt(endIP.length() - 1);
for (String s : tem) {
String ip = fromIP.toString().replace("[*]",
s.split(";")[0])
+ "-"
+ endIP.toString().replace("[*]", s.split(";")[1]);
if (validate(ip)) {
ipList.add(ip);
}
}
}else {
if (validate(allow)) {
ipList.add(allow);
}
}
}
}
/**
* 在添加至白名单时进行格式校验
* @param allow
* @return
*/
private boolean validate(String allow) {
for (String s : allow.split("-")){
if (!pattern.matcher(s).matches()) {
return false;
}
}
return true;
}
/**
* 对单个IP节点进行范围限定
* @param ip
* @return 返回限定后的IP范围,格式为List[10;19, 100;199]
*/
private List complete(String ip) {
List com = new ArrayList<>();
if (ip.length() == 1) {
com.add("0;255");
}else if (ip.length() == 2) {
String s1 = complete(ip, 1);
if (s1 != null){
com.add(s1);
}
String s2 = complete(ip, 2);
if (s2 != null) {
com.add(s2);
}
}else {
String s1 = complete(ip, 1);
if (s1 != null) {
com.add(s1);
}
}
return com;
}
/**
* 字符串格式处理
* @param ip
* @param length
* @return
*/
private String complete(String ip, int length) {
String from = "";
String end = "";
if (length == 1) {
from = ip.replace("*", "0");
end = ip.replace("*", "9");
}else{
from = ip.replace("*", "00");
end = ip.replace("*", "99");
}
if (Integer.valueOf(from) > 255){
return null;
}
if (Integer.valueOf(end) > 255) {
end = "255";
}
return from + ";" + end;
}
/**
* 检查IP是否是允许的IP
* @param ip
* @return
*/
private boolean checkLoginIP(String ip) {
if (ipList.isEmpty() || ipList.contains(ip)) {
return true;
}else{
for (String allow : ipList) {
if (allow.indexOf("-") > -1){
String[] from = allow.split("-")[0].split("\\.");
String[] end = allow.split("-")[1].split("\\.");
String[] tag = ip.split("\\.");
// 对IP从左到右进行逐段匹配
boolean check = true;
for (int i = 0; i < 4; i++) {
int s = Integer.valueOf(from[i]);
int t = Integer.valueOf(tag[i]);
int e = Integer.valueOf(end[i]);
if (!(s <= t && t <= e)) {
check = false;
break;
}
}
if (check) {
return true;
}
}
}
}
return false;
}
}