自定义的SpringSecurity异常一直被全局异常捕获或者SpringSecurity异常抛出不了问题;UserNotFoundException 不抛出问题

问题描述

在项目中自定义的SpringSecurity异常不会正常执行,而是会被全局异常捕获到。解决完SpringSecurity异常被全局异常捕获的bug之后,轮到UserNotFoundException 异常一直捕获不到

 

问题分析

  • 现象:当存在全局异常处理器的时候,自定义的SpringSecurity异常拦截器处理时效,当移除全局异常处理器时,自定义的SpringSecurity异常拦截器正常启动。
  • 结论:全局异常处理器会干扰到SpringSecurity的异常处理流程,导致自定义的处理器无法按预期执行。问题出现在SpringSecurity异常处理和转换的过滤器 ExceptionTranslatonFilter 的 doFilter 方法。

 

解决方案

  • 为了确保异常能够传递给 Spring Security 的自定义异常处理器,你可以对全局异常处理器(GlobalExceptionHandler)进行修改。如果异常是 AuthenticationExceptionAccessDeniedException,则继续将其抛出以便交给自定义处理器处理。
  • 代码如下

 

注意:导入的包一定不能导入错误,不然抛出的异常会不匹配,还是导致SpringSecurity异常捕获不到。

import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.AuthenticationException; 

并且在判断的时候最好是如下语句:

// 意思是AccessDeniedException异常及其子类和AuthenticationException异常的子类都能被接收,不然之后UserDetial抛出的 UserNotFoundException 异常捕获不到 
if (AccessDeniedException.class.isInstance(e) ||  AuthenticationException.class.isInstance(e))

完整代码如下:

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 处理自定义业务异常 SystemException
     */
    @ExceptionHandler(SystemException.class)
    public ResponseResult systemExceptionHandler(SystemException e) {
        log.error("【业务异常】错误码:{},错误信息:{}", e.getCode(), e.getMsg(), e);
        return ResponseResult.errorResult(e.getCode(), e.getMsg());
    }


    /**
     * 处理其他所有未捕获的异常(兜底)
     */
    @ExceptionHandler(Exception.class)
    public ResponseResult defaultExceptionHandler(Exception e) throws Exception {
        log.error("【未知异常】{}", e.getMessage(), e);
        System.out.println(e.getClass());
        //抛出AccessDeniedException异常
        // 将 Spring Security 异常继续抛出,以便交给自定义处理器处理AuthenticationException
        if (AccessDeniedException.class.isInstance(e) ||  AuthenticationException.class.isInstance(e)) {
            throw e;
        }

        return ResponseResult.errorResult(AppHttpCodeEnum.INTERNAL_SERVER_ERROR);
    }

    // 处理参数校验异常
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseResult handleValidationExceptions(MethodArgumentNotValidException ex) {
        String errorMessages = ex.getBindingResult()
        .getAllErrors()
        .stream()
        .map(DefaultMessageSourceResolvable::getDefaultMessage)
        .collect(Collectors.joining(", "));
        return ResponseResult.errorResult(AppHttpCodeEnum.VALIDATION_FAILED.getCode(), errorMessages);
    }
}

 

/**
 * 实现了 AccessDeniedHandler 接口,用于处理 用户已经登录,但没有访问某个资源的权限时的异常处理逻辑 。
 */
@Component
@Slf4j
public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        accessDeniedException.printStackTrace();
        log.info("AccessDeniedHandler 处理异常: {}", accessDeniedException.getMessage());
        ResponseResult result = ResponseResult.errorResult(AppHttpCodeEnum.NO_OPERATOR_AUTH);
        //响应给前端
//        String json = JsonUtils.writeValueAsString(result);
        ResponseUtils.outJson(response, result);
    }
}

@Component
@Slf4j
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
//        authException.printStackTrace();
        //InsufficientAuthenticationException
        //BadCredentialsException
        ResponseResult result = null;
        //对象的类型判断,准确判断是什么类型(authException instanceof BadCredentialsException),并在全响应中处理
        if(authException instanceof UsernameNotFoundException || authException instanceof BadCredentialsException){
            // authException.getMessage() 的结果是 “用户名或密码错误"
            log.info("AuthenticationEntryPoint 处理异常: {}", authException.getMessage());
            result = ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_ERROR);
        }
        else if(authException instanceof InsufficientAuthenticationException){
            log.info("AuthenticationEntryPoint 处理异常: {}", authException.getMessage());
            result = ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }
        else if(authException instanceof InternalAuthenticationServiceException){
            log.info("AuthenticationEntryPoint 处理异常: {}", authException.getMessage());
            result = ResponseResult.errorResult(AppHttpCodeEnum.ACCOUNT_DISABLED);
        }
        else{
            log.info("AuthenticationEntryPoint 处理异常: {}", authException.getMessage());
            result = ResponseResult.errorResult(AppHttpCodeEnum.UNAUTHORIZED.getCode(),AppHttpCodeEnum.UNAUTHORIZED.getMsg());
        }
        //响应给前端
//        String json = JsonUtils.writeValueAsString(result);
        ResponseUtils.outJson(response,result);
    }
}

 

解决完上面的问题之后,出现UserNotFoundException 异常一直捕获不到

  • UserNotFoundException异常的意思是,用户名错误 异常。

代码如下:

public class UserDetailServiceImpl implements UserDetailsService

经过实践,断点之后得到结论,就是因为上面出现的异常会被一直认为是 BadCredentialsException 异常(“message是 用户名或密码错误”)。也就是说 UserNotFoundException 不可能一直被单独捕获到。

  • 解决方法

https://jitwxs.cn/d9ec6e24

 

参考文章:

https://juejin.cn/post/7309158128700620850

https://jitwxs.cn/d9ec6e24

 

 

 

posted @ 2025-06-13 09:59  ly元  阅读(102)  评论(0)    收藏  举报