Springboot教程(二)——过滤器、拦截器

过滤器

过滤器可以在调用控制器方法之前进行一些操作,过滤器类一般放在filter包下。

配置类注册

使用过滤器时,要实现Filter接口,并重写doFilter方法:

Kotlin 复制代码
class TestFilter : Filter {

    override fun doFilter(request: ServletRequest?, response: ServletResponse?, chain: FilterChain?) {
        // 逻辑
        chain?.doFilter(request, response)
    }
}

这里要注意,过滤器最后应调用chain.doFilter(request, response)方法,将请求交给后一个过滤器。当然,有些时候不想交给后一个过滤器,也可以不写

要启用过滤器,需要写一个配置类,用@Configuration标注。在配置类中,定义一个方法,用@Bean标注,这个方法需要先获取一个FilterRegistrationBean<T>对象,用于注册过滤器,再对这个对象进行一些操作,最后返回这个对象。这里面有一个泛型,表示要注册的过滤器的类型:

Kotlin 复制代码
@Configuration
class TestConfig {

    @Bean
    fun getFilter(): FilterRegistrationBean<TestFilter>{
        val bean = FilterRegistrationBean<TestFilter>()
        // 逻辑
        return bean
    }

}

它的基本操作如下:

Kotlin 复制代码
    @Bean
    fun getFilter(): FilterRegistrationBean<TestFilter>{
        val bean = FilterRegistrationBean<TestFilter>()
        bean.filter = TestFilter()    // 设置注册过滤器的对象
        bean.order = 1                // 设置过滤器优先级,值越小优先级越高,1是最顶级
        bean.addUrlPatterns("/index") // 设置过滤的路径
        bean.setName("testFilter")    // 设置过滤器的名字
        return bean
    }

我们来实践一下:

在项目下创建filter包,在filter包下创建TestFilter类:

Kotlin 复制代码
package com.example.c0101.filter

import jakarta.servlet.Filter
import jakarta.servlet.FilterChain
import jakarta.servlet.ServletRequest
import jakarta.servlet.ServletResponse

class TestFilter : Filter {

    override fun doFilter(request: ServletRequest?, response: ServletResponse?, chain: FilterChain?) {
        println("doFilter")
        chain?.doFilter(request, response)
    }
}

这个类实现了Filter接口,重写了doFilter方法,表示过滤器的操作。在这里面只是打印了"doFilter"的信息。

在项目下创建config包,在config包下创建TestConfig类:

Kotlin 复制代码
package com.example.c0101.config

import com.example.c0101.filter.TestFilter
import org.springframework.boot.web.servlet.FilterRegistrationBean
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration
class TestConfig {

    @Bean
    fun getFilter(): FilterRegistrationBean<TestFilter>{
        val bean = FilterRegistrationBean<TestFilter>()
        bean.filter = TestFilter()    // 设置注册过滤器的对象
        bean.addUrlPatterns("/index") // 设置过滤的路径
        return bean
    }

}

这个类用于注册一个TestFilter的过滤器。

在项目下创建controller包,在controller包下创建TestController类:

Kotlin 复制代码
package com.example.c0101.controller

import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RestController
class TestController {
    @RequestMapping("/index")
    fun index(): String{
        println("进入index方法")
        return "这是主页"
    }
}

这个控制器注册了/index的路径,返回一个"这是主页"的字符串。

我们用浏览器访问http://127.0.0.1:8080/index,然后回到idea里,发现控制台输出:

Kotlin 复制代码
doFilter
进入index方法

因为过滤器会在进入方法之前执行


@WebFilter注解注册

将@WebFilter标注在过滤器类上,可以快速注册一个过滤器。但是,@WebFilter需要和@Component同时使用,这样才能被Spring Boot扫描到:

Kotlin 复制代码
@WebFilter("/index")
@Component
class TestFilter : Filter {

    override fun doFilter(request: ServletRequest?, response: ServletResponse?, chain: FilterChain?) {
        println("doFilter")
        chain?.doFilter(request, response)
    }
}

Spring Boot的扫描

提到了"被Spring Boot扫描到",那就讲一下扫描。Spring Boot的主类是创建项目时就自带的XXXApplication.kt内定义的XXXApplication:

Kotlin 复制代码
@SpringBootApplication
class C0101Application

fun main(args: Array<String>) {
    runApplication<C0101Application>(*args)
}

这个类被@SpringBootApplication注解标注,这个注解源码的最核心注解有如三个:

  • @SpringBootConfiguration 让项目采用Java注解的配置方式,而不是xml配置方式
  • @EnableAutoConfiguration 开启自动配置,启动时可以自动加载配置文件和配置类
  • @ComponentScan 启动组件扫描器。组件扫描器可以扫描被@Component注解标注的类

所以说,一个类想要被扫描到,就必须被@Component注解标注。我们之前学的@Controller、@Configuration等注解,其实上内部都有@Component注解,因此可以被扫描到

@WebFilter实践

删除原来项目的配置类和config包,在TestFilter类上标注:

Kotlin 复制代码
@WebFilter("/index")
@Component

运行代码,在浏览器访问http://127.0.0.1:8080/index,控制台输出:

Kotlin 复制代码
doFilter
进入index方法

和我们之前的结果一样

拦截器

定义一个拦截器类,需要继承HandlerInterceptor接口,通过重写preHandle、postHandle、afterCompletion方法,设置一个请求的不同时期的拦截方法:

事件
收到请求
过滤器doFilter方法
拦截器preHandle方法
控制器对应的方法
拦截器postHandle方法
解析视图
请求结束
拦截器afterCompletion方法

定义拦截器的代码如下:

Kotlin 复制代码
class TestInterceptor : HandlerInterceptor {

    override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean {
        println("preHandle")
        return true
    }

    override fun postHandle(
        request: HttpServletRequest,
        response: HttpServletResponse,
        handler: Any,
        modelAndView: ModelAndView?
    ) {
        println("postHandle")
    }

    override fun afterCompletion(
        request: HttpServletRequest,
        response: HttpServletResponse,
        handler: Any,
        ex: Exception?
    ) {
        println("afterCompletion")
    }
}

这里面preHandle方法的返回值如果为true表示正常执行,如果为false表示阻止请求正常执行

注册一个拦截器,需要创建配置类,继承WebMvcConfigurer接口并重写addInterceptors方法,这个方法会传入一个registry参数,我们需要调用它的addInterceptor方法,并接收它的返回值,再用这个返回值调用addPathPatterns方法设置要拦截的路径:

Kotlin 复制代码
@Configuration
class TestConfig : WebMvcConfigurer{

    override fun addInterceptors(registry: InterceptorRegistry) {
        val regist = registry.addInterceptor(TestInterceptor())
        regist.addPathPatterns("/index")
    }
}

我们来实践一下

网站的某些路径需要用户先登录才能访问,那么如何确保用户已经登录呢?

最常用的做法是,在用户登录后给用户一个访问令牌,用户访问其他路径时,需要将访问令牌传给服务器,服务器再对访问令牌进行判断。我们可以通过拦截器简单的模拟拦截访问令牌:

创建一个interceptor包,创建TestInterceptor类:

Kotlin 复制代码
package com.example.c0101.interceptor

import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.springframework.web.servlet.HandlerInterceptor

class TestInterceptor : HandlerInterceptor {

    override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean {
        val token = request.getParameter("token")
        return if (token == "token") true else{
            response.status = 401
            val outputStream = response.outputStream
            outputStream.write("令牌错误".toByteArray())
            false
        }
    }

}

代码首先通过request.getParameter方法获取token参数(访问令牌),然后判断这个访问令牌是否正确(为了方便起见,我们通过判断访问令牌是否为"token"来判断访问令牌是否正确),如果正确则请求正常执行,否则通过response.setStatus方法设置请求状态码(401:当前请求需要用户验证),然后通过response.getOutputStream获取响应的输出流,再向这个输出流写入"令牌错误"的信息,然后阻止请求执行

关于request、response这里不多讲,它们是Servlet里的类的对象

接下来创建config包,再config包下创建TestConfig类:

Kotlin 复制代码
package com.example.c0101.config

import com.example.c0101.interceptor.TestInterceptor
import org.springframework.context.annotation.Configuration
import org.springframework.web.servlet.config.annotation.InterceptorRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer

@Configuration
class TestConfig : WebMvcConfigurer{

    override fun addInterceptors(registry: InterceptorRegistry) {
        val regist = registry.addInterceptor(TestInterceptor())
        regist.addPathPatterns("/index")
    }
}

将/index路径注册了TestInterceptor拦截器

接下来用postman访问http://127.0.0.1:8080/index,进行传入token和不传入token的测试:

可以发现,拦截器成功拦截了令牌错误的访问

相关推荐
追逐时光者5 小时前
推荐 12 款开源美观、简单易用的 WPF UI 控件库,让 WPF 应用界面焕然一新!
后端·.net
Jagger_5 小时前
敏捷开发流程-精简版
前端·后端
苏打水com6 小时前
数据库进阶实战:从性能优化到分布式架构的核心突破
数据库·后端
西瓜er7 小时前
JAVA:Spring Boot 集成 FFmpeg 实现多媒体处理
java·spring boot·ffmpeg
间彧7 小时前
Spring Cloud Gateway与Kong或Nginx等API网关相比有哪些优劣势?
后端
间彧7 小时前
如何基于Spring Cloud Gateway实现灰度发布的具体配置示例?
后端
间彧7 小时前
在实际项目中如何设计一个高可用的Spring Cloud Gateway集群?
后端
间彧7 小时前
如何为Spring Cloud Gateway配置具体的负载均衡策略?
后端
间彧7 小时前
Spring Cloud Gateway详解与应用实战
后端
EnCi Zheng9 小时前
SpringBoot 配置文件完全指南-从入门到精通
java·spring boot·后端