Describe the bug

@RestControllerAdvice annotation does not intercept AuthenticationException.

To Reproduce Steps to reproduce the behavior.

My situation is as follows: only a few interfaces need to log in, and most of the other interfaces do not need to log in, so I put the interfaces that need to log in into AUTH_LIST, and then configure them as authenticated, and configure the other interfaces as permitAll

    public static final List<String> AUTH_LIST = Arrays.asList(
            "/event",
            "/events",
            "/integral",
            "/integral/logs"
    );

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        // Disable CSRF (cross site request forgery)
        http.csrf().disable();
        // Disable Frame
        http.headers().frameOptions().disable();

        // No session will be created or used by spring security
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        http.authorizeRequests()
                // The pattern does not contain the servlet context-path, see AntPathRequestMatcher.matcher method.
                .antMatchers(AUTH_LIST.toArray(new String[0]))
                .authenticated()
                .anyRequest()
                .permitAll();

        // Handle AuthenticationException and AccessDeniedException
        http.exceptionHandling()
                .authenticationEntryPoint(authenticationEntryPoint)
                .accessDeniedHandler(accessDeniedHandler);

        // Add JwtAuthenticationFilter
        http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }

Global exception handling is as follows:

@RestControllerAdvice
public class ZhiYuanGlobalExceptionHandler {

    @ExceptionHandler({AuthenticationException.class})
    public void handleAuthenticationException(AuthenticationException ae) {
        throw ae;
    }
}

the UserDetailsService implementation class is as follows:

package io.github.llnancy.zhiyuan.security;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.base.Preconditions;
import io.github.llnancy.mojian.base.enums.TableStatusFieldEnum;
import io.github.llnancy.zhiyuan.repository.entity.UserEntity;
import io.github.llnancy.zhiyuan.repository.mapper.UserMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.Objects;

/**
 * UserDetailsService Impl
 *
 * @author sunchaser admin@lilu.org.cn
 * @since JDK8 2022/11/2
 */
@Component
@RequiredArgsConstructor
@Slf4j
public class UserDetailsServiceImpl implements UserDetailsService {

    private final UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String userId) throws BadCredentialsException {
        UserEntity userEntity = userMapper.selectOne(
                Wrappers.<UserEntity>lambdaQuery()
                        .eq(UserEntity::getUserId, userId)
        );
        if (Objects.isNull(userEntity)) {
            // UsernameNotFoundException 异常默认会被 Spring Security 转化为 BadCredentialsException
            // 对应错误信息为:用户名或密码错误。详见 AbstractUserDetailsAuthenticationProvider.java:141
            throw new UsernameNotFoundException("用户名或密码错误");
        }
        Preconditions.checkArgument(ObjectUtils.notEqual(TableStatusFieldEnum.FORBIDDEN.ordinal(), userEntity.getIsDeleted()), "对不起,您的账号已被停用,请联系管理员");
        return new LoginUser(userEntity, Collections.emptyList());
    }
}

When I use a non-existent username and password to access the /integral interface that needs to be logged in, a UsernameNotFoundException will be thrown in the loadUserByUsername method, and @RestControllerAdvice does not intercept it at this time.

I have ensured that the IOC container can scan these beans.

I have tried setting the interfaces that do not require login to permitAll, and the other interfaces to authenticated, which works normally, but the number of interfaces that do not require login in my project is much greater than the number of interfaces that need to be logged in, so what I expect is, List the interface that needs to be logged in, set it as authenticated, and set other interfaces as permitAll.

I don’t know if I wrote something wrong or the configuration is wrong, if so, please let me know, thank you very much.

Expected behavior

List the interface that needs to be logged in, set it as authenticated, and set other interfaces as permitAll.

http.authorizeRequests()
        .antMatchers(AUTH_LIST.toArray(new String[0]))
        .authenticated()
        .anyRequest()
        .permitAll();

In this way, @RestControllerAdvice and @ExceptionHandler({AuthenticationException.class}) can intercept the UsernameNotFoundException exception thrown in the UserDetailsService implementation class.

Sample

not yet available.

A link to a GitHub repository with a minimal, reproducible sample.

Reports that include a sample will take priority over reports that do not. At times, we may require a sample, so it is good to try and include a sample up front.

Comment From: llnancy

Version Information:

Spring Boot: 2.7.11

<spring-security.version>5.7.8</spring-security.version>

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.