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());
}
}