springboot+security自定义认证异常和授权异常

1、Spring Security异常 Spring Security异常主要分为两大类:

    认证异常:AuthenticationException,这个是所有认证异常的父类 BadCredentialsException登录凭证异常、UsernameNotFoundException用户名不存在异常、LockedException账户被锁定异常等 权限异常:AccessDeniedException,这个是所有权限异常的父类; CsrfException Csrf令牌异常等

2、自定义认证异常和授权异常 Spring security中一般都会在自定义配置类( WebSecurityConfigurerAdapter )中使用HttpSecurity 提供的 exceptionHandling() 方法用来提供异常处理。该方法构造出 ExceptionHandlingConfigurer 异常处理配置类。该配置类提供了两个实用接口:

    AuthenticationEntryPoint:该类用来统一处理 AuthenticationException 异常; AccessDeniedHandler:该类用来统一处理 AccessDeniedException 异常;

我们只要实现并配置这两个异常处理类即可实现对 Spring Security 认证和授权相关的异常进行统一的自定义处理。

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and().formLogin()
                //开启异常处理
                .and().exceptionHandling()
                //处理认证异常
                .authenticationEntryPoint((request,response,e)->{
                    if(e instanceof LockedException){
                        //e是走的父类AuthenticationException,针对指定子类异常可以自定义一些逻辑
                    }
                    response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
                    response.setStatus(HttpStatus.UNAUTHORIZED.value());
                    response.getWriter().write("没有认证");
                })
                //处理授权异常
                .accessDeniedHandler((request,response,e)->{
                    response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
                    response.setStatus(HttpStatus.FORBIDDEN.value());
                    response.getWriter().write("授权异常");
                })
                .and().csrf().disable();
    }
}

3、自定义全局异常 如果是springboot+security项目,并且定义了全局异常,那上面的授权异常AccessDeniedException及子类和认证异常AuthenticationException及子类都会优先被全局异常执行,导致security配置的异常处理不起作用,主要原因是执行先后的问题,spring请求中各组件执行顺序如下:Controller》Aspect》ControllerAdvice》Interceptor》Filter。 可见,当发生授权异常AccessDeniedException及子类和认证异常AuthenticationException及子类都会优先被全局异常ControllerAdvice处理,所以如果还是想让security自己处理,我们在全局异常中捕获到上述异常,直接抛出就行,代码如下

@RestControllerAdvice
public class GlobalExceptionHandler {
 
    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
 
    //security的授权异常(AccessDeniedException及子类)抛出交由security AuthenticationEntryPoint 处理
    @ExceptionHandler(AccessDeniedException.class)
    public void accessDeniedException(AccessDeniedException e) throws AccessDeniedException {
        throw e;
    }
 
    //security的认证异常(AuthenticationException及子类)抛出由security AccessDeniedHandler 处理
    @ExceptionHandler(AuthenticationException.class)
    public void authenticationException(AuthenticationException e) throws AuthenticationException {
        throw e;
    }
 
    //未知异常
    @ExceptionHandler(Exception.class)
    public Result otherException(Exception e) {
        e.printStackTrace();
        logger.error("系统异常 全局拦截异常信息:{}",e.getMessage());
        return Result.error(e.getMessage());
    }
}
经验分享 程序员 微信小程序 职场和发展