I hope to add a feature for registering filters. When I want to develop a class similar to FormLogiConfigurer, I need to manually add Filter to register my implementation of a filter similar to UsernamePasswordAuthenticationFilter into the interceptor chain. However, if I use the configuration class in HttpSecurity. apply, I will add this interceptor again to the interceptor chain. If the configuration class is directly applied, an exception will be thrown, 'The Filter class xxx does not have a registered order and cannot be added without a specified order. Advisor using addFilterBefore or addFilterAfter instance.' At this point, I have to use reflection to manually add to the interceptor chain registrar instead of adding to the interceptor chain instance

My code is as follows

package com.example.demo.security;

import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class SmsAuthorizationConfigurer<H extends HttpSecurity> extends AbstractAuthenticationFilterConfigurer<HttpSecurity, SmsAuthorizationConfigurer<H>, SmsAuthorizationFilter> {

    private static final String DEFAULT_LOGIN_PROCESSING_URL = "/sms/login";

    public SmsAuthorizationConfigurer() {
        super(new SmsAuthorizationFilter(), DEFAULT_LOGIN_PROCESSING_URL);
    }

    @Override
    protected RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl) {
        return new AntPathRequestMatcher(loginProcessingUrl, "POST");
    }

    @Override
    public SmsAuthorizationConfigurer<H> loginPage(String loginPage) {
        return super.loginPage(loginPage);
    }

    public SmsAuthorizationConfigurer<H> mobileParameter(String mobileParameter) {
        getAuthenticationFilter().setMobileParameter(mobileParameter);
        return this;
    }

    public SmsAuthorizationConfigurer<H> smsCodeParameter(String smsCodeParameter) {
        getAuthenticationFilter().setSmsCodeParameter(smsCodeParameter);
        return this;
    }

    @Override
    public void init(HttpSecurity http) throws Exception {
        super.init(http);
        Class<?> aClass = Class.forName("org.springframework.security.config.annotation.web.builders.FilterOrderRegistration");
        Method put = aClass.getDeclaredMethod("put", Class.class, int.class);
        put.setAccessible(true);
        Method getOrder = aClass.getDeclaredMethod("getOrder", Class.class);
        getOrder.setAccessible(true);
        Field filterOrders = http.getClass().getDeclaredField("filterOrders");
        filterOrders.setAccessible(true);
        Object filterOrderRegistration = filterOrders.get(http);
        Integer order = (Integer) getOrder.invoke(filterOrderRegistration, UsernamePasswordAuthenticationFilter.class);
        put.invoke(filterOrderRegistration, getAuthenticationFilter().getClass(), order + 1);
    }
}
package com.example.demo.config;

import com.example.demo.security.SmsAuthorizationConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.apply(new SmsAuthorizationConfigurer<>()).mobileParameter("phone").smsCodeParameter("code");
        httpSecurity.csrf().disable();
        return httpSecurity.build();
    }

    @Bean
    UserDetailsService userDetailsService() {
        UserDetails userDetails = User.builder()
                .username("user")
                .password("{noop}123456")
                .authorities("test1").build();
        return new InMemoryUserDetailsManager(userDetails);
    }
}

package com.example.demo.security;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.lang.Nullable;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.Assert;

public class SmsAuthorizationFilter extends AbstractAuthenticationProcessingFilter {

    public static final String SPRING_SECURITY_FORM_MOBILE_KEY = "mobile";

    public static final String SPRING_SECURITY_FORM_SMS_CODE_KEY = "smsCode";
    public static final String DEFAULT_REQUEST_MATCHER= "/sms/login";

    private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher(DEFAULT_REQUEST_MATCHER,
            "POST");

    private String mobileParameter = SPRING_SECURITY_FORM_MOBILE_KEY;

    private String smsCodeParameter = SPRING_SECURITY_FORM_SMS_CODE_KEY;

    private boolean postOnly = true;
    public SmsAuthorizationFilter() {
        super(DEFAULT_ANT_PATH_REQUEST_MATCHER);
    }

    public SmsAuthorizationFilter(AuthenticationManager authenticationManager) {
        super(DEFAULT_ANT_PATH_REQUEST_MATCHER, authenticationManager);
    }


    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException{
        if (this.postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        }
        String mobile = obtainMobile(request);
        mobile = (mobile != null) ? mobile.trim() : "";
        String smsCode = obtainSmsCode(request);
        smsCode = (smsCode != null) ? smsCode : "";
        SmsAuthenticationToken authRequest = SmsAuthenticationToken.unauthenticated(mobile,
                smsCode);
        setDetails(request, authRequest);
        return this.getAuthenticationManager().authenticate(authRequest);
    }

    protected void setDetails(HttpServletRequest request, SmsAuthenticationToken authRequest) {
        authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
    }

    @Nullable
    protected String obtainMobile(HttpServletRequest request) {
        return request.getParameter(this.mobileParameter);
    }

    @Nullable
    protected String obtainSmsCode(HttpServletRequest request) {
        return request.getParameter(this.smsCodeParameter);
    }

    public void setMobileParameter(String mobileParameter) {
        Assert.hasText(mobileParameter, "mobile parameter must not be empty or null");
        this.mobileParameter = mobileParameter;
    }

    public void setSmsCodeParameter(String mobileParameter) {
        Assert.hasText(mobileParameter, "smsCode parameter must not be empty or null");
        this.smsCodeParameter = mobileParameter;
    }

    public final String getMobileParameter() {
        return this.mobileParameter;
    }


    public final String getSmsCodeParameter() {
        return this.smsCodeParameter;
    }
}

package com.example.demo.security;

import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityCoreVersion;

import java.util.Collection;

public class SmsAuthenticationToken extends AbstractAuthenticationToken {

    private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;

    private final Object principal;

    private Object credentials;

    public SmsAuthenticationToken(Object principal, Object credentials) {
        super(null);
        this.principal = principal;
        this.credentials = credentials;
        setAuthenticated(false);
    }

    public SmsAuthenticationToken(Object principal, Object credentials,
                                  Collection<? extends GrantedAuthority> authorities) {
        super(authorities);
        this.principal = principal;
        this.credentials = credentials;
        super.setAuthenticated(true);
    }

    public static SmsAuthenticationToken unauthenticated(Object principal, Object credentials) {
        return new SmsAuthenticationToken(principal, credentials);
    }

    @Override
    public Object getCredentials() {
        return credentials;
    }

    @Override
    public Object getPrincipal() {
        return principal;
    }
}

Comment From: jzheaux

Thanks for getting in touch! It feels like this is a question that would be better suited to Stack Overflow. We prefer to use GitHub issues only for bugs and enhancements. Feel free to update this issue with a link to the re-posted question (so that other people can find it) or add more detail if you feel this is a genuine bug.