之前胖哥带着大家使用SpringSecurity过滤器实现验证码认证。今天来完善一下验证码认证的配置方式,更符合SpringSecurity的设计风格,也更Involve。CaptchaAuthenticationFilter是模仿UsernamePasswordAuthenticationFilter实现的。同理,既然UsernamePasswordAuthenticationFilter的配置是由FormLoginConfigurer完成的,那么应该可以仿照FormLoginConfigurer写一个配置类CaptchaAuthenticationFilterConfigurer来配置CaptchaAuthenticationFilter。公共最终类FormLoginConFigurer>扩展了AbstractAuthenticationFilterConfigurer,USERNAMEPASSWordAuthenticationFilter>{//publicabstractclassAbstractAuthenticationFilterConfigurer,TextendsAbstractAuthenticationFilterConfigurer,FextendsAbstractAuthenticationProcessingFilter>extendsAbstractHttpConfigurer{}理论上也可以继承这个类你会发现这个方法不起作用。因为AbstractAuthenticationFilterConfigurer只能被SpringSecurity内部使用,所以不建议自定义。原因是它最终使用HttpSecurity.addFilter(Filter)方法为HttpSecurity添加了一个过滤器,该方法只能由内置过滤器使用(参见FilterOrderRegistration)。了解了这个机制之后,我们只能再抽象一层,对其父类AbstractHttpConfigurer进行改造。改造过程中的BAbstractAuthenticationFilterConfigurer实际上是指HttpSecurity,所以这个要保留;T指的是它自己的实现。我们不需要下沉一层到FormLoginConfigurer的继承层来配置CaptchaAuthenticationFilter,直接在AbstractAuthenticationFilterConfigurer的继承层实现即可,所以这里的T是指需要自己配置类,不需要抽象,所以不需要;同理,F也不需要,明明就是CaptchaAuthenticationFilter,不用泛化。Inthisway,theconfigurationclassstructureofCaptchaAuthenticationFiltercanbedefinedasfollows:publicclassCaptchaAuthenticationFilterConfigurer>extendsAbstractHttpConfigurer,H>{//nolongergeneralizeandconcretizeprivatefinalCaptchaAuthenticationFilter;//a码用户服务privateCaptchaUserDetailsServicecaptchaUserDetailsService;//验证码处理服务privateCaptchaServicecaptchaService;//保存认证请求细节的策略privateAuthenticationDetailsSourceauthenticationDetailsSource;//默认使用保存请求认证成功处理器privateSavedRequestAwareAuthenticationSuccessHandlerdefaultSuccessHandler=newSavedRequestAwareAuthenticationSuccessHandler();//authenticationsuccesshandlerprivateAuthenticationSuccessHandlersuccessHandler=this.defaultSuccessHandler;//loginauthenticationendpointprivateLoginUrlAuthenticationEntryPointauthenticationEntryPoint;//whethertocustomizethepageprivatebooleancustomLoginPage;//loginpage私有字符串登录页面;//登录成功urlprivateStringloginProcessingUrl;//认证失败处理器privateAuthenticationFailureHandlerfailureHandler;//认证路径是否放开privatebooleanpermitAll;//认证失败的urlprivateStringfailureUrl;/***创建一个具有最小默认值的新实例*/publicCaptchaAuthenticationFilterConfigurer(){setLoginPage("/login/captcha");this.authFilter=newCaptchaAuthenticationFilter();}publicCaptchaAuthenticationFilterConfigurerformLoginDisabled(){this.formLoginEnabled=false;归还这个;}publicCaptchaAuthenticationFilterConfigurercaptchaUserDetailsS??ervice(CaptchaUserDetailsS??ervicecaptchaUserDetailsS??ervice){this.captchaUserDetailsS??ervice=captchaUserDetailsS??ervice;归还这个;}publicCaptchaAuthenticationFilterConfigurercaptchaService(CaptchaServicecaptchaService){这个.captchaService=验证码服务;归还这个;}publicCaptchaAuthenticationFilterConfigurerusernameParameter(StringusernameParameter){authFilter.setUsernameParameter(usernameParameter);归还这个;}publicCaptchaAuthenticationFilterConfigurercaptchaParameter(StringcaptchaParameter){authFilter.setCaptchaParameter(captchaParameter);归还这个;}publicCaptchaAuthenticationFilterConfigurerparametersConverter(Converter转换器){authFilter.setConverter(转换器);归还这个;}@Overridepublicvoidinit(Hhttp)throwsException{updateAuthenticationDefaults();更新访问默认值(http);registerDefaultAuthenticationEntryPoint(http);//在这里禁用默认的页面过滤器。如果要自定义登录页面,可以自己实现。它可能与FormLogin冲突//initDefaultLoginFilter(http);//将对应的Provider放入i编写HttpSecurityinitProvider(http);}@Overridepublicvoidconfigure(Hhttp)throwsException{//这里,使用预插入过滤器的方式来代替http.addFilterBefore(filter,LogoutFilter.class);}//其他方法同AbstractAuthenticationFilterConfigurer}其实就是模仿AbstractAuthenticationFilterConfigurer的风格及其实现类来实现所使用的配置项。这里值得一提的是,CaptchaService的配置也可以从SpringIoC中找到(参考getBeanOrNull方法,在SpringSecurity中随处可见,推荐借鉴),比较灵活,可以从中配置方法或自动注入。privatevoidinitProvider(Hhttp){ApplicationContextapplicationContext=http.getSharedObject(ApplicationContext.class);//去SpringIoC获取CaptchaUserDetailsS??erviceif(captchaUserDetailsS??ervice==null){captchaUserDetailsS??ervice=getBeanOrNull(applicationContext,CaptchaUserDetailsS??ervice.class);}//如果没有配置CaptchaService,去SpringIoC获取if(captchaService==null){captchaService=getBeanOrNull(applicationContext,CaptchaService.class);}//InitializeProviderCaptchaAuthenticationProvidercaptchaAuthenticationProvider=this.postProcess(newCaptchaAuthenticationProvider(captchaUserDetailsS??ervice,captchaService);//会添加到ProviderManager的注册列表中http.authenticationProvider(captchaAuthenticationProvider);}配置效果下面来看看配置效果CaptchaAuthenticationFilterConfigurer:@BeanSecurityFilterChaindefaultSecurityFilterChain(HttpSecurityhttp,UserDetailsS??erviceuserDetailsS??ervice)throwsException{http.csrf().disable().authorizeRequests().mvcMatchers("/foo/**").access("hasAuthority('ROLE_USER')").anyRequest().authenticated().and()//所有的AbstractHttpConfigurer都可以通过apply方法添加到HttpSecurity中.apply(newCaptchaAuthenticationFilterConfigurer<>())//这里直接配置验证码处理服务true方便测试.captchaService((phone,rawCode)->true)//使用手机号获取验证码,为了方便直接写在这里,映射了实际的手机和用户名。captchaUserDetailsS??ervice(phone->userDetailsS??ervice.loadUserByUsername("felord"))//默认认证成功跳转到/路径这里转化为直接返回认证信息到json.successHandler((request,response,authentication)->{//这里将认证信息以JSON形式返回给ServletServerHttpResponseservletServerHttpResponse=newServletServerHttpResponse(响应);MappingJackson2HttpMessageConvertermappingJackson2HttpMessageConverter=newMappingJackson2HttpMessageConverter();mappingJackson2HttpMessageConverter.write(认证,MediaType.APPLICATION_JSON,servletServerHttpResponse);});返回http.build();}是不是要优雅很多,解决了你自己配过滤器很多疑难杂症在学习中必须模仿。先模仿成功,然后分析思考为什么模仿成功,最后形成自己的创意。不要被一些不熟悉的概念所迷惑。有些转换不需要深入了解细节。关注公众号:Felordcn获取更多资讯个人博客:https://felord.cn