Spring Security JWT场景下未知URL返回401而非404问题修复
问题分析与解决方案
问题场景
基于Spring Security整合JWT实现认证功能,已排除/user/register和/auth/signin接口的Security校验,但访问不存在的未知URL时,始终返回401认证错误,而非预期的404"方法未找到"错误。全局异常处理器中NoHandlerFoundException的处理逻辑未生效,控制台输出InsufficientAuthenticationException。
核心原因
- Spring Security执行顺序优先于Spring MVC:请求先经过Security过滤器链,
anyRequest().authenticated()规则会对所有请求触发认证校验,未携带有效JWT时直接抛出认证异常,此时DispatcherServlet还未执行路由匹配,NoHandlerFoundException根本不会被抛出。 - 全局异常处理器未被Spring识别:
GlobalExceptionHandler类缺少@RestControllerAdvice注解,导致其定义的异常处理逻辑不生效。 - 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
相关产品推荐
相关产品推荐

