spring-projects / spring-projects/spring-security

Endpoint filters should be reachable when using `spring.mvc.servlet.path`

Open
#14,230 5 comments 0 reactions 1 assignee View on GitHub

@jzheaux is already working on this.

Since Dec 4, 2023.

in: config type: enhancement
Dominant language
Java
Stars
9.6k
Forks
6.3k
Avg merge
2d 11h
Merged PRs (30d)
52

Description

Expected Behavior

DefaultLoginPageGeneratingFilter supports manual path prefix, such as ${spring.mvc.servlet.path} prefix.

As the issue #14188 that I submitted, DefaultLoginPageGeneratingFilter doesn't work while ${spring.mvc.servlet.path} property is not '/'. due to DefaultLoginPageGeneratingFilter only process fixed url path: /login.

My current solution is to hack FormLoginConfigurer class, like bellow codes:

@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception
{
    // Management ENdpoints Base Path is '/__admin'
    String endpointsWebBasePath = AppConfigHolder.getManagementEndpointsBasePath();
    
    if(GeneralHelper.isStrEmpty(endpointsWebBasePath) || endpointsWebBasePath.equals("/"))
        http.authorizeHttpRequests((authorizeHttpRequests) -> authorizeHttpRequests.anyRequest().permitAll()).build();
    else
    {
        // Spring MVC Servlet Path is '/aaa'
        String mvcServletPath = AppConfigHolder.getSpringMvcServletPath();
        // prefix == '/aaa'
        String prefix = (GeneralHelper.isStrNotEmpty(mvcServletPath) && !mvcServletPath.equals("/")) ? mvcServletPath : "";
        // managementBasePath == "/aaa/__admin/**"
        String managementBasePath = prefix + endpointsWebBasePath + "/**";
        
        http
        .authorizeHttpRequests((authorizeHttpRequests) -> authorizeHttpRequests
            .requestMatchers(AntPathRequestMatcher.antMatcher(managementBasePath)).authenticated()
            .requestMatchers(AntPathRequestMatcher.antMatcher("/**")).permitAll())
        .csrf((csrf) -> csrf.disable())
        .httpBasic(Customizer.withDefaults())
        .formLogin(new Customizer<FormLoginConfigurer<HttpSecurity>>()
        {
            @Override
            public void customize(FormLoginConfigurer<HttpSecurity> cfg)
            {
                if(GeneralHelper.isStrEmpty(prefix))
                    return;
				
                // if prefix == '/aaa', then set login page to '/aaa/login'
                cfg.loginPage(prefix + DefaultLoginPageGeneratingFilter.DEFAULT_LOGIN_PAGE_URL);
                
                try
                {
                    final String FIELD_NAME = "customLoginPage";
                    Field customLoginPage   = AbstractAuthenticationFilterConfigurer.class.getDeclaredField(FIELD_NAME);
                    
                    // Because loginPage() set the field 'customLoginPage' of FormLoginConfigurer to true automatically, DefaultLoginPageGeneratingFilter would be ignored.  
                    // Cheat FormLoginConfigurer and reset it's 'customLoginPage' to false.
                    // Ugly, but work fine.
                    customLoginPage.setAccessible(true);
                    customLoginPage.set(cfg, false);
                }
                catch(Exception e)
                {
                    throw new RuntimeException("set actuator login page fail", e);
                }
            }
        });
    }
    
    return http.build();
}

Conclusion

FormLoginConfigurer provides another loginPage() like loginPage(String loginPage, boolean customLoginPage) to replace the hack code above, and then DefaultLoginPageGeneratingFilter would supports manual path prefix.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.