1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        Springboot 中使用攔截器

        共 9495字,需瀏覽 19分鐘

         ·

        2021-02-26 10:44

        本來已收錄到我寫的10萬字Springboot經(jīng)典學(xué)習(xí)筆記中,筆記在持續(xù)更新……文末有領(lǐng)取方式

        攔截器的原理很簡單,是 AOP 的一種實現(xiàn),專門攔截對動態(tài)資源的后臺請求,即攔截對控制層的請求。使用場景比較多的是判斷用戶是否有權(quán)限請求后臺,更拔高一層的使用場景也有,比如攔截器可以結(jié)合 websocket 一起使用,用來攔截 websocket 請求,然后做相應(yīng)的處理等等。攔截器不會攔截靜態(tài)資源,Spring Boot 的默認靜態(tài)目錄為 resources/static,該目錄下的靜態(tài)頁面、js、css、圖片等等,不會被攔截(也要看如何實現(xiàn),有些情況也會攔截,我在下文會指出)。

        1. 攔截器的快速使用

        使用攔截器很簡單,只需要兩步即可:定義攔截器和配置攔截器。在配置攔截器中,Spring Boot 2.0 以后的版本和之前的版本有所不同,我會重點講解一下這里可能出現(xiàn)的坑。

        1.1 定義攔截器

        定義攔截器,只需要實現(xiàn) HandlerInterceptor 接口,HandlerInterceptor 接口是所有自定義攔截器或者 Spring Boot 提供的攔截器的鼻祖,所以,首先來了解下該接口。該接口中有三個方法: preHandle(……)、postHandle(……) 和 afterCompletion(……) 。

        preHandle(……) 方法:該方法的執(zhí)行時機是,當某個 url 已經(jīng)匹配到對應(yīng)的 Controller 中的某個方法,且在這個方法執(zhí)行之前。所以 preHandle(……) 方法可以決定是否將請求放行,這是通過返回值來決定的,返回 true 則放行,返回 false 則不會向后執(zhí)行。
        postHandle(……) 方法:該方法的執(zhí)行時機是,當某個 url 已經(jīng)匹配到對應(yīng)的 Controller 中的某個方法,且在執(zhí)行完了該方法,但是在 DispatcherServlet 視圖渲染之前。所以在這個方法中有個 ModelAndView 參數(shù),可以在此做一些修改動作。
        afterCompletion(……) 方法:顧名思義,該方法是在整個請求處理完成后(包括視圖渲染)執(zhí)行,這時做一些資源的清理工作,這個方法只有在 preHandle(……) 被成功執(zhí)行后并且返回 true 才會被執(zhí)行。

        了解了該接口,接下來自定義一個攔截器。

        /**
         * 自定義攔截器
         * @author shengwu ni
         * @date 2018/08/03
         */

        public class MyInterceptor implements HandlerInterceptor {

            private static final Logger logger = LoggerFactory.getLogger(MyInterceptor.class);

            @Override
            public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

                HandlerMethod handlerMethod = (HandlerMethod) handler;
                Method method = handlerMethod.getMethod();
                String methodName = method.getName();
                logger.info("====攔截到了方法:{},在該方法執(zhí)行之前執(zhí)行====", methodName);
                // 返回true才會繼續(xù)執(zhí)行,返回false則取消當前請求
                return true;
            }

            @Override
            public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
                logger.info("執(zhí)行完方法之后進執(zhí)行(Controller方法調(diào)用之后),但是此時還沒進行視圖渲染");
            }

            @Override
            public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
                logger.info("整個請求都處理完咯,DispatcherServlet也渲染了對應(yīng)的視圖咯,此時我可以做一些清理的工作了");
            }
        }

        OK,到此為止,攔截器已經(jīng)定義完成,接下來就是對該攔截器進行攔截配置。

        1.2 配置攔截器

        在 Spring Boot 2.0 之前,我們都是直接繼承 WebMvcConfigurerAdapter 類,然后重寫 addInterceptors 方法來實現(xiàn)攔截器的配置。但是在 Spring Boot 2.0 之后,該方法已經(jīng)被廢棄了(當然,也可以繼續(xù)用),取而代之的是 WebMvcConfigurationSupport 方法,如下:

        @Configuration
        public class MyInterceptorConfig extends WebMvcConfigurationSupport {

            @Override
            protected void addInterceptors(InterceptorRegistry registry) {
                registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
                super.addInterceptors(registry);
            }
        }

        在該配置中重寫 addInterceptors 方法,將我們上面自定義的攔截器添加進去,addPathPatterns 方法是添加要攔截的請求,這里我們攔截所有的請求。這樣就配置好攔截器了,接下來寫一個 Controller 測試一下:

        @Controller
        @RequestMapping("/interceptor")
        public class InterceptorController {

            @RequestMapping("/test")
            public String test() {
                return "hello";
            }
        }

        讓其跳轉(zhuǎn)到 hello.html 頁面,直接在 hello.html 中輸出 hello interceptor 即可。啟動項目,在瀏覽器中輸入 localhost:8080/interceptor/test 看一下控制臺的日志:

        ====攔截到了方法:test,在該方法執(zhí)行之前執(zhí)行====  
        執(zhí)行完方法之后進執(zhí)行(Controller方法調(diào)用之后),但是此時還沒進行視圖渲染  
        整個請求都處理完咯,DispatcherServlet也渲染了對應(yīng)的視圖咯,此時我可以做一些清理的工作了

        可以看出攔截器已經(jīng)生效,并能看出其執(zhí)行順序。

        1.3 解決靜態(tài)資源被攔截問題

        上文中已經(jīng)介紹了攔截器的定義和配置,但是這樣是否就沒問題了呢?其實不然,如果使用上面這種配置的話,我們會發(fā)現(xiàn)一個缺陷,那就是靜態(tài)資源被攔截了??梢栽?resources/static/ 目錄下放置一個圖片資源或者 html 文件,然后啟動項目直接訪問,即可看到無法訪問的現(xiàn)象。

        也就是說,雖然 Spring Boot 2.0 廢棄了WebMvcConfigurerAdapter,但是 WebMvcConfigurationSupport 又會導(dǎo)致默認的靜態(tài)資源被攔截,這就需要我們手動將靜態(tài)資源放開。

        如何放開呢?除了在 MyInterceptorConfig 配置類中重寫 addInterceptors 方法外,還需要再重寫一個方法:addResourceHandlers,將靜態(tài)資源放開:

        /**
         * 用來指定靜態(tài)資源不被攔截,否則繼承WebMvcConfigurationSupport這種方式會導(dǎo)致靜態(tài)資源無法直接訪問
         * @param registry
         */

        @Override
        protected void addResourceHandlers(ResourceHandlerRegistry registry) {
            registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
            super.addResourceHandlers(registry);
        }

        這樣配置好之后,重啟項目,靜態(tài)資源也可以正常訪問了。如果你是個善于學(xué)習(xí)或者研究的人,那肯定不會止步于此,沒錯,上面這種方式的確能解決靜態(tài)資源無法訪問的問題,但是,還有更方便的方式來配置。

        我們不繼承 WebMvcConfigurationSupport 類,直接實現(xiàn) WebMvcConfigurer 接口,然后重寫 addInterceptors 方法,將自定義的攔截器添加進去即可,如下:

        @Configuration
        public class MyInterceptorConfig implements WebMvcConfigurer {
            @Override
            public void addInterceptors(InterceptorRegistry registry) {
                // 實現(xiàn)WebMvcConfigurer不會導(dǎo)致靜態(tài)資源被攔截
                registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
            }
        }

        這樣就非常方便了,實現(xiàn) WebMvcConfigure 接口的話,不會攔截 Spring Boot 默認的靜態(tài)資源。

        這兩種方式都可以,具體他們之間的細節(jié),感興趣的讀者可以做進一步的研究,由于這兩種方式的不同,繼承 WebMvcConfigurationSupport 類的方式可以用在前后端分離的項目中,后臺不需要訪問靜態(tài)資源(就不需要放開靜態(tài)資源了);實現(xiàn) WebMvcConfigure 接口的方式可以用在非前后端分離的項目中,因為需要讀取一些圖片、css、js文件等等。

        2. 攔截器使用實例

        2.1 判斷用戶有沒有登錄

        一般用戶登錄功能我們可以這么做,要么往 session 中寫一個 user,要么針對每個 user 生成一個 token,第二種要更好一點,那么針對第二種方式,如果用戶登錄成功了,每次請求的時候都會帶上該用戶的 token,如果未登錄,則沒有該 token,服務(wù)端可以檢測這個 token 參數(shù)的有無來判斷用戶有沒有登錄,從而實現(xiàn)攔截功能。我們改造一下 preHandle 方法,如下:

        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            String methodName = method.getName();
            logger.info("====攔截到了方法:{},在該方法執(zhí)行之前執(zhí)行====", methodName);

            // 判斷用戶有沒有登陸,一般登陸之后的用戶都有一個對應(yīng)的token
            String token = request.getParameter("token");
            if (null == token || "".equals(token)) {
                logger.info("用戶未登錄,沒有權(quán)限執(zhí)行……請登錄");
                return false;
            }

            // 返回true才會繼續(xù)執(zhí)行,返回false則取消當前請求
            return true;
        }

        重啟項目,在瀏覽器中輸入 localhost:8080/interceptor/test 后查看控制臺日志,發(fā)現(xiàn)被攔截,如果在瀏覽器中輸入 localhost:8080/interceptor/test?token=123 即可正常往下走。

        2.2 取消攔截操作

        根據(jù)上文,如果我要攔截所有 /admin 開頭的 url 請求的話,需要在攔截器配置中添加這個前綴,但是在實際項目中,可能會有這種場景出現(xiàn):某個請求也是 /admin 開頭的,但是不能攔截,比如 /admin/login 等等,這樣的話又需要去配置。那么,可不可以做成一個類似于開關(guān)的東西,哪里不需要攔截,我就在哪里弄個開關(guān)上去,做成這種靈活的可插拔的效果呢?

        是可以的,我們可以定義一個注解,該注解專門用來取消攔截操作,如果某個 Controller 中的方法我們不需要攔截掉,即可在該方法上加上我們自定義的注解即可,下面先定義一個注解:

        /**
         * 該注解用來指定某個方法不用攔截
         */

        @Target(ElementType.METHOD)
        @Retention(RetentionPolicy.RUNTIME)
        public @interface UnInterception {
        }

        然后在 Controller 中的某個方法上添加該注解,在攔截器處理方法中添加該注解取消攔截的邏輯,如下:

        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            String methodName = method.getName();
            logger.info("====攔截到了方法:{},在該方法執(zhí)行之前執(zhí)行====", methodName);

            // 通過方法,可以獲取該方法上的自定義注解,然后通過注解來判斷該方法是否要被攔截
            // @UnInterception 是我們自定義的注解
            UnInterception unInterception = method.getAnnotation(UnInterception.class);
            if (null != unInterception) {
                return true;
            }
            // 返回true才會繼續(xù)執(zhí)行,返回false則取消當前請求
            return true;
        }

        Controller 中的方法代碼可以參見源碼,重啟項目在瀏覽器中輸入 http://localhost:8080/interceptor/test2?token=123 測試一下,可以看出,加了該注解的方法不會被攔截。

        3. 總結(jié)

        本節(jié)主要介紹了 Spring Boot 中攔截器的使用,從攔截器的創(chuàng)建、配置,到攔截器對靜態(tài)資源的影響,都做了詳細的分析。Spring Boot 2.0 之后攔截器的配置支持兩種方式,可以根據(jù)實際情況選擇不同的配置方式。最后結(jié)合實際中的使用,舉了兩個常用的場景,希望讀者能夠認真消化,掌握攔截器的使用。

        該文已收錄到我寫的《10萬字Springboot經(jīng)典學(xué)習(xí)筆記》中,點擊下面小卡片,進入【Java開發(fā)寶典】,回復(fù):筆記,即可免費獲取。

        點贊是最大的支持 

        瀏覽 50
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            a级片在线 | www.大骚逼.com | 国产大学生一区 | 伦理电影91 | wwwxxx在线观看 | 国产精品操逼网站 | 丁香五月天天操天天爽 | 天天操天天操天天操天天 | 人人操人人操人人摸 | 亚洲中文字幕图 |