Spring Security 注册过滤器注意事项
前两天和小伙伴聊了 Spring Security+JWT 实现无状态登录,然后有小伙伴反馈了一个问题,感觉这是一个我们平时写代码容易忽略的问题,写一篇文章和小伙伴们聊一聊。
一 问题复原
先来说问题吧,在 Spring Security+JWT 登录中,整体上的思路就是用户登录成功之后返回 JWT 字符串,然后以后用户每次请求都携带上 JWT 字符串,服务端进行校验,校验通过之后,请求继续执行。
按照上面的思路,我们的项目中需要有一个 JwtFilter 用来从请求中提取请求传来的 Jwt 字符串进行校验,类似下面这样:
然后有一个小伙伴反馈,在项目中使用了 WebSecurityCustomizer 给 Swagger 相关的请求都放行了,结果这些被放行的请求都被 JwtFilter 拦截了,这是咋回事呢?
首先小伙伴们要知道,使用 WebSecurityCustomizer 放行的请求,都不再经过 SecurityFilter 了,所以按理不该再被 JwtFilter 拦截了,因为 JwtFilter 是隶属于 SecurityFilter 这个过滤器链中的,并非原生的跟 Servlet 平级的那种 Filter。
但是为什么又拦截了呢?
松哥看了下代码,发现问题出在 @Component 这个注解上。
二 原理分析
在 Spring Boot 项目启动的时候,有一个环节就是把 Spring 容器中所有类型为 Filter 的 Bean 找出来,并且自动添加到容器的过滤器链条中(注意不是添加到 Spring Security 过滤器链中)。
这段代码的逻辑位于 ServletContextInitializerBeans#addAdaptableBeans 方法中,在该方法中,会调用 addAsRegistrationBean 方法完成以上事情:
可以看到,这里传入的参数 type 和 beanType 都是 Filter,从 Spring 容器中找到 Filter 类型的 Bean 存入到 initializers 集合中。不过注意,添加到集合中的实际上是封装之后的 registration 对象,这个对象通过 adapter.createRegistrationBean
方法创建出来,在该方法中,由于我们没有为当前过滤器设置拦截的请求地址,所以默认拦截所有请求,拦截规则是 /*
。
最后在 ServletWebServerApplicationContext#selfInitialize 方法中遍历上一步找到的过滤器,并逐个进行配置,相关代码如下:
DynamicRegistrationBean#register:
AbstractFilterRegistrationBean#addRegistration:
可以看到,这最终就是大家熟知的添加过滤器的代码了。
三 解决方案
找到问题的原因,那么问题就好解决了。
问题的产生,主要是因为 Spring 自动查找容器中所有 Filter 类型的 Bean,并进行配置,那么我们的解决方案就是不要把这个 Bean 注册到 Spring 容器中,即不要添加 @Component 注解,而是直接自己 new 出来就行了,在配置过滤器链的时候,像下面这样配置即可:
经过上面这样配置之后,JwtFilter 就不存在于原生过滤器链中了,只是单纯的存在于 SecurityFilter 中。
理解了 Spring Security 原理,那么日常开发中各种奇奇怪怪的情况,我们就都能轻车熟路的解决了。
如果小伙伴们想要彻底掌握 Spring Security+OAuth2,那么可以看看松哥最近录制的这套全新的视频教程。
版权声明: 本文为 InfoQ 作者【江南一点雨】的原创文章。
原文链接:【http://xie.infoq.cn/article/600f39ee30c30b7618283ca96】。未经作者许可,禁止转载。
评论