AI智能
改变未来

Spring Security配置个过滤器也这么卷

以前胖哥带大家用Spring Security过滤器实现了验证码认证,今天我们来改良一下验证码认证的配置方式,更符合Spring Security的设计风格,也更加内卷。

CaptchaAuthenticationFilter

是通过模仿

UsernamePasswordAuthenticationFilter

实现的。同样的道理,由于

UsernamePasswordAuthenticationFilter

的配置是由

FormLoginConfigurer

来完成的,应该也能模仿一下

FormLoginConfigurer

,写一个配置类

CaptchaAuthenticationFilterConfigurer

去配置

CaptchaAuthenticationFilter

public final class FormLoginConfigurer<H extends HttpSecurityBuilder<H>> extendsAbstractAuthenticationFilterConfigurer<H, FormLoginConfigurer<H>, UsernamePasswordAuthenticationFilter> {// 省略}

AbstractAuthenticationFilterConfigurer

FormLoginConfigurer

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

AbstractAuthenticationFilterConfigurer

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

理论上我们模仿一下,也继承一下这个类,但是你会发现这种方式行不通。因为

AbstractAuthenticationFilterConfigurer

只能Spring Security内部使用,不建议自定义。原因在于它最终向

HttpSecurity

添加过滤器使用的是

HttpSecurity.addFilter(Filter)

方法,这个方法只有内置过滤器(参见

FilterOrderRegistration

)才能使用。了解了这个机制之后,我们只能往上再抽象一层,去改造其父类

AbstractHttpConfigurer

改造过程

AbstractAuthenticationFilterConfigurer<B,T,F>

中的

B

是实际指的

HttpSecurity

,因此这个要保留;

T

指的是它本身的实现,我们配置

CaptchaAuthenticationFilter

不需要下沉一层到

FormLoginConfigurer

这个继承级别,直接在

AbstractAuthenticationFilterConfigurer

这个继承级别实现即可,因此

T

这里指的就是需要配置类本身,也不需要再抽象化,因此是不需要的;同样的原因

F

也不需要,很明确是

CaptchaAuthenticationFilter

,不需要再泛化。这样

CaptchaAuthenticationFilter

的配置类结构可以这样定义:

public class CaptchaAuthenticationFilterConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<CaptchaAuthenticationFilterConfigurer<H>, H> {// 不再泛化  具体化private final CaptchaAuthenticationFilter authFilter;// 特定的验证码用户服务private CaptchaUserDetailsService captchaUserDetailsService;// 验证码处理服务private CaptchaService captchaService;// 保存认证请求细节的策略private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource;// 默认使用保存请求认证成功处理器private SavedRequestAwareAuthenticationSuccessHandler defaultSuccessHandler = new SavedRequestAwareAuthenticationSuccessHandler();// 认证成功处理器private AuthenticationSuccessHandler successHandler = this.defaultSuccessH56candler;// 登录认证端点private LoginUrlAuthenticationEntryPoint authenticationEntryPoint;// 是否 自定义页面private boolean customLoginPage;// 登录页面private String loginPage;// 登录成功urlprivate String loginProcessingUrl;// 认证失败处理器private AuthenticationFailureHandler failureHandler;// 认证路径是否放开private boolean permitAll;//  认证失败的urlprivate String failureUrl;/*** Creates a new instance with minimal defaults*/public CaptchaAuthenticationFilterConfigurer() {setLoginPage(\"/login/captcha\");this.authFilter = new CaptchaAuthenticationFilter();}public CaptchaAuthenticationFilterConfigurer<H> formLoginDisabled() {this.formLoginEnabled = false;return this;}public CaptchaAuthenticationFilterConfigurer<H> captchaUserDetailsService(CaptchaUserDetailsService captchaUserDetailsService) {this.captchaUserDetailsService = captchaUserDetailsService;return this;}public CaptchaAuthenticationFilterConfigurer<H> captchaService(CaptchaService captchaService) {this.captchaService = captchaService;return this;}public CaptchaAuthenticationFilterConfigurer<H> usernameParameter(String usernameParameter) {56cauthFilter.setUsernameParameter(usernameParameter);return this;}public CaptchaAuthenticationFilterConfigurer<H> captchaParameter(String captchaParameter) {authFilter.setCaptchaParameter(captchaParameter);return this;}public CaptchaAuthenticationFilterConfigurer<H> parametersConverter(Converter<HttpServletRequest, CaptchaAuthenticationToken> converter) {authFilter.setConverter(converter);return this;}@Overridepublic void init(H http) throws Exception {updateAuthenticationDefaults();updateAccessDefaults(http);registerDefaultAuthenticationEntryPoint(http);// 这里禁用默认页面过滤器 如果你想自定义登录页面 可以自行实现 可能和FormLogin冲突// initDefaultLoginFilter(http);// 把对应的Provider也在init时写入HttpSecurityinitProvider(http);}@Overridepublic void configure(H http) throws Exception {//这里改为使用前插过滤器方法http.addFilterBefore(filter, LogoutFilter.class);}// 其它方法 同AbstractAuthenticationFilterConfigurer}

其实就是模仿

AbstractAuthenticationFilterConfigurer

及其实现类的风格把用的配置项实现一边。这里值得一提的是ad0

CaptchaService

的配置也可以从Spring IoC中查找(参考

getBeanOrNull

方法,这个方法在Spring Security中随处可见,建议借鉴),这样更加灵活,既能从方法配置也能自动注入。

private void initProvider(H http) {ApplicationContext applicationContext = http.getSharedObject(ApplicationContext.class);// 没有配置CaptchaUserDetailsService就去Spring IoC获取if (captchaUserDetailsService == null) {captchaUserDetailsService = getBeanOrNull(applicationContext, CaptchaUserDetailsService.class);}// 没有配置CaptchaService就去Spring IoC获取if (captchaService == null) {captchaService = getBeanOrNull(applicationContext, CaptchaService.class);}// 初始化 ProviderCaptchaAuthenticationProvider captchaAuthenticationProvider = this.postProcess(new CaptchaAuthenticationProvider(captchaUserDetailsService, captchaService));// 会增加到ProviderManager的注册列表中http.authenticationProvider(captchaAuthenticationProvider);}

配置类效果

我们来看看

CaptchaAuthenticationFilterConfigurer

的配置效果:

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

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

关注公众号:Felordcn  获取更多资讯

个人博客:https://felord.cn

赞(0) 打赏
未经允许不得转载:爱站程序员基地 » Spring Security配置个过滤器也这么卷