SpringBoot2.0学习笔记:(八) Spring Boot中拦截器的使用

版权声明:本文为博主原创文章,未经博主允许不得转载 https://blog.csdn.net/liujun03/article/details/82818468

一、使用方法

对于在Spring Boot2.0中使用拦截器来说,其使用方法与Spring Boot1.0并无很大区别 。

我的需求是实现登录拦截,通过在session中判断有没有登录用户名来实现拦截

首先就是建立一个实现了HandlerInterceptor的拦截器类。

如下:

public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Object userName = request.getSession().getAttribute("loginUserName");
        if(StringUtils.isEmpty(userName)){
            request.setAttribute("msg","您没有操作权限");
            request.getRequestDispatcher("/index.html").forward(request,response);
            return false;
        }
        return true;
    }

}

这里要提一句的是,Spring Boot2.0是基于Java8的,其中接口可以有默认实现,也就是说你实现一个接口并不用实现它的所有方法。如上,我只实现了preHandle方法。

之后的任务就是将自定义的拦截器添加到InterceptorRegistry中去,这个工作在Spring Boot1.0的时候可以通过继承WebMvcConfigurerAdapter完成,但在Spring Boot2.0中这个适配类是被弃用了的,所以我们可以直接实现WebMvcConfigurer来完成拦截器的添加。

如下:

@Configuration
public class WebMVCConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("login");
        registry.addViewController("/index.html").setViewName("login");

    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/","user/login","/index.html")
                .excludePathPatterns("/public/**","/resources/**");

    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        //第一个方法设置访问路径前缀,第二个方法设置资源路径
        registry.addResourceHandler("/resources/**","/public/**")
                .addResourceLocations("classpath:/resources/","classpath:/public/");
    }
}

在实现WebMvcConfigurer之后可以选择你要重写的方法,这里重写addInterceptors这个方法来添加自定义的拦截器。

  • addInterceptor用于添加你自定义的拦截器实例

  • addPathPatterns用于添加要拦截的url,可以写多个。

  • excludePathPatterns用于添加不需要拦截的url,可以写多个。

至此,我们的登录拦截器就完成了。

二、疑难解惑

1.excludePathPatterns失效的问题

这个现象就是你在excludePathPatterns方法中添加了要忽略的路径,但当你访问此路径的时候拦截器依然进行了拦截。

这是因为你要忽略的路径在项目中并不存在,springboot会将路径编程/error,从而无法进行排除

2. 静态资源被拦截的问题

在Spring Boot1.0中,我们自定义的拦截器并不会对静态资源做出拦截,但是在Spring Boot2.0中,我们自定义的拦截器对静态资源同样做出了拦截。

具体原因私以为如下:

在SpringBoot引入Web模块之后,就会进行WebMvc的自动化配置,即:WebMvcAutoConfiguration,

WebMvcAutoConfiguration中还有个内部静态配置类,即:EnableWebMvcConfiguration

EnableWebMvcConfiguration是继承了DelegatingWebMvcConfiguration类,

DelegatingWebMvcConfiguration又继承了WebMvcConfigurationSupport类:

所以当开启WebMvcAutoConfiguration的自动配置后,WebMvcConfigurationSupport中的bean也会自动加载。

其中WebMvcConfigurationSupportresourceHandlerMapping方法就是处理静态资源映射的:

对Spring Boot 1.0依赖的spring-webmvc-4.3.x版本来说,静态资源的映射处理为:

/**
* Return a handler mapping ordered at Integer.MAX_VALUE-1 with mapped
* resource handlers. To configure resource handling, override
* {@link #addResourceHandlers}.
*/
@Bean
public HandlerMapping resourceHandlerMapping() {
    Assert.state(this.applicationContext != null, "No ApplicationContext set");
    Assert.state(this.servletContext != null, "No ServletContext set");

    ResourceHandlerRegistry registry = new ResourceHandlerRegistry(this.applicationContext,
                                                                   this.servletContext, mvcContentNegotiationManager(), mvcUrlPathHelper());
    addResourceHandlers(registry);

    AbstractHandlerMapping handlerMapping = registry.getHandlerMapping();
    if (handlerMapping != null) {
        handlerMapping.setPathMatcher(mvcPathMatcher());
        handlerMapping.setUrlPathHelper(mvcUrlPathHelper());
        // 这里添加一个静态资源拦截器
        handlerMapping.setInterceptors(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider()));
        handlerMapping.setCorsConfigurations(getCorsConfigurations());
    }
    else {
        handlerMapping = new EmptyHandlerMapping();
    }
    return handlerMapping;
}

这个方法中找到这行代码:

handlerMapping.setInterceptors(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider()));

由此处可知,在spring-webmvc-4.3.x版本中,静态资源处理器有固定的拦截器。

而在spring-webmvc-5.0.x中:

/**
	 * Return a handler mapping ordered at Integer.MAX_VALUE-1 with mapped
	 * resource handlers. To configure resource handling, override
	 * {@link #addResourceHandlers}.
	 */
@Bean
public HandlerMapping resourceHandlerMapping() {
    Assert.state(this.applicationContext != null, "No ApplicationContext set");
    Assert.state(this.servletContext != null, "No ServletContext set");

    ResourceHandlerRegistry registry = new ResourceHandlerRegistry(this.applicationContext,
                                                                   this.servletContext, mvcContentNegotiationManager(), mvcUrlPathHelper());
    addResourceHandlers(registry);

    AbstractHandlerMapping handlerMapping = registry.getHandlerMapping();
    if (handlerMapping != null) {
        handlerMapping.setPathMatcher(mvcPathMatcher());
        handlerMapping.setUrlPathHelper(mvcUrlPathHelper());
        // 这里通过getInterceptors()找拦截器
        handlerMapping.setInterceptors(getInterceptors());
        handlerMapping.setCorsConfigurations(getCorsConfigurations());
    }
    else {
        handlerMapping = new EmptyHandlerMapping();
    }
    return handlerMapping;
}

给静态资源处理器添加拦截器的代码是:

handlerMapping.setInterceptors(getInterceptors());

getInterceptors()的代码是:

/**
	 * Provide access to the shared handler interceptors used to configure
	 * {@link HandlerMapping} instances with.
	 * <p>This method cannot be overridden; use {@link #addInterceptors} instead.
	 */
protected final Object[] getInterceptors() {
    if (this.interceptors == null) {
        InterceptorRegistry registry = new InterceptorRegistry();
        addInterceptors(registry);
        registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService()));
        registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider()));
        this.interceptors = registry.getInterceptors();
    }
    return this.interceptors.toArray();
}

其中:

InterceptorRegistry registry = new InterceptorRegistry();
addInterceptors(registry);

这里可以获取到配置类当中自定义的HandlerInterceptor,之后将自定义的拦截器添加到拦截器链中,而这个方法返回的是所有的拦截器,其中包括我们自定义的。所以每个静态资源的请求都会被自定义Interceptor拦截。

解决办法

因为自定义的拦截器拦截了所有的路径,所以首先我们需要重写addResourceHandlers()方法,指定静态资源的访问路径前缀以及静态资源所处路径:

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    //第一个方法设置访问路径前缀,第二个方法设置资源路径
    registry.addResourceHandler("/resources/**","/public/**")
        .addResourceLocations("classpath:/resources/","classpath:/public/");
}

然后在添加自定义拦截器时忽略静态资源的路径前缀:

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new LoginInterceptor())
        .addPathPatterns("/**")
        .excludePathPatterns("/","user/login","/index.html","/error.html")
        .excludePathPatterns("/public/**","/resources/**");

}

最后,在访问静态资源的时候,加上资源所处的完整路径,例如

Spring boot1.0可以这样访问静态资源:

localhost:8080/11.png

那么Spring Boot2.0加上自定义拦截器就得这样了:

localhost:8080/public/11.pnglocalhost:8080/resources/11.png

猜你喜欢

转载自blog.csdn.net/liujun03/article/details/82818468