SpringBoot自定义拦截器实现IP白名单功能

简介: SpringBoot自定义拦截器实现IP白名单功能转载请注明源地址:http://www.cnblogs.com/funnyzpc/p/8993331.html  首先,相关功能已经上线了,且先让我先祷告一番:    阿门~ (-__-)  额,正文开始前我先说两句吧,能完成这个功能十分感...

SpringBoot自定义拦截器实现IP白名单功能

转载请注明源地址:http://www.cnblogs.com/funnyzpc/p/8993331.html

  首先,相关功能已经上线了,且先让我先祷告一番:  

  阿门~ (-__-)

  额,正文开始前我先说两句吧,能完成这个功能十分感谢csdn网友的一篇帖子的帮助,在此深表以感谢

  这位朋友的源贴也很不错,如觉得我写的不好,可以移步这里:https://blog.csdn.net/u011244202/article/details/54895038

  先,我简要的说下这样做的原因,公司现在的主体架构是thinkphp的,由于php的开发人员走的已经差不多了再加上php岗位一时半会也很难补上,另外工头也是写java的...,算是元婴种种吧,现在所有的主体功能代码都慢慢的往java方向迁移,同时,请注意,java的开发人员目前来看只有我一个,遂架构迁移只能以springboot模块功能的方式逐步走,前端的架构刚开始走,又不能上线,只能将开发出来的模块给php后端调用,两种语言的后端数据交互又不能太复杂(就是不能加Auth),否则会加重php与java数据交互的成本,遂,就有了在springboot端做ip过滤。

  额,我仔细分析了下,实现此功能目前大致可有三种方式:

    A>因为spring框架中每一个controller就是一个拦截器,遂,可以在每个拦截器里面加ip过滤,显而易见的问题是=>代码会过于冗余,不利于维护

    B>可以在springboot提供的拦截器里面做,这样。。。,可能是比较合适的,但似乎也会存在代码冗余的问题

    C>可以在各个模块的顶部使用拦截器组件,比如Zuul,。。。问题是我们现在的框架还没完善到这一步

  其实,如框架比较完善的情况下,以上方式都不太好,最好的是将前端分离,直接调用java,登陆校验等使用OAuth2来做签名认证。

  欸~,可能由于个人能力有限,我就说说使用springboot自带的拦截器做的功能吧。

  放代码:

 1 package com.github.carvechris.security.common.util;
 2 
 3 import javax.servlet.http.HttpServletRequest;
 4 
 5 /**
 6  * CREATE BY funnyZpC ON 2018/5/3
 7  **/
 8 public class IPUtils {
 9     /**
10      * 获取用户真实IP地址,不使用request.getRemoteAddr()的原因是有可能用户使用了代理软件方式避免真实IP地址,
11      * 可是,如果通过了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP值
12      *
13      * @return ip
14      */
15     public static String getRealIP(HttpServletRequest request) {
16         String ip = request.getHeader("x-forwarded-for");
17         if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
18             // 多次反向代理后会有多个ip值,第一个ip才是真实ip
19             if( ip.indexOf(",")!=-1 ){
20                 ip = ip.split(",")[0];
21             }
22         }
23         if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
24             ip = request.getHeader("Proxy-Client-IP");
25             System.out.println("Proxy-Client-IP ip: " + ip);
26         }
27         if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
28             ip = request.getHeader("WL-Proxy-Client-IP");
29             System.out.println("WL-Proxy-Client-IP ip: " + ip);
30         }
31         if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
32             ip = request.getHeader("HTTP_CLIENT_IP");
33             System.out.println("HTTP_CLIENT_IP ip: " + ip);
34         }
35         if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
36             ip = request.getHeader("HTTP_X_FORWARDED_FOR");
37             System.out.println("HTTP_X_FORWARDED_FOR ip: " + ip);
38         }
39         if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
40             ip = request.getHeader("X-Real-IP");
41             System.out.println("X-Real-IP ip: " + ip);
42         }
43         if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
44             ip = request.getRemoteAddr();
45             System.out.println("getRemoteAddr ip: " + ip);
46         }
47         return ip;
48     }
49 }

由于每次校验ip的时候是从请求头里面获取的(HttpSevletRequest),请求头里面的ip多种多样,比如在使用了nginx反向代理的时候ip就很丰富了,遂我就写了个IPUtils单独做这个事情。

  ,接下来就将这个Utils用起来,开始配置拦截器

 1 package com.github.carvechris.security.bankFlow.config;
 2 
 3 import com.github.carvechris.security.bankFlow.entity.ZwIpFilter;
 4 import com.github.carvechris.security.bankFlow.mapper.ZwIpFilterMapper;
 5 import com.github.carvechris.security.common.util.IPUtils;
 6 import org.apache.commons.lang3.StringUtils;
 7 import org.apache.log4j.Logger;
 8 import org.springframework.beans.factory.annotation.Autowired;
 9 import org.springframework.web.servlet.HandlerInterceptor;
10 import org.springframework.web.servlet.ModelAndView;
11 
12 import javax.servlet.http.HttpServletRequest;
13 import javax.servlet.http.HttpServletResponse;
14 import java.util.List;
15 
16 /**
17  * CREATE BY funnyZpC ON 2018/5/3
18  **/
19 
20 public class IPInterceptor implements HandlerInterceptor {
21     private static final Logger LOG= Logger.getLogger(IPInterceptor.class.getName());
22 
23 
24     @Autowired
25     private ZwIpFilterMapper ipFilterMapper;
26 
27     private ZwIpFilter ipFilter;
28 
29     @Override
30     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
31         //过滤ip,若用户在白名单内,则放行
32         String ipAddress=IPUtils.getRealIP(request);
33         LOG.info("USER IP ADDRESS IS =>"+ipAddress);
34         if(!StringUtils.isNotBlank(ipAddress))
35             return false;
36         ipFilter=new ZwIpFilter();
37         ipFilter.setModule("sino-bankflow");//模块
38         ipFilter.setIp(ipAddress);//ip地址
39         ipFilter.setMark(0);//白名单
40         List<ZwIpFilter> ips=ipFilterMapper.select(ipFilter);
41         if(ips.isEmpty()){
42             response.getWriter().append("<h1 style=\"text-align:center;\">Not allowed!</h1>");
43             return false;
44         }
45         return true;
46     }
47 
48 
49     @Override
50     public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
51 
52     }
53 
54 
55     @Override
56     public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
57 
58     }
59 }

拦截器不是随随便便写的,需要使用SpringBoot提供的拦截器(HandlerInterceptor)模板来做,拦截器是什么=>"切面编程",另外需要说明的是这三个方法:

  preHandle=>请求处理前拦截,处理通过返回true,否则返回false不进行处理

  postHandle=>请求处理后拦截(页面渲染前),处理通过返回true,否则返回false

  afterCompletion=>请求处理后拦截,(同上)

 好了,既然已经清楚了,也就是在请求处理前拦截过滤IP,对于上面代码需要说明的是>由于使用的是Mybatis的方式实现DB操作,故注入ZwIpFilterMapper,将ip黑白名单放在数据库,可随时修改使用,额,我把ZwIpFilter这个对象的表结构给下吧。

  拦截器定义OK了,但是并不能实现拦截,现在已经做好的只是按照springboot拦截器HandlerInterceptor定义好了拦截器的实现模板,遂,现在要做的是将拦截器放入spring上下文中,以实现启动即可拦截,思路是,先将实现的拦截器功能定义为一个Bean,然后将这个Bean放入到拦截器注册中心(InterceptorRegistry),同时再添加一个拦截前缀即可。我的实现代码和网友的方式大致无二,大家可根据自己的喜好进行调整以符合业务要求:

 1 package com.github.carvechris.security.bankFlow.config;
 2 
 3 /*import com.github.carvechris.security.auth.client.interceptor.ServiceAuthRestInterceptor;
 4 import com.github.carvechris.security.auth.client.interceptor.UserAuthRestInterceptor;
 5 */
 6 
 7 import com.github.carvechris.security.common.handler.GlobalExceptionHandler;
 8 import org.springframework.context.annotation.Bean;
 9 import org.springframework.context.annotation.Configuration;
10 import org.springframework.context.annotation.Primary;
11 import org.springframework.web.servlet.HandlerInterceptor;
12 import org.springframework.web.servlet.config.annotation.CorsRegistry;
13 import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
14 import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
15 
16 /*import org.springframework.web.servlet.config.annotation.InterceptorRegistry;*/
17 
18 /**
19  * Created by funnyZpC on 2017/9/8.
20  */
21 @Configuration("admimWebConfig")
22 @Primary
23 public class WebConfiguration extends WebMvcConfigurerAdapter {
24     @Bean
25     GlobalExceptionHandler getGlobalExceptionHandler() {
26         return new GlobalExceptionHandler();
27     }
28 
29     //将自定义的拦截器定义为一个bean
30     @Bean
31     public HandlerInterceptor getMyInterceptor(){
32         return new IPInterceptor();
33     }
34 
35     @Override
36     public void addInterceptors(InterceptorRegistry registry){
37         // 多个拦截器组成一个拦截器链
38         // addPathPatterns 用于添加拦截规则, 这里假设拦截 /** 后面的全部链接
39         // excludePathPatterns 用户排除拦截
40         registry.addInterceptor(getMyInterceptor()).addPathPatterns("/**");
41         super.addInterceptors(registry);
42     }
43 
44 }

  最后,根据写的功能测试下,先我将原有的ip定义为127.0.0.3

试试:

显示不允许进入,说明功能已经成功了,现在我们来看看从控制台打印的ip信息:

打印的ip是127.0.0.1,这是本机IP,OK,现在还不能看出功能是否完全ok,我就测试下同局域网的手机请求看看,需要根据服务端ip做请求,服务端ip是:

故,我的请求地址应该是:http://11.11.11.239:8080/,看下控制台和页面输出:

由于我没有将手机的ip添加至数据库,遂显示“Not allowed!"说明功能正常ヽ(ˋ▽ˊ)ノ

 

其实,功能已经在生产服务器调试了,事实上问题还多着呢,比如我们现有的java应用全部放在docker里面,获取的ip全部是docker的内网ip,至于这个问题不通没法测NGINX代理环境下的ip情况,以及阿里云的负载均衡下的ip代理问题,欸~,革命仍未成功,同志仍需努力呀。。。

  END,现在是2018-05-12 19:41:11,晚饭时间,晚饭后我还有已有一贴要写

 

funnyzpc@gmail.com
相关文章
|
4天前
|
消息中间件 缓存 Java
手写模拟Spring Boot启动过程功能
【11月更文挑战第19天】Spring Boot自推出以来,因其简化了Spring应用的初始搭建和开发过程,迅速成为Java企业级应用开发的首选框架之一。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,帮助读者深入理解其工作机制。
21 3
|
4天前
|
Java 开发者 微服务
手写模拟Spring Boot自动配置功能
【11月更文挑战第19天】随着微服务架构的兴起,Spring Boot作为一种快速开发框架,因其简化了Spring应用的初始搭建和开发过程,受到了广大开发者的青睐。自动配置作为Spring Boot的核心特性之一,大大减少了手动配置的工作量,提高了开发效率。
20 0
|
1月前
|
Java API 数据库
构建RESTful API已经成为现代Web开发的标准做法之一。Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐。
【10月更文挑战第11天】本文介绍如何使用Spring Boot构建在线图书管理系统的RESTful API。通过创建Spring Boot项目,定义`Book`实体类、`BookRepository`接口和`BookService`服务类,最后实现`BookController`控制器来处理HTTP请求,展示了从基础环境搭建到API测试的完整过程。
42 4
|
1月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,包括版本兼容性、安全性、性能调优等方面。
143 1
|
28天前
|
Java API 数据库
Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐
本文通过在线图书管理系统案例,详细介绍如何使用Spring Boot构建RESTful API。从项目基础环境搭建、实体类与数据访问层定义,到业务逻辑实现和控制器编写,逐步展示了Spring Boot的简洁配置和强大功能。最后,通过Postman测试API,并介绍了如何添加安全性和异常处理,确保API的稳定性和安全性。
35 0
|
17天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。首先,创建并配置 Spring Boot 项目,实现后端 API;然后,使用 Ant Design Pro Vue 创建前端项目,配置动态路由和菜单。通过具体案例,展示了如何快速搭建高效、易维护的项目框架。
95 62
|
14天前
|
前端开发 Java easyexcel
SpringBoot操作Excel实现单文件上传、多文件上传、下载、读取内容等功能
SpringBoot操作Excel实现单文件上传、多文件上传、下载、读取内容等功能
54 8
|
15天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,帮助开发者提高开发效率和应用的可维护性。
34 2
|
19天前
|
JSON Java API
springboot集成ElasticSearch使用completion实现补全功能
springboot集成ElasticSearch使用completion实现补全功能
23 1
|
23天前
|
存储 Java 数据管理
强大!用 @Audited 注解增强 Spring Boot 应用,打造健壮的数据审计功能
本文深入介绍了如何在Spring Boot应用中使用`@Audited`注解和`spring-data-envers`实现数据审计功能,涵盖从添加依赖、配置实体类到查询审计数据的具体步骤,助力开发人员构建更加透明、合规的应用系统。