Skip to content

ProxyingHandlerMethodArgumentResolver conflicts with @AuthenticationPrincipal #2937

Closed
@spadou

Description

@spadou

In a Spring application (with Spring Boot 3.1.3 currently), using data and security the ProxyingHandlerMethodArgumentResolver will conflict with the use of @AuthenticationPrincipal in a controller when a custom interface is used for the principal. The ProxyingHandlerMethodArgumentResolver will be registered before AuthenticationPrincipalArgumentResolver and handle the argument as a projection in the fallback preventing @AuthenticationPrincipal from working.

I see this was improved in #1237, but only the Spring packages are filtered out. In my case I use a custom interface for the principal to inject due to others requirements, so it is not ignored by the ProxyingHandlerMethodArgumentResolver. The list of ignored packages also doesn't seem configurable so it is not an option to handle the issue. I could also exclude the SpringDataWebAutoConfiguration but this exclude everything (like pageable) so it is not ideal.

There is already a @ProjectedPayload in ProxyingHandlerMethodArgumentResolver so wouldn't it make sense to remove the fallback as it could cause issues in multiple situations?

Here a small example that reproduce the issue:

@SpringBootApplication
public class ArgumentResolverConflictApplication {

    public static void main(String[] args) {
        SpringApplication.run(ArgumentResolverConflictApplication.class, args);
    }

    @RestController
    public static class ArgumentResolverConflictRestController {

        @GetMapping("/principal")
        String principal(@AuthenticationPrincipal CustomUserDetails principal) {
            return principal.getUsername();
        }
    }

    @Bean
    public InMemoryUserDetailsManager inMemoryUserDetailsManager() {
        return new InMemoryUserDetailsManager(User.withUsername("user").password("{noop}password").build()) {
            @Override
            public UserDetails loadUserByUsername(String username) {
                var user = super.loadUserByUsername(username);
                return new CustomUser(user.getUsername(), user.getPassword(), user.isEnabled(), user.isAccountNonExpired(), user.isCredentialsNonExpired(),
                        user.isAccountNonLocked(), user.getAuthorities());
            }
        };
    }

    public interface CustomUserDetails extends UserDetails {
    }

    public static class CustomUser extends User implements CustomUserDetails {
        public CustomUser(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked,
                          Collection<? extends GrantedAuthority> authorities) {
            super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
        }
    }
}

If this is run with security and data (jdbc for example), the principal wont be injected in the controller but we'll have an empty projection instead. If SpringDataWebAutoConfiguration is excluded, it works as expected.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions