Spring 攔截器——HandlerInterceptor

原創(chuàng)性聲明:本文完全為筆者原創(chuàng),請(qǐng)尊重筆者勞動(dòng)力。轉(zhuǎn)載務(wù)必注明原文地址。

今天在做項(xiàng)目的時(shí)候偶遇一個(gè)業(yè)務(wù)需求:用戶登錄后要對(duì)用戶密碼強(qiáng)度進(jìn)行判斷,密碼過(guò)期的話不強(qiáng)制登出,而只強(qiáng)制用戶跳轉(zhuǎn)到密碼修改頁(yè)面,而不能進(jìn)行其他操作,包括后臺(tái)的api的請(qǐng)求。

注意:此時(shí)用戶仍然處于登錄狀態(tài),因此登錄的后端api權(quán)限驗(yàn)證此時(shí)都是授權(quán)的。

因此,采用Spring攔截器的方式進(jìn)行業(yè)務(wù)處理。HandlerInterceptor攔截器常見(jiàn)的用途有:

1、日志記錄:記錄請(qǐng)求信息的日志,以便進(jìn)行信息監(jiān)控、信息統(tǒng)計(jì)、計(jì)算PV(Page View)等。
2、權(quán)限檢查:如登錄檢測(cè),進(jìn)入處理器檢測(cè)檢測(cè)是否登錄,如果沒(méi)有直接返回到登錄頁(yè)面;
3、性能監(jiān)控:有時(shí)候系統(tǒng)在某段時(shí)間莫名其妙的慢,可以通過(guò)攔截器在進(jìn)入處理器之前記錄開(kāi)始時(shí)間,在處理完后記錄結(jié)束時(shí)間,從而得到該請(qǐng)求的處理時(shí)間(如果有反向代理,如apache可以自動(dòng)記錄);
4、通用行為:讀取cookie得到用戶信息并將用戶對(duì)象放入請(qǐng)求,從而方便后續(xù)流程使用,還有如提取Locale、Theme信息等,只要是多個(gè)處理器都需要的即可使用攔截器實(shí)現(xiàn)。
5、OpenSessionInView:如Hibernate,在進(jìn)入處理器打開(kāi)Session,在完成后關(guān)閉Session。
…………本質(zhì)也是AOP(面向切面編程),也就是說(shuō)符合橫切關(guān)注點(diǎn)的所有功能都可以放入攔截器實(shí)現(xiàn)。

廢話不多說(shuō),直接上代碼,代碼的理解放在注釋里。

code(PasswordStateInterceptor.java):

package com.gildata.gup.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import com.gildata.gup.domain.User;
import com.gildata.gup.domain.UserEncryptReset;
import com.gildata.gup.repository.UserEncryptResetRepository;

@Component // 不可少
public class PasswordStateInterceptor implements HandlerInterceptor { // 必須實(shí)現(xiàn)HandlerInterceptor接口

    @Autowired
    private UserEncryptResetRepository userEncryptResetRepository;//用戶密碼狀態(tài)的查詢的DAO類
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        // TODO Auto-generated method stub
        SecurityContext securityContext =  (SecurityContext) request.getSession().getAttribute("SPRING_SECURITY_CONTEXT"); // 獲取session中的SecurityContext對(duì)象,它包含了用戶的信息
        User user = (User) securityContext.getAuthentication().getPrincipal();
        if ( !user.equals(null) ) { // 說(shuō)明已經(jīng)登錄了
            UserEncryptReset uer = userEncryptResetRepository.findOneByUsername(user.getUsername()); //查詢記錄用戶密碼狀態(tài)的對(duì)象
            if ((uer != null) && (uer.getPasswordState().equals("1"))) { //uer存在并且用戶密碼狀態(tài)為過(guò)期狀態(tài),才不允許放行
                response.sendRedirect("/#/passwordReset"); // 將用戶
                return false;
            }
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        // TODO Auto-generated method stub
        return;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        // TODO Auto-generated method stub
    }
}

有了攔截器PasswordStateInterceptor,還需要對(duì)攔截器進(jìn)行注冊(cè)。需要使用WebMvcConfigurerAdapter 下的addInterceptors方法。 新建一個(gè)類WebConfigfilter.java,繼承自WebMvcConfigurerAdapter

code(WebConfigfilter.java):

package com.gildata.gup.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import com.gildata.gup.interceptor.PasswordStateInterceptor;

@Configuration // 配置
public class WebConfigfilter extends WebMvcConfigurerAdapter{
    
    @Autowired
    private PasswordStateInterceptor passwordStateInterceptor; // 實(shí)例化攔截器

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // super.addInterceptors(registry);
        // 注冊(cè)自定義的攔截器passwordStateInterceptor
        registry.addInterceptor(passwordStateInterceptor)
            .addPathPatterns("/api/*") //匹配要過(guò)濾的路徑
            .excludePathPatterns("/api/changePasswordByUser/*") //匹配不過(guò)濾的路徑。密碼還要修改呢,所以這個(gè)路徑不能攔截
            .excludePathPatterns("/api/passwordStateValid") //密碼狀態(tài)驗(yàn)證也不能攔截
            .excludePathPatterns("/api/getManagerVersion");//版本信息同樣不能攔截
    }
}

此時(shí)攔截器就會(huì)生效了。
下面重點(diǎn)講解攔截器下的三個(gè)重載方法:

  • preHandle
  • postHandle
  • afterCompletion

(1)preHandle (HttpServletRequest request, HttpServletResponse response, Object handle) 方法,顧名思義,該方法將在請(qǐng)求處理之前進(jìn)行調(diào)用。SpringMVC 中的Interceptor 是鏈?zhǔn)降恼{(diào)用的,在一個(gè)應(yīng)用中或者說(shuō)是在一個(gè)請(qǐng)求中可以同時(shí)存在多個(gè)Interceptor 。每個(gè)Interceptor 的調(diào)用會(huì)依據(jù)它的聲明順序依次執(zhí)行,而且最先執(zhí)行的都是Interceptor 中的preHandle 方法,所以可以在這個(gè)方法中進(jìn)行一些前置初始化操作或者是對(duì)當(dāng)前請(qǐng)求的一個(gè)預(yù)處理,也可以在這個(gè)方法中進(jìn)行一些判斷來(lái)決定請(qǐng)求是否要繼續(xù)進(jìn)行下去。該方法的返回值是布爾值Boolean 類型的,當(dāng)它返回為false 時(shí),表示請(qǐng)求結(jié)束,后續(xù)的Interceptor 和Controller 都不會(huì)再執(zhí)行;當(dāng)返回值為true 時(shí)就會(huì)繼續(xù)調(diào)用下一個(gè)Interceptor 的preHandle 方法,如果已經(jīng)是最后一個(gè)Interceptor 的時(shí)候就會(huì)是調(diào)用當(dāng)前請(qǐng)求的Controller 方法。

(2)postHandle (HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView) 方法,由preHandle 方法的解釋我們知道這個(gè)方法包括后面要說(shuō)到的afterCompletion 方法都只能是在當(dāng)前所屬的Interceptor 的preHandle 方法的返回值為true 時(shí)才能被調(diào)用。postHandle 方法,顧名思義就是在當(dāng)前請(qǐng)求進(jìn)行處理之后,也就是Controller 方法調(diào)用之后執(zhí)行,但是它會(huì)在DispatcherServlet 進(jìn)行視圖返回渲染之前被調(diào)用,所以我們可以在這個(gè)方法中對(duì)Controller 處理之后的ModelAndView 對(duì)象進(jìn)行操作。postHandle 方法被調(diào)用的方向跟preHandle 是相反的,也就是說(shuō)先聲明的Interceptor 的postHandle 方法反而會(huì)后執(zhí)行,這和Struts2 里面的Interceptor 的執(zhí)行過(guò)程有點(diǎn)類型。Struts2 里面的Interceptor 的執(zhí)行過(guò)程也是鏈?zhǔn)降?,只是在Struts2 里面需要手動(dòng)調(diào)用ActionInvocation 的invoke 方法來(lái)觸發(fā)對(duì)下一個(gè)Interceptor 或者是Action 的調(diào)用,然后每一個(gè)Interceptor 中在invoke 方法調(diào)用之前的內(nèi)容都是按照聲明順序執(zhí)行的,而invoke 方法之后的內(nèi)容就是反向的。

(3)afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex) 方法,該方法也是需要當(dāng)前對(duì)應(yīng)的Interceptor 的preHandle 方法的返回值為true 時(shí)才會(huì)執(zhí)行。顧名思義,該方法將在整個(gè)請(qǐng)求結(jié)束之后,也就是在DispatcherServlet 渲染了對(duì)應(yīng)的視圖之后執(zhí)行。這個(gè)方法的主要作用是用于進(jìn)行資源清理工作的。

注意: handler,第三個(gè)參數(shù)為響應(yīng)處理器(一般為Controller)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容