You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Security JWT场景下未知URL返回401而非404问题修复

问题分析与解决方案

问题场景

基于Spring Security整合JWT实现认证功能,已排除/user/register和/auth/signin接口的Security校验,但访问不存在的未知URL时,始终返回401认证错误,而非预期的404"方法未找到"错误。全局异常处理器中NoHandlerFoundException的处理逻辑未生效,控制台输出InsufficientAuthenticationException。

核心原因

  1. Spring Security执行顺序优先于Spring MVC:请求先经过Security过滤器链,anyRequest().authenticated()规则会对所有请求触发认证校验,未携带有效JWT时直接抛出认证异常,此时DispatcherServlet还未执行路由匹配,NoHandlerFoundException根本不会被抛出。
  2. 全局异常处理器未被Spring识别:GlobalExceptionHandler类缺少@RestControllerAdvice注解,导致其定义的异常处理逻辑不生效。
  3. Spring MVC默认不抛出404异常:即使路由匹配失败,DispatcherServlet默认返回404响应而非抛出异常,全局处理器无法捕获。

修复步骤

1. 修复全局异常处理器

给GlobalExceptionHandler添加@RestControllerAdvice注解,确保Spring能扫描并启用该异常处理器:

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<APIError> handle(MethodArgumentNotValidException exception) {
        List<ErrorDetails> errorDetailsList=exception.getBindingResult().getFieldErrors()
                .stream().map(fieldError -> new ErrorDetails(fieldError.getDefaultMessage(),fieldError.getField()))
                .collect(Collectors.toList());

        return new ResponseEntity<>(APIError.createInstance(HttpStatus.BAD_REQUEST.toString(),
                errorDetailsList), null, HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(BadRequestException.class)
    public ResponseEntity<APIError> handle(BadRequestException ex){
        return new ResponseEntity<>(APIError.createInstance(HttpStatus.BAD_REQUEST.toString(),
                List.of(new ErrorDetails(ex.getMessage(),null))), null, HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(UserNotFoundException.class)
    public ResponseEntity<APIError> handle(UserNotFoundException ex){
        return new ResponseEntity<>(APIError.createInstance(HttpStatus.NOT_FOUND.toString(),
                List.of(new ErrorDetails(ex.getMessage(),null))), null, HttpStatus.NOT_FOUND);
    }

    @ExceptionHandler(NoHandlerFoundException.class)
    public ResponseEntity<APIError> handle(NoHandlerFoundException ex){
        return new ResponseEntity<>(APIError.createInstance(HttpStatus.NOT_FOUND.toString(),
                List.of(new ErrorDetails(ex.getMessage(),null))), null, HttpStatus.NOT_FOUND);
    }

    @ExceptionHandler({ AuthenticationException.class })
    @ResponseBody
    public ResponseEntity<APIError> handleAuthenticationException(Exception ex) {
        APIError re = APIError.createInstance(HttpStatus.UNAUTHORIZED.toString(),
                List.of(new ErrorDetails(ErrorMessageCodes.INVALID_AUTHENTICATION,null)));
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(re);
    }
}

2. 配置Spring MVC抛出404异常

在application.properties中添加以下配置,让DispatcherServlet在路由匹配失败时抛出NoHandlerFoundException:

# 开启路由匹配失败时抛出异常
spring.mvc.throw-exception-if-no-handler-found=true
# 关闭静态资源自动映射,避免静态资源请求触发404异常
spring.web.resources.add-mappings=false

3. 调整Spring Security授权规则

修改WebSecurityConfigure类,注入MvcRequestMatcher.Builder,仅对已定义的MVC路由进行认证校验,未匹配的路由直接放行,交给Spring MVC处理404:

@EnableMethodSecurity
public class WebSecurityConfigure {

    @Autowired
    UserDetailsServiceImpl userDetailsService;

    @Autowired
    private AuthEntryPointJwt unauthorizedHandler;

    @Autowired
    private MvcRequestMatcher.Builder mvc;

    @Bean
    public AuthTokenFilter authenticationJwtTokenFilter() {
        return new AuthTokenFilter();
    }

    @Bean
    public DaoAuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
        authProvider.setUserDetailsService(userDetailsService);
        authProvider.setPasswordEncoder(passwordEncoder());
        return authProvider;
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
        return authConfig.getAuthenticationManager();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf(csrf -> csrf.disable())
                .exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler))
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/user").permitAll()
                        // 仅对已存在的MVC接口路由要求认证
                        .requestMatchers(mvc.pattern("/**")).authenticated()
                        // 未匹配到任何路由的请求直接放行,交由MVC处理404
                        .anyRequest().permitAll()
                );
        http.authenticationProvider(authenticationProvider());
        http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }

    @Bean
    public WebSecurityCustomizer webSecurityCustomizer(){
        return (web)->web.ignoring()
                .requestMatchers(HttpMethod.POST,"/user/register")
                .requestMatchers(HttpMethod.POST,"/auth/signin");
    }

}

效果验证

  • 访问不存在的URL时,Spring MVC会先匹配路由,抛出NoHandlerFoundException,被全局处理器捕获后返回404响应。
  • 访问受保护的合法API时,若未携带有效JWT,Spring Security会抛出认证异常,返回401响应;携带有效JWT则正常访问接口。

内容的提问来源于stack exchange,提问作者Thirumalesu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 17:29:51