无法捕获SpringSecurity的认证和鉴权中发生异常的原因
使用@ControllerAdvice的全局异常处理器无法捕获到SpringSecurity中的异常,原因如下:
在SpringSecurity中,如果认证或者授权的过程中出现了异常会被ExceptionTranslationFilter捕获到。在ExceptionTranslationFilter中会去判断是认证失败还是授权失败出现的异常:
- 如果是【认证】过程中出现的异常会被封装成AuthenticationException然后调用AuthenticationEntryPoint对象的方法去进行异常处理。
- 如果是【授权】过程中出现的异常会被封装成AccessDeniedException然后调用AccessDeniedHandler对象的方法去进行异常处理。
所以如果我们需要自定义异常处理,我们只需要实现AuthenticationEntryPoint和AccessDeniedHandler接口,然后配置给SpringSecurity即可。
代码实现
WebUtils工具类,用来渲染字符串:
public class WebUtils {
/**
* 将字符串渲染到客户端
*
* @param response 渲染对象
* @param string 待渲染的字符串
* @return null
*/
public static String renderString(HttpServletResponse response, String string) {
try {
response.setStatus(200);
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
response.getWriter().print(string);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
AuthenticationEntryPoint 自定义实现类
@Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
ResponseResult result = new ResponseResult(401, authException.getMessage());
String json = JSON.toJSONString(result);
WebUtils.renderString(response, json);
}
}
AccessDeniedHandler 自定义实现类:
@Component
public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
ResponseResult result = new ResponseResult(403, accessDeniedException.getMessage());
String json = JSON.toJSONString(result);
WebUtils.renderString(response, json);
}
}
将自定义异常处理器配置给SpringSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//注入
@Autowired
private AuthenticationEntryPoint authenticationEntryPoint;
@Autowired
private AccessDeniedHandler accessDeniedHandler;
@Override
protected void configure(HttpSecurity http) throws Exception {
//配置自定义异常处理器
http.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.accessDeniedHandler(accessDeniedHandler);
}
}
测试
授权未通过时: