SpringBoot自定義攔截器實現IP白名單功能

SpringBoot自定義攔截器實現IP白名單功能php

轉載請註明源地址:http://www.cnblogs.com/funnyzpc/p/8993331.htmlhtml

  首先,相關功能已經上線了,且先讓我先禱告一番:  前端

  阿門~ (-__-)java

  額,正文開始前我先說兩句吧,能完成這個功能十分感謝csdn網友的一篇帖子的幫助,在此深表以感謝nginx

  這位朋友的源貼也很不錯,如以爲我寫的很差,能夠移步這裏:https://blog.csdn.net/u011244202/article/details/54895038git

  先,我簡要的說下這樣作的緣由,公司如今的主體架構是thinkphp的,因爲php的開發人員走的已經差很少了再加上php崗位一時半會也很難補上,另外工頭也是寫java的...,算是元嬰種種吧,如今全部的主體功能代碼都慢慢的往java方向遷移,同時,請注意,java的開發人員目前來看只有我一個😅,遂架構遷移只能以springboot模塊功能的方式逐步走,前端的架構剛開始走,又不能上線,只能將開發出來的模塊給php後端調用,兩種語言的後端數據交互又不能太複雜(就是不能加Auth),不然會加劇php與java數據交互的成本,遂,就有了在springboot端作ip過濾。github

  額,我仔細分析了下,實現此功能目前大體可有三種方式:web

    A>由於spring框架中每個controller就是一個攔截器,遂,能夠在每一個攔截器裏面加ip過濾,顯而易見的問題是=>代碼會過於冗餘,不利於維護spring

    B>能夠在springboot提供的攔截器裏面作,這樣。。。,多是比較合適的,但彷佛也會存在代碼冗餘的問題docker

    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,晚飯時間,晚飯後我還有已有一貼要寫

相關文章
相關標籤/搜索