In the past, We used Spring Security filter to implement captcha authentication. Today, we will improve the configuration of captcha authentication, which is more consistent with the design style of Spring Security, and more internal.
CaptchaAuthenticationFilter by imitating UsernamePasswordAuthenticationFilter. In the same way, as a result of UsernamePasswordAuthenticationFilter configuration is done by FormLoginConfigurer, should also can imitate the FormLoginConfigurer, Write a configuration class CaptchaAuthenticationFilter CaptchaAuthenticationFilterConfigurer to configuration.
public final class FormLoginConfigurer<H extends HttpSecurityBuilder<H>> extends
AbstractAuthenticationFilterConfigurer<H.FormLoginConfigurer<H>, UsernamePasswordAuthenticationFilter> {
/ / to omit
}
Copy the code
AbstractAuthenticationFilterConfigurer
Inheritance FormLoginConfigurer looks a bit complicated, but is not complicated, just inherited AbstractAuthenticationFilterConfigurer.
public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecurityBuilder<B>, T extends AbstractAuthenticationFilterConfigurer<B.T.F>, F extends AbstractAuthenticationProcessingFilter>
extends AbstractHttpConfigurer<T.B> {}Copy the code
In theory, let’s emulate and inherit this class, but you’ll see that doesn’t work that way. Because AbstractAuthenticationFilterConfigurer only Spring Security for internal use, the custom is not recommended. The reason is that it finally adds a Filter to HttpSecurity using the httpSecurity.addFilter (Filter) method, which is only available with built-in filters (see FilterOrderRegistration). Once we understand this mechanism, we can only go up one level and modify its parent class AbstractHttpConfigurer.
Transformation process
AbstractAuthenticationFilterConfigurer < B, T, F > HttpSecurity the B is practical in, so that to keep;
T refers to itself, we don’t need to configure CaptchaAuthenticationFilter sinking a layer to FormLoginConfigurer level of this inheritance, Directly in the inheritance AbstractAuthenticationFilterConfigurer level, therefore T here means the need to configure the class itself, also no longer need to abstraction, so don’t need; For the same reason F don’t need, is CaptchaAuthenticationFilter is clear, no longer need to generalization. Such CaptchaAuthenticationFilter configuration class structure can be defined like this:
public class CaptchaAuthenticationFilterConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<CaptchaAuthenticationFilterConfigurer<H>, H> {
// No more generalization concretization
private final CaptchaAuthenticationFilter authFilter;
// Specific captcha user service
private CaptchaUserDetailsService captchaUserDetailsService;
// Captcha processing service
private CaptchaService captchaService;
// The policy to save the authentication request details
privateAuthenticationDetailsSource<HttpServletRequest, ? > authenticationDetailsSource;// The save request authentication success handler is used by default
private SavedRequestAwareAuthenticationSuccessHandler defaultSuccessHandler = new SavedRequestAwareAuthenticationSuccessHandler();
// Authenticate the successful handler
private AuthenticationSuccessHandler successHandler = this.defaultSuccessHandler;
// Log in to the authentication endpoint
private LoginUrlAuthenticationEntryPoint authenticationEntryPoint;
// Whether to customize the page
private boolean customLoginPage;
// Login page
private String loginPage;
// Successful login URL
private String loginProcessingUrl;
// Failed to authenticate the processor
private AuthenticationFailureHandler failureHandler;
// Whether to release the authentication path
private boolean permitAll;
// Failed authentication URL
private String failureUrl;
/** * Creates a new instance with minimal defaults */
public CaptchaAuthenticationFilterConfigurer(a) {
setLoginPage("/login/captcha");
this.authFilter = new CaptchaAuthenticationFilter();
}
public CaptchaAuthenticationFilterConfigurer<H> formLoginDisabled(a) {
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);
// Disable the default page filter here. If you want to customize the login page, you can implement it yourself
// initDefaultLoginFilter(http);
// Write the corresponding Provider to HttpSecurity in init
initProvider(http);
}
@Override
public void configure(H http) throws Exception {
// Use the forward-inserted filter method instead
http.addFilterBefore(filter, LogoutFilter.class);
}
/ / other methods with AbstractAuthenticationFilterConfigurer
}
Copy the code
Is actually imitating AbstractAuthenticationFilterConfigurer and its implementation class style make use of configuration items. It is worth noting that the configuration of CaptchaService can also be found in Spring IoC (refer to the getBeanOrNull method, which is ubiquitous in Spring Security), which is more flexible and can be either configured from the method or injected automatically.
private void initProvider(H http) {
ApplicationContext applicationContext = http.getSharedObject(ApplicationContext.class);
/ / no configuration CaptchaUserDetailsService Spring IoC access
if (captchaUserDetailsService == null) {
captchaUserDetailsService = getBeanOrNull(applicationContext, CaptchaUserDetailsService.class);
}
// Go to Spring IoC without CaptchaService
if (captchaService == null) {
captchaService = getBeanOrNull(applicationContext, CaptchaService.class);
}
// Initialize the Provider
CaptchaAuthenticationProvider captchaAuthenticationProvider = this.postProcess(new CaptchaAuthenticationProvider(captchaUserDetailsService, captchaService));
// will be added to the ProviderManager registration list
http.authenticationProvider(captchaAuthenticationProvider);
}
Copy the code
Configuration Class Effect
Let’s take a look at CaptchaAuthenticationFilterConfigurer configuration effect:
@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, UserDetailsService userDetailsService) throws Exception {
http.csrf().disable()
.authorizeRequests()
.mvcMatchers("/foo/**").access("hasAuthority('ROLE_USER')")
.anyRequest().authenticated()
.and()
// All AbstractHttpConfigurer AbstractHttpConfigurer can be added to HttpSecurity using the Apply method
.apply(new CaptchaAuthenticationFilterConfigurer<>())
// Configure the captcha processing service
.captchaService((phone, rawCode) -> true)
// Get the verification code from the phone number
.captchaUserDetailsService(phone -> userDetailsService.loadUserByUsername("felord"))
// By default, the authentication information is returned directly to json
.successHandler((request, response, authentication) -> {
// The authentication information is returned as JSON
ServletServerHttpResponse servletServerHttpResponse = new ServletServerHttpResponse(response);
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
mappingJackson2HttpMessageConverter.write(authentication, MediaType.APPLICATION_JSON,servletServerHttpResponse);
});
return http.build();
}
Copy the code
Is it a lot more elegant, to solve your own configuration of the filter a lot of difficult problems. Learning must imitate, first imitate success, and then analyze why to imitate success, and finally form their own creativity. Don’t be fooled by some unfamiliar concepts. Some modifications don’t require in-depth knowledge of the details.
Follow our public id: Felordcn for more information
Personal blog: https://felord.cn