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


    过滤器

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

    配置类注册

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

    1. class TestFilter : Filter {
    2. override fun doFilter(request: ServletRequest?, response: ServletResponse?, chain: FilterChain?) {
    3. // 逻辑
    4. chain?.doFilter(request, response)
    5. }
    6. }

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

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

    1. @Configuration
    2. class TestConfig {
    3. @Bean
    4. fun getFilter(): FilterRegistrationBean{
    5. val bean = FilterRegistrationBean()
    6. // 逻辑
    7. return bean
    8. }
    9. }

     它的基本操作如下:

    1. @Bean
    2. fun getFilter(): FilterRegistrationBean{
    3. val bean = FilterRegistrationBean()
    4. bean.filter = TestFilter() // 设置注册过滤器的对象
    5. bean.order = 1 // 设置过滤器优先级,值越小优先级越高,1是最顶级
    6. bean.addUrlPatterns("/index") // 设置过滤的路径
    7. bean.setName("testFilter") // 设置过滤器的名字
    8. return bean
    9. }

    我们来实践一下:

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

    1. package com.example.c0101.filter
    2. import jakarta.servlet.Filter
    3. import jakarta.servlet.FilterChain
    4. import jakarta.servlet.ServletRequest
    5. import jakarta.servlet.ServletResponse
    6. class TestFilter : Filter {
    7. override fun doFilter(request: ServletRequest?, response: ServletResponse?, chain: FilterChain?) {
    8. println("doFilter")
    9. chain?.doFilter(request, response)
    10. }
    11. }

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

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

    1. package com.example.c0101.config
    2. import com.example.c0101.filter.TestFilter
    3. import org.springframework.boot.web.servlet.FilterRegistrationBean
    4. import org.springframework.context.annotation.Bean
    5. import org.springframework.context.annotation.Configuration
    6. @Configuration
    7. class TestConfig {
    8. @Bean
    9. fun getFilter(): FilterRegistrationBean{
    10. val bean = FilterRegistrationBean()
    11. bean.filter = TestFilter() // 设置注册过滤器的对象
    12. bean.addUrlPatterns("/index") // 设置过滤的路径
    13. return bean
    14. }
    15. }

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

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

    1. package com.example.c0101.controller
    2. import org.springframework.web.bind.annotation.RequestMapping
    3. import org.springframework.web.bind.annotation.RestController
    4. @RestController
    5. class TestController {
    6. @RequestMapping("/index")
    7. fun index(): String{
    8. println("进入index方法")
    9. return "这是主页"
    10. }
    11. }

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

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

    1. doFilter
    2. 进入index方法

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


    @WebFilter注解注册

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

    1. @WebFilter("/index")
    2. @Component
    3. class TestFilter : Filter {
    4. override fun doFilter(request: ServletRequest?, response: ServletResponse?, chain: FilterChain?) {
    5. println("doFilter")
    6. chain?.doFilter(request, response)
    7. }
    8. }

    Spring Boot的扫描

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

    1. @SpringBootApplication
    2. class C0101Application
    3. fun main(args: Array<String>) {
    4. runApplication(*args)
    5. }

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

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

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

    @WebFilter实践

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

    1. @WebFilter("/index")
    2. @Component

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

    1. doFilter
    2. 进入index方法

    和我们之前的结果一样

    拦截器

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

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

    定义拦截器的代码如下:

    1. class TestInterceptor : HandlerInterceptor {
    2. override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean {
    3. println("preHandle")
    4. return true
    5. }
    6. override fun postHandle(
    7. request: HttpServletRequest,
    8. response: HttpServletResponse,
    9. handler: Any,
    10. modelAndView: ModelAndView?
    11. ) {
    12. println("postHandle")
    13. }
    14. override fun afterCompletion(
    15. request: HttpServletRequest,
    16. response: HttpServletResponse,
    17. handler: Any,
    18. ex: Exception?
    19. ) {
    20. println("afterCompletion")
    21. }
    22. }

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

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

    1. @Configuration
    2. class TestConfig : WebMvcConfigurer{
    3. override fun addInterceptors(registry: InterceptorRegistry) {
    4. val regist = registry.addInterceptor(TestInterceptor())
    5. regist.addPathPatterns("/index")
    6. }
    7. }

    我们来实践一下

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

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

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

    1. package com.example.c0101.interceptor
    2. import jakarta.servlet.http.HttpServletRequest
    3. import jakarta.servlet.http.HttpServletResponse
    4. import org.springframework.web.servlet.HandlerInterceptor
    5. class TestInterceptor : HandlerInterceptor {
    6. override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean {
    7. val token = request.getParameter("token")
    8. return if (token == "token") true else{
    9. response.status = 401
    10. val outputStream = response.outputStream
    11. outputStream.write("令牌错误".toByteArray())
    12. false
    13. }
    14. }
    15. }

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

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

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

    1. package com.example.c0101.config
    2. import com.example.c0101.interceptor.TestInterceptor
    3. import org.springframework.context.annotation.Configuration
    4. import org.springframework.web.servlet.config.annotation.InterceptorRegistry
    5. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
    6. @Configuration
    7. class TestConfig : WebMvcConfigurer{
    8. override fun addInterceptors(registry: InterceptorRegistry) {
    9. val regist = registry.addInterceptor(TestInterceptor())
    10. regist.addPathPatterns("/index")
    11. }
    12. }

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

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

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

  • 相关阅读:
    【无人机】四轴无人机的轨迹进行可视化和动画处理(Matlab代码实现)
    《Mycat分布式数据库架构》之数据切分和读写分离
    Calendar 求取指定时间 下周一 下一月1号 下一年1月1号 下一季度首月1号
    编译pycaffe过程中遇到的问题及解决
    day8-机器学习模型评估
    sys.argv和argparse和os.environ
    网络安全(黑客)自学
    EMC测试项目
    c语言指针的指针
    【AIGC】提示词 Prompt 分享
  • 原文地址:https://blog.csdn.net/wozuishuai_/article/details/136249098