附DEMO| 绝活!Spring Security过滤器就该这么配置

举报
码农小胖哥 发表于 2022/03/31 22:34:18 2022/03/31
【摘要】 2022年的开工福利已经发布,点击下面按钮获取最新PDF。 以前胖哥带大家用Spring Security过滤器实现了手机验证码认证,今天我们来改良一下验证码认证的配置方式。这绝对是绝活666,不再看、点赞一波吗?天天白嫖,晚上睡得着觉? CaptchaAuthenticationFilter这个验证码过滤器是通过模仿Us...

2022年的开工福利已经发布,点击下面按钮获取最新PDF。

bc272e82314afc0b1d9aae927731d270.png

以前胖哥带大家用Spring Security过滤器实现了手机验证码认证,今天我们来改良一下验证码认证的配置方式。这绝对是绝活666,不再看、点赞一波吗?天天白嫖,晚上睡得着觉30b5e7facf8083cb435fa032d553e056.png

CaptchaAuthenticationFilter这个验证码过滤器是通过模仿UsernamePasswordAuthenticationFilter实现的。同样的道理,由于UsernamePasswordAuthenticationFilter的配置是由FormLoginConfigurer来完成的,应该也能模仿一下FormLoginConfigurer,写一个配置类CaptchaAuthenticationFilterConfigurer去配置CaptchaAuthenticationFilter


   
  1. public final class FormLoginConfigurer<H extends HttpSecurityBuilder<H>> extends
  2.   AbstractAuthenticationFilterConfigurer<H, FormLoginConfigurer<H>, UsernamePasswordAuthenticationFilter> {
  3.  
  4.     // 省略
  5. }

AbstractAuthenticationFilterConfigurer

FormLoginConfigurer看起来有点复杂,不过继承关系并不复杂,只继承了AbstractAuthenticationFilterConfigurer


   
  1. public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecurityBuilder<B>, T extends AbstractAuthenticationFilterConfigurer<B, T, F>, F extends AbstractAuthenticationProcessingFilter>
  2.   extends AbstractHttpConfigurer<T, B> {
  3. }

理论上我们模仿一下,也继承一下这个类,但是你会发现这种方式行不通。因为AbstractAuthenticationFilterConfigurer只能Spring Security内部使用,不建议自定义。原因在于它最终向HttpSecurity添加过滤器使用的是HttpSecurity.addFilter(Filter)方法,这个方法只有内置过滤器(参见FilterOrderRegistration)才能使用。了解了这个机制之后,我们只能往上再抽象一层,去改造其父类AbstractHttpConfigurer

改造过程

AbstractAuthenticationFilterConfigurer<B,T,F>中的B是实际指的HttpSecurity,因此这个要保留;

T指的是它本身的实现,我们配置CaptchaAuthenticationFilter不需要下沉一层到FormLoginConfigurer这个继承级别,直接在AbstractAuthenticationFilterConfigurer这个继承级别实现即可,因此T这里指的就是需要配置类本身,也不需要再抽象化,因此是不需要的;同样的原因F也不需要,很明确是CaptchaAuthenticationFilter,不需要再泛化。这样CaptchaAuthenticationFilter的配置类结构可以这样定义:


   
  1. public class CaptchaAuthenticationFilterConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<CaptchaAuthenticationFilterConfigurer<H>, H> {
  2.     // 不再泛化  具体化 
  3.     private final CaptchaAuthenticationFilter authFilter;
  4.     // 特定的验证码用户服务
  5.     private CaptchaUserDetailsService captchaUserDetailsService;
  6.     // 验证码处理服务
  7.     private CaptchaService captchaService;
  8.     // 保存认证请求细节的策略 
  9.     private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource;
  10.     // 默认使用保存请求认证成功处理器 
  11.     private SavedRequestAwareAuthenticationSuccessHandler defaultSuccessHandler = new SavedRequestAwareAuthenticationSuccessHandler();
  12.     // 认证成功处理器
  13.     private AuthenticationSuccessHandler successHandler = this.defaultSuccessHandler;
  14.      // 登录认证端点
  15.     private LoginUrlAuthenticationEntryPoint authenticationEntryPoint;
  16.     // 是否 自定义页面 
  17.     private boolean customLoginPage;
  18.     // 登录页面
  19.     private String loginPage;
  20.     // 登录成功url
  21.     private String loginProcessingUrl;
  22.     // 认证失败处理器
  23.     private AuthenticationFailureHandler failureHandler;
  24.     // 认证路径是否放开
  25.     private boolean permitAll;
  26.     //  认证失败的url
  27.     private String failureUrl;
  28.     /**
  29.      * Creates a new instance with minimal defaults
  30.      */
  31.     public CaptchaAuthenticationFilterConfigurer() {
  32.         setLoginPage("/login/captcha");
  33.         this.authFilter = new CaptchaAuthenticationFilter();
  34.     }
  35.     public CaptchaAuthenticationFilterConfigurer<H> formLoginDisabled() {
  36.         this.formLoginEnabled = false;
  37.         return this;
  38.     }
  39.     public CaptchaAuthenticationFilterConfigurer<H> captchaUserDetailsService(CaptchaUserDetailsService captchaUserDetailsService) {
  40.         this.captchaUserDetailsService = captchaUserDetailsService;
  41.         return this;
  42.     }
  43.     public CaptchaAuthenticationFilterConfigurer<H> captchaService(CaptchaService captchaService) {
  44.         this.captchaService = captchaService;
  45.         return this;
  46.     }
  47.     public CaptchaAuthenticationFilterConfigurer<H> usernameParameter(String usernameParameter) {
  48.         authFilter.setUsernameParameter(usernameParameter);
  49.         return this;
  50.     }
  51.     public CaptchaAuthenticationFilterConfigurer<H> captchaParameter(String captchaParameter) {
  52.         authFilter.setCaptchaParameter(captchaParameter);
  53.         return this;
  54.     }
  55.     public CaptchaAuthenticationFilterConfigurer<H> parametersConverter(Converter<HttpServletRequest, CaptchaAuthenticationToken> converter) {
  56.         authFilter.setConverter(converter);
  57.         return this;
  58.     }
  59.     @Override
  60.     public void init(H http) throws Exception {
  61.         updateAuthenticationDefaults();
  62.         updateAccessDefaults(http);
  63.         registerDefaultAuthenticationEntryPoint(http);
  64.         // 这里禁用默认页面过滤器 如果你想自定义登录页面 可以自行实现 可能和FormLogin冲突
  65.         // initDefaultLoginFilter(http);
  66.         // 把对应的Provider也在init时写入HttpSecurity
  67.         initProvider(http);
  68.     }
  69.      @Override
  70.     public void configure(H http) throws Exception {
  71.         
  72.         //这里改为使用前插过滤器方法
  73.          http.addFilterBefore(filter, LogoutFilter.class);
  74.     }
  75.     
  76.      // 其它方法 同AbstractAuthenticationFilterConfigurer
  77. }

其实就是模仿AbstractAuthenticationFilterConfigurer及其实现类的风格把用的配置项实现一遍。这里值得一提的是CaptchaService的配置也可以从Spring IoC中查找(参考getBeanOrNull方法,这个方法在Spring Security中随处可见,建议借鉴),这样更加灵活,既能从方法配置也能自动注入。


   
  1. private void initProvider(H http) {
  2.         ApplicationContext applicationContext = http.getSharedObject(ApplicationContext.class);
  3.         // 没有配置CaptchaUserDetailsService就去Spring IoC获取
  4.         if (captchaUserDetailsService == null) {
  5.             captchaUserDetailsService = getBeanOrNull(applicationContext, CaptchaUserDetailsService.class);
  6.         }
  7.         // 没有配置CaptchaService就去Spring IoC获取
  8.         if (captchaService == null) {
  9.             captchaService = getBeanOrNull(applicationContext, CaptchaService.class);
  10.         } 
  11.         // 初始化 Provider
  12.         CaptchaAuthenticationProvider captchaAuthenticationProvider = this.postProcess(new CaptchaAuthenticationProvider(captchaUserDetailsService, captchaService));
  13.         // 会增加到ProviderManager的注册列表中
  14.         http.authenticationProvider(captchaAuthenticationProvider);
  15.     }

配置类效果

我们来看看CaptchaAuthenticationFilterConfigurer的配置效果:


   
  1. @Bean
  2.     SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, UserDetailsService userDetailsService) throws Exception {
  3.         http.csrf().disable()
  4.                 .authorizeRequests()
  5.                 .mvcMatchers("/foo/**").access("hasAuthority('ROLE_USER')")
  6.                 .anyRequest().authenticated()
  7.                 .and()
  8.                 // 所有的 AbstractHttpConfigurer 都可以通过apply方法加入HttpSecurity
  9.                 .apply(new CaptchaAuthenticationFilterConfigurer<>())
  10.                 // 配置验证码处理服务   这里直接true 方便测试
  11.                 .captchaService((phone, rawCode) -> true)
  12.                 // 通过手机号去拿验证码,这里为了方便直接写死了,实际phone和username做个映射  
  13.                 .captchaUserDetailsService(phone -> userDetailsService.loadUserByUsername("felord"))
  14.                 // 默认认证成功跳转到/路径  这里改造成把认证信息直接返回json
  15.                 .successHandler((request, response, authentication) -> {
  16.                 // 这里把认证信息以JSON形式返回
  17.                     ServletServerHttpResponse servletServerHttpResponse = new ServletServerHttpResponse(response);
  18.                     MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
  19.                            mappingJackson2HttpMessageConverter.write(authentication, MediaType.APPLICATION_JSON,servletServerHttpResponse);
  20.                 });
  21.         return http.build();
  22.     }

是不是要优雅很多,解决了你自己配置过滤器的很多疑难杂症。学习一定要模仿,先模仿成功,然后再分析思考为什么会模仿成功,最后形成自己的创造力。千万不要被一些陌生概念唬住,有些改造是不需要去深入了解细节的。

公众号私信回复 filterconfig 获取 DEMO

OAuth2授权服务器Keycloak宣布不再适配Spring Boot和Spring Security

2022-02-15

338261571aeba7b7612c5e941a2a46a8.png

Spring Security过滤器链如何匹配到特定的请求

2022-02-14

90ce607cbda90cfeea386936e9642df1.png

干货福利PDF下载|Spring Security过滤器链体系

2022-02-10

91024c93083892664e5aa6c2bfd5c638.png

a13f89ed246c6bc5269a72ea8f0d2799.gif

文章来源: felord.blog.csdn.net,作者:码农小胖哥,版权归原作者所有,如需转载,请联系作者。

原文链接:felord.blog.csdn.net/article/details/122974814

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。