前言:
CaptchaAuthenticationFilter 是通过模仿 UsernamePasswordAuthenticationFilter 实现的。同样的道理,由于 UsernamePasswordAuthenticationFilter 的配置是由 FormLoginConfigurer 来完成的,应该也能模仿一下 FormLoginConfigurer ,写一个配置类 CaptchaAuthenticationFilterConfigurer 去配置 CaptchaAuthenticationFilter 。
1 2 3 4 5 |
public final class FormLoginConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractAuthenticationFilterConfigurer<H, FormLoginConfigurer<H>, UsernamePasswordAuthenticationFilter> {
// 省略 } |
AbstractAuthenticationFilterConfigurer
FormLoginConfigurer 看起来有点复杂,不过继承关系并不复杂,只继承了 AbstractAuthenticationFilterConfigurer 。
1 2 3 |
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 的配置类结构可以这样定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
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 .defaultSuccessHandler; // 登录认证端点 private LoginUrlAuthenticationEntryPoint authenticationEntryPoint; // 是否 自定义页面 private boolean customLoginPage; // 登录页面 private String loginPage; // 登录成功url private String loginProcessingUrl; // 认证失败处理器 private AuthenticationFailureHandler failureHandler; // 认证路径是否放开 private boolean permitAll; // 认证失败的url private 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) { authFilter.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 ; } @Override public void init(H http) throws Exception { updateAuthenticationDefaults(); updateAccessDefaults(http); registerDefaultAuthenticationEntryPoint(http); // 这里禁用默认页面过滤器 如果你想自定义登录页面 可以自行实现 可能和FormLogin冲突 // initDefaultLoginFilter(http); // 把对应的Provider也在init时写入HttpSecurity initProvider(http); } @Override public void configure(H http) throws Exception { //这里改为使用前插过滤器方法 http.addFilterBefore(filter, LogoutFilter. class ); } // 其它方法 同AbstractAuthenticationFilterConfigurer } |
其实就是模仿 AbstractAuthenticationFilterConfigurer 及其实现类的风格把用的配置项实现一边。这里值得一提的是 CaptchaService 的配置也可以从 Spring IoC 中查找(参考 getBeanOrNull 方法,这个方法在Spring Security中随处可见,建议借鉴),这样更加灵活,既能从方法配置也能自动注入。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
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 ); } // 初始化 Provider CaptchaAuthenticationProvider captchaAuthenticationProvider = this .postProcess( new CaptchaAuthenticationProvider(captchaUserDetailsService, captchaService)); // 会增加到ProviderManager的注册列表中 http.authenticationProvider(captchaAuthenticationProvider); } |
配置类效果
我们来看看 CaptchaAuthenticationFilterConfigurer 的配置效果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
@Bean SecurityFilterChain 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(); mappingJackson2HttpMessageConverter.write(authentication, MediaType.APPLICATION_JSON,servletServerHttpResponse); }); return http.build(); } |
是不是要优雅很多,解决了你自己配置过滤器的很多疑难杂症。学习一定要模仿,先模仿成功,然后再分析思考为什么会模仿成功,最后形成自己的创造力。千万不要被一些陌生概念唬住,有些改造是不需要去深入了解细节的。
到此这篇关于Java中的Spring Security配置滤器的文章就介绍到这了,更多相关Spring Security过滤器内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!
原文链接:https://juejin.cn/post/7067184733406527501
查看更多关于Java中的Spring Security配置过滤器的详细内容...