概述

在这个教程中,我们将关注Spring MVC的HandlerInterceptor。更具体地说,我们将学习如何在处理请求前后改变模型参数。

如果你想了解HandlerInterceptor的基础知识,可以阅读这篇文章

2. Maven依赖

为了使用Interceptors,你需要在pom.xml文件的dependencies部分添加以下内容:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>5.3.13</version>
</dependency>

最新的版本可以在这里找到。

这个依赖只涵盖了Spring Web,别忘了为完整的web应用添加spring-corespring-context,以及你选择的日志库。

3. 自定义实现

HandlerInterceptor的一个用例是在每个生成的视图上添加通用或用户特定的参数。在我们的示例中,我们将使用自定义拦截器实现将已登录用户的用户名添加到模型参数中。在更复杂的系统中,我们可能会添加更多信息,如用户头像路径、用户位置等。

首先,让我们定义我们的新Interceptor类:

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 =========================");
}

请注意,日志记录非常重要,因为这种逻辑在我们的应用程序的“幕后”工作。很容易忘记我们在每次生成视图时都正确地更改了一些模型参数。

4. 配置

要在Spring配置中添加我们刚刚创建的Interceptor,我们需要在实现WebMvcConfigurerWebConfig类中重写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的InterceptorspreHandle()方法按照配置顺序执行,而postHandle()afterCompletion()方法则按相反的顺序调用。

5. 总结

本教程展示了如何使用Spring MVC的HandlerInterceptor拦截Web请求,以便提供用户信息。

在这个特定示例中,我们专注于在web应用中向模型参数添加已登录用户的详细信息。你可以扩展这个HandlerInterceptor实现,添加更多详细信息。

所有示例和配置都在GitHub上可用。

5.1. 系列文章

系列文章的所有内容:


» 下一篇: Hystrix简介