附DEMO| 绝活!Spring Security过滤器就该这么配置
2022年的开工福利已经发布,点击下面按钮获取最新PDF。
以前胖哥带大家用Spring Security过滤器实现了手机验证码认证,今天我们来改良一下验证码认证的配置方式。这绝对是绝活666,不再看、点赞一波吗?天天白嫖,晚上睡得着觉?
CaptchaAuthenticationFilter
这个验证码过滤器是通过模仿UsernamePasswordAuthenticationFilter
实现的。同样的道理,由于UsernamePasswordAuthenticationFilter
的配置是由FormLoginConfigurer
来完成的,应该也能模仿一下FormLoginConfigurer
,写一个配置类CaptchaAuthenticationFilterConfigurer
去配置CaptchaAuthenticationFilter
。
-
public final class FormLoginConfigurer<H extends HttpSecurityBuilder<H>> extends
-
AbstractAuthenticationFilterConfigurer<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.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中随处可见,建议借鉴),这样更加灵活,既能从方法配置也能自动注入。
-
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
的配置效果:
-
@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();
-
}
是不是要优雅很多,解决了你自己配置过滤器的很多疑难杂症。学习一定要模仿,先模仿成功,然后再分析思考为什么会模仿成功,最后形成自己的创造力。千万不要被一些陌生概念唬住,有些改造是不需要去深入了解细节的。
公众号私信回复 filterconfig 获取 DEMO
OAuth2授权服务器Keycloak宣布不再适配Spring Boot和Spring Security
![338261571aeba7b7612c5e941a2a46a8.png](https://img-blog.csdnimg.cn/img_convert/338261571aeba7b7612c5e941a2a46a8.png)
![90ce607cbda90cfeea386936e9642df1.png](https://img-blog.csdnimg.cn/img_convert/90ce607cbda90cfeea386936e9642df1.png)
干货福利PDF下载|Spring Security过滤器链体系
![91024c93083892664e5aa6c2bfd5c638.png](https://img-blog.csdnimg.cn/img_convert/91024c93083892664e5aa6c2bfd5c638.png)
文章来源: felord.blog.csdn.net,作者:码农小胖哥,版权归原作者所有,如需转载,请联系作者。
原文链接:felord.blog.csdn.net/article/details/122974814
- 点赞
- 收藏
- 关注作者
评论(0)