攔截器的使用

来源:https://www.cnblogs.com/lichking2017/archive/2018/05/17/9053625.html
-Advertisement-
Play Games

穿越:從0開始,構建前後端分離應用 攔截器的作用 攔截器是web項目不可或缺的組成部分,一般使用攔截器實現以下功能 1、登錄session驗證 防止瀏覽器端繞過登錄,直接進入到應用 或者session超時後,返回到登錄頁面 2、記錄系統日誌 一個完善的應用系統,應該具備監控功能,通過完善的系統日誌記 ...


穿越:從0開始,構建前後端分離應用

攔截器的作用

攔截器是web項目不可或缺的組成部分,一般使用攔截器實現以下功能

1、登錄session驗證

  防止瀏覽器端繞過登錄,直接進入到應用

  或者session超時後,返回到登錄頁面

2、記錄系統日誌

  一個完善的應用系統,應該具備監控功能,通過完善的系統日誌記錄系統運行過程中都經歷了什麼,當發生錯誤的時候及時通知管理人員,將損失降到最低。同時通過系統日誌的監控,也能監控每次訪問的響應時長,作為性能調優的參考

3、對請求進行前置或後置的操作

  比如對於服務端返回的異常信息,可以通過攔截器統一的進行後處理,使其格式統一

攔截器的實現方式

有兩種方式

1、基於Spring AOP 的切麵方式 2、基於Servlet規範的攔截器  

實戰

下麵分享一下攔截器,在我的項目中是如何使用的。

我分別用基於Spring AOP的攔截器實現了登錄驗證及系統日誌

使用基於Servlet規範的攔截器實現了跨域請求

基於Spring AOP的攔截器-登錄驗證

實現過程

1、pom中添加依賴
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.1</version>
</dependency>
2、開啟Spring對@AspectJ的支持 在spring-mybatis.xml配置文件中,加入下麵的內容 <!--開啟Spring對@AspectJ的支持--> <aop:aspectj-autoproxy/> 當然,要先在xml文件頭部加上aop的命名空間(紅色字體部分)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> 
3、新建攔截器類 LoginInterceptor
4、在類上添加註解
     @Component :將類的實例納入到Spring 容器中管理
     @Aspect :聲明是基於@ASpectJ的註解實現
5、新建通知方法
    當應用中的方法處於切點表達式聲明的範圍內的時候,通知將被執行
6、使用@Around、@Before、@After來生命通知的類型是環繞通知、前置通知、後置通知
7、定義切點表達式

具體實現

 1 package com.wt.common.security.interceptor;
 2 
 3 import com.wt.common.core.annotations.IgnoreAuth;
 4 import com.wt.common.core.result.HttpResultEntity;
 5 import com.wt.common.core.result.HttpResultHandle;
 6 import com.wt.common.core.utils.ServletNativeObjectUtil;
 7 import com.wt.common.security.handler.HttpSessionHandler;
 8 import com.wt.common.security.model.SysUser;
 9 import org.aspectj.lang.ProceedingJoinPoint;
10 import org.aspectj.lang.annotation.Around;
11 import org.aspectj.lang.annotation.Aspect;
12 import org.aspectj.lang.reflect.MethodSignature;
13 import org.springframework.core.annotation.Order;
14 import org.springframework.stereotype.Component;
15 
16 import javax.servlet.http.HttpServletRequest;
17 import java.lang.reflect.Method;
18 
19 /**
20  * @ProjectName: syInfo
21  * @Package: com.wt.common.core.interceptor
22  * @Description:
23  * @Author: [email protected]
24  * @CreateDate: 2018/5/16 上午8:20
25  * @Version: v1.0
26  */
27 
28 @Component
29 @Order(1)
30 @Aspect
31 public class LoginInterceptor {
32 //    Logger logger = LoggerFactory.getLogger(LoginInterceptor.class);
33 
34     @Around("@within(org.springframework.web.bind.annotation.RestController)")
35     public HttpResultEntity loginCheck(ProceedingJoinPoint pjp) throws Throwable {
36         HttpServletRequest request = ServletNativeObjectUtil.getRequest();
37         SysUser loginUser = (SysUser) request.getSession().getAttribute(HttpSessionHandler.Items.LOGINUSER.name());
38 
39         final MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
40         final Method method = methodSignature.getMethod();
41         boolean ignoreAuth = method.isAnnotationPresent(IgnoreAuth.class);
42 
43         if ((null == loginUser)&&!ignoreAuth) {
44             return new HttpResultEntity(HttpResultHandle.HttpResultEnum.NOTLOG);
45         }
46         return (HttpResultEntity) pjp.proceed();
47     }
48 }
View Code

一些說明

在上述過程中需要理解一下的有以下兩點
1、切點表達式
@within(org.springframework.web.bind.annotation.RestController)
它的意思代表了,通知的範圍是只要有類添加了@RestController的註解,那麼類中的方法,只要被調用,都會執行相應的通知
2、為什麼這麼配置呢?
為什麼這麼配置:因為我的項目是基於SpringMVC框架的,並且使用的請求都是基於Restful規範的。所以所有的Action都會配置@RestController這個註解,也就是說,所有的後臺請求,
3、上述配置要完成的功能是什麼?
如果用戶沒有登錄,那麼請求就會被打回,併在頁面上給與用戶提示
4、對於@Around環繞通知的執行過程是什麼樣的?
正常流:瀏覽器發起請求-》通知被執行-》在通知的內部,根據業務邏輯判斷,該請求是否合法,也就是前置的一些處理,如果合法調用pjp.proceed()方法-》進入controller的方法執行,執行完成後-》返回到通知內部,繼續執行pjp.proceed()後面的代碼-》返回客戶端
異常流:瀏覽器發起請求-》通知被執行-》在通知的內部,根據業務邏輯判斷,該請求是否合法,也就是前置的一些處理,如果不合法,直接return-》瀏覽器顯示處理結果

關於@AspectJ的相關知識就不再這裡介紹了,感興趣的朋友可以查看:@Aspect註解教程

基於Spring AOP的攔截器-系統日誌

具體實現

  1 package com.wt.common.security.interceptor;
  2 
  3 
  4 import com.google.gson.Gson;
  5 import com.wt.common.core.exception.BaseErrorException;
  6 import com.wt.common.core.exception.BaseLogicException;
  7 import com.wt.common.core.result.HttpResultEntity;
  8 import com.wt.common.core.result.HttpResultHandle;
  9 import com.wt.common.core.utils.ServletNativeObjectUtil;
 10 import com.wt.common.security.handler.HttpSessionHandler;
 11 import com.wt.common.security.model.SysUser;
 12 import com.wt.common.security.model.SyslogPerformance;
 13 import com.wt.common.security.service.SyslogPerformanceService;
 14 import org.apache.commons.lang3.StringUtils;
 15 import org.aspectj.lang.ProceedingJoinPoint;
 16 import org.aspectj.lang.annotation.Around;
 17 import org.aspectj.lang.annotation.Aspect;
 18 import org.slf4j.Logger;
 19 import org.slf4j.LoggerFactory;
 20 import org.springframework.beans.factory.annotation.Autowired;
 21 import org.springframework.core.annotation.Order;
 22 import org.springframework.stereotype.Component;
 23 
 24 import javax.servlet.http.HttpServletRequest;
 25 
 26 /**
 27  * @ProjectName: syInfo
 28  * @Package: com.wt.common.core.interceptor
 29  * @Description:
 30  * @Author: [email protected]
 31  * @CreateDate: 2018/5/16 下午4:14
 32  * @Version: v1.0
 33  */
 34 
 35 @Component
 36 @Aspect
 37 @Order(2)
 38 public class LogInterceptor {
 39 
 40     Logger logger = LoggerFactory.getLogger(LoginInterceptor.class);
 41 
 42     @Autowired
 43     private SyslogPerformanceService syslogPerformanceService;
 44 
 45 
 46     @Around("@within(org.springframework.web.bind.annotation.RestController)")
 47     public HttpResultEntity logRecord(ProceedingJoinPoint pjp) {
 48         Gson gson = new Gson();
 49         HttpServletRequest request = ServletNativeObjectUtil.getRequest();
 50         SyslogPerformance syslogPerformance = this.setLog(request);
 51         syslogPerformance.setParameters(gson.toJson(pjp.getArgs()));
 52 
 53         long startTime = System.currentTimeMillis(), endTime = 0, consume = 0;
 54 
 55         String requestInfo = String.format("⭐️{User-Agent:[%s],Protocol:[%s],Remote Addr:[%s],Method:[%s],uri:[%s],Cookie:[%s],operator:[%s],parameters:[%s]}⭐️",
 56                 request.getHeader("User-Agent"), request.getProtocol(), request.getRemoteAddr(),
 57                 request.getMethod(), request.getRequestURI(), request.getHeader("Cookie"),
 58                 "ceshi",
 59                 gson.toJson(pjp.getArgs()));
 60         try {
 61             HttpResultEntity result = (HttpResultEntity) pjp.proceed();
 62             endTime = System.currentTimeMillis();
 63             logger.info(requestInfo);
 64             return result;
 65         } catch (Throwable throwable) {
 66             endTime = System.currentTimeMillis();
 67             if (throwable instanceof BaseLogicException) {
 68                 String errorMessage = ((BaseLogicException) throwable).getExceptionBody().getMessage();
 69                 String errorCode = ((BaseLogicException) throwable).getExceptionBody().getMessage();
 70                 logger.error(StringUtils.join(requestInfo, errorMessage), throwable);
 71                 return HttpResultHandle.getErrorResult(errorCode, errorMessage);
 72             }
 73             if (throwable instanceof BaseErrorException) {
 74                 logger.error(StringUtils.join(requestInfo, throwable.getMessage()), throwable);
 75                 return HttpResultHandle.getErrorResult();
 76             }
 77 
 78             logger.error(StringUtils.join(requestInfo, throwable.getMessage()), throwable);
 79             return HttpResultHandle.getErrorResult();
 80 
 81         } finally {
 82             consume = endTime - startTime;
 83             syslogPerformance.setTimeConsuming(String.valueOf(consume));
 84             syslogPerformanceService.save(syslogPerformance);
 85         }
 86     }
 87 
 88     private SyslogPerformance setLog(HttpServletRequest request) {
 89         SysUser currentUser = (SysUser) request.getSession().getAttribute(HttpSessionHandler.Items.LOGINUSER.name());
 90         SyslogPerformance syslogPerformance = new SyslogPerformance();
 91         syslogPerformance
 92                 .setRemoteHost(request.getRemoteHost())
 93                 .setRemotePort(request.getRemotePort())
 94                 .setRequestType(request.getMethod())
 95                 .setRequestURI(request.getRequestURI());
 96         if(currentUser!=null){
 97             syslogPerformance.setOperatorId(currentUser.getUserId()).setOperatorName(currentUser.getUserName());
 98         }
 99         return syslogPerformance;
100     }
101 }
View Code

一些說明

1、如果後臺的請求執行正常,那麼放行並記錄日誌

2、如果出現錯誤,同一處理結果,並返回結果到瀏覽器

3、無論處理過程是否異常,都會記錄到資料庫表當中

效果

1、功能如下圖,每當一次請求被執行,在日誌表中都會進行記錄,包括時長,及時間。可以再擴展一下,加上操作人

 

 

基於Servlet規範的攔截器-跨域請求

實現過程

1、新建攔截器類CrossDomainInterceptor,並繼承自HandlerInterceptor 2、對攔截器進行配置,在spring配置文件中,添加下麵的內容
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <mvc:exclude-mapping path="/index.html"/>
            <bean class="com.wt.common.core.interceptor.CrossDomainInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

</beans>
3、重寫以下方法     preHandle:在請求調用之前調用     postHandle:在請求執行完成,且返回視圖渲染之前調用     afterCompletion:在請求執行完成,並且完成視圖渲染之後執行

具體實現

 1 package com.wt.common.core.interceptor;
 2 
 3 import org.slf4j.Logger;
 4 import org.slf4j.LoggerFactory;
 5 import org.springframework.core.annotation.Order;
 6 import org.springframework.web.servlet.HandlerInterceptor;
 7 
 8 import javax.servlet.http.HttpServletRequest;
 9 import javax.servlet.http.HttpServletResponse;
10 
11 /**
12  * @ProjectName: syInfo
13  * @Package: com.wt.common.core.interceptor
14  * @Description:
15  * @Author: [email protected]
16  * @CreateDate: 2018/5/15 下午11:21
17  * @Version: v1.0
18  */
19 @Order(1)
20 public class CrossDomainInterceptor implements HandlerInterceptor {
21     Logger logger = LoggerFactory.getLogger(CrossDomainInterceptor.class);
22     @Override
23     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
24         response.setHeader("Access-Control-Allow-Headers", "X-Requested-With, accept, content-type, xxxx");
25         response.setHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH");
26         response.setHeader("Access-Control-Allow-Origin", "*");
27         response.setHeader("Access-Control-Allow-Credentials", "true");
28         return true;
29     }
30 }

一些說明

1、這個比較簡單,沒什麼太多說的地方,註意方法的返回值即可,根據項目的業務邏輯,如果請求通行,那麼就return true,否則返回false。
2、如果有多個攔截器,執行順序會按照攔截器在spring配置文件中聲明的先後順序執行,執行過程如下
    如果有A、B兩個攔截器,A聲明在先,B聲明在後,執行順序為
    A.preHandle-》B.preHandle-》B.postHandle-》A.postHandle

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在iPhone 6還沒出的時候,都是用640×1136 px來做設計稿的,自從6的發佈,所有的設計稿尺寸以750×1334 px來做設計稿尺寸 以750x1334px作為設計稿標準尺寸的原由: 所以做設計稿事請以750x1334px來做設計稿 iPhone界面設計規範: iPhone 界面尺寸: 左 ...
  • 本篇內容是自學自記,現在我還不知道Service Fabric究竟是怎麼個入門法,反正按照入門教程先進行本地Service Fabric集群的安裝,萬里路始於足下,要學習總得先把環境裝好了才能開始學習是不? 首先是先決條件,具體可見 https://docs.microsoft.com/zh-cn/ ...
  • 緣起 翻開清單,一條條計劃一直列在那裡,一天又一天,不知道什麼時候寫下了它,也知不道什麼時候完成它,它一直在那靜靜的等待著。 靜下心來,反思自己,才發現自己是多麼的無知,多麼的沒有毅力。設定了無數目標,指定了無數計劃,但是到頭來呢?都是在無盡的嘆息中,放棄了定下的目標。 堅持 只是每天不斷在內心迴響 ...
  • lavaan簡明教程 [中文翻譯版] 譯者註:此文檔原作者為比利時Ghent大學的Yves Rosseel博士,lavaan亦為其開發,完全開源、免費。我在學習的時候順手翻譯了一下,向Yves的開源精神致敬。此翻譯因偷懶部分刪減,但也有增加,有錯誤請留言 「轉載請註明出處」 目錄 lavaan簡明教 ...
  • 恢復內容開始 作者 : liuyang0 來源 : 博客園 常見排序演算法總結與實現 本文使用Java實現這幾種排序。以下是對排序演算法總體的介紹。 冒泡排序 時間複雜度:O(n^2),最優時間複雜度:O(n),平均時間複雜度:O(n^2) 插入排序 時間複雜度:O(n^2),最優時間複雜度:O(n), ...
  • 一、初始Redis 1、Redis特性與優點 速度快。redis所有數據都存放於記憶體;是用C語言實現,更加貼近硬體;使用了單線程架構,避免了多線程競爭問題 基於鍵值對的數據結構,支持的數據結構豐富。它主要提供了5種數據結構: 字元串、 哈希、 列表、 集合、 有序集合, 同時在字元串的基礎之上演變出 ...
  • 一:java不同進位數據的表現形式 二進位表示形式前面加0b(b可以大寫也可以小寫) 八進位表示形式前面加0 十六進位表示形式前面加0x 任意進位到十進位的轉換圖解 十進位到十進位的轉換 12345 = 10000 + 2000 +300 + 40 + 5 =1 *10^4 + 2 *10^3 + ...
  • 使用DOM解析xml文件 要解析的xml文件如下: 解析xml的代碼如下: 基本步驟差不多就是: 首先是先獲得根元素 然後getElementByTagName()獲得相應標簽的node集合 通過集合,如list中的item()獲得集合中的具體元素 最後根據getTextContent()獲得具體元 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...