1. 簡介
本教程將重點介紹 Spring MVC 的 HandlerInterceptor。 尤其是,我們將修改 Spring MVC 的模型參數,在處理請求之前和之後。
如果您想了解 HandlerInterceptor 的基本知識,請查看這篇文章。
2. Maven 依賴
為了使用 攔截器,您需要在您的 pom.xml 的 dependencies 部分添加以下內容:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>6.2.1</version>
</dependency>
最新版本可以在這裏找到:這裏。
該依賴僅涵蓋 Spring Web,請務必添加 spring-core 和 spring-context 以構建完整的 Web 應用程序,並選擇您需要的日誌庫。
3. 自定義實現
`HandlerInterceptor 的一種使用場景是在模型中添加常見/用户特定的參數,這些參數將在生成的每個視圖中可用。
在我們的示例中,我們將使用自定義攔截器實現來為模型添加已登錄用户的用户名。在更復雜的系統中,我們還可以添加更具體的的信息,例如:用户頭像路徑、用户位置等。
讓我們首先定義我們的新 <em>Interceptor</em> 類:
public class UserInterceptor extends HandlerInterceptorAdapter {
private static Logger log = LoggerFactory.getLogger(UserInterceptor.class);
...
}我們擴展了 HandlerInterceptorAdapter,因為我們只希望實現 preHandle() 和 postHandle() 方法。
正如我們之前提到的,我們希望將已登錄用户的姓名添加到模型中。首先,我們需要檢查用户是否已登錄。我們可以通過檢查 SecurityContextHolder 來獲取此信息。
public static boolean isUserLogged() {
try {
return !SecurityContextHolder.getContext().getAuthentication()
.getName().equals("anonymousUser");
} catch (Exception e) {
return false;
}
}當 HttpSession 建立,但未登錄任何用户時,Spring Security上下文中用户名將等於 anonymousUser。接下來,我們繼續實現 preHandle():。
3.1. 方法 preHandle()
在處理請求之前,無法訪問模型參數。為了添加用户名,需要使用 HttpSession 將參數設置好:
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object object) throws Exception {
if (isUserLogged()) {
addToModelUserDetails(request.getSession());
}
return true;
}如果我們在處理請求時使用這些信息,那麼這一點至關重要。正如我們所見,我們正在檢查用户是否已登錄,然後通過獲取其會話來向請求添加參數。
private void addToModelUserDetails(HttpSession session) {
log.info("=============== addToModelUserDetails =========================");
String loggedUsername
= SecurityContextHolder.getContext().getAuthentication().getName();
session.setAttribute("username", loggedUsername);
log.info("user(" + loggedUsername + ") session : " + session);
log.info("=============== addToModelUserDetails =========================");
}我們使用 SecurityContextHolder 來獲取 loggedUsername。 您可以覆蓋 Spring Security 的 UserDetails 實現以獲取電子郵件而不是標準用户名。
3.2. 方法 postHandle()
在處理請求後,我們的模型參數可用,因此我們可以訪問它們來修改或添加新的值。要執行此操作,我們使用重寫的 postHandle() 方法:
@Override
public void postHandle(
HttpServletRequest req,
HttpServletResponse res,
Object o,
ModelAndView model) throws Exception {
if (model != null && !isRedirectView(model)) {
if (isUserLogged()) {
addToModelUserDetails(model);
}
}
}讓我們來查看一下實現細節。
首先,最好檢查模型是否為 null。 這將防止我們遇到 NullPointerException。
此外,我們還可以檢查一個 View 是否不是 RedirectView 的實例。
在請求處理和然後重定向後,無需添加/更改參數,因為新控制器將立即再次執行處理。 為了檢查視圖是否被重定向,我們引入了以下方法:
public static boolean isRedirectView(ModelAndView mv) {
String viewName = mv.getViewName();
if (viewName.startsWith("redirect:/")) {
return true;
}
View view = mv.getView();
return (view != null && view instanceof SmartView
&& ((SmartView) view).isRedirectView());
}最後,我們再次檢查用户是否已登錄,如果是,則向 Spring 模型添加參數:
private void addToModelUserDetails(ModelAndView model) {
log.info("=============== addToModelUserDetails =========================");
String loggedUsername = SecurityContextHolder.getContext()
.getAuthentication().getName();
model.addObject("loggedUsername", loggedUsername);
log.trace("session : " + model.getModel());
log.info("=============== addToModelUserDetails =========================");
}請注意,日誌記錄非常重要,因為這種邏輯在我們的應用程序“後台”運行。很容易忘記在每次更改模型參數時,在 View 中沒有正確地進行日誌記錄。
4. 配置
要將我們創建的 攔截器 添加到 Spring 配置中,我們需要在實現 WebMvcConfigurer 接口的 WebConfig 類中覆蓋 addInterceptors() 方法。
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new UserInterceptor());
}我們可能通過編輯我們的 XML Spring 配置文件來達到相同的配置:
<mvc:interceptors>
<bean id="userInterceptor" class="com.baeldung.web.interceptor.UserInterceptor"/>
</mvc:interceptors>從此刻起,我們可以訪問所有生成視圖中的所有用户相關參數。
請注意,如果配置了多個 Spring Interceptors,則 preHandle() 方法的執行順序與配置順序相同,而 postHandle() 和 afterCompletion() 方法的調用則相反。
5. 結論
本教程介紹了使用 Spring MVC 的 HandlerInterceptor 攔截 Web 請求,以便提供用户信息的實現方法。
在本示例中,我們重點添加了已登錄用户的詳細信息到 Web 應用程序的模型參數中。您可以通過添加更詳細的信息來擴展此 HandlerInterceptor 實現。
5.1. 系列文章
所有系列文章如下:
- Spring MVC Handler Interceptor 簡介
- 使用 Handler Interceptor 修改 Spring 模型參數(本篇)