SpringSecurity - 启动流程分析(十二)- ExceptionTranslationFilter 过滤器

本文解析了SpringSecurity中ExceptionTranslationFilter的作用,它如何捕获并转换认证和授权异常,以及如何通过自定义`AuthenticationEntryPoint`和`AccessDeniedHandler`进行异常处理。重点介绍了filter链中的位置和关键逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

SpringSecurity - 启动流程分析(四)- 默认过滤器 这篇文章中,我们知道了 ExceptionTranslationFilter 是默认提供的,排序在倒数第 2 位的过滤器,接下来我们就来看一下它的作用是什么。

概述

ExceptionTranslationFilter 字面意思是:异常转换过滤器

HttpSecurity 中的配置方法为:

public ExceptionHandlingConfigurer<HttpSecurity> exceptionHandling() throws Exception {
	return getOrApply(new ExceptionHandlingConfigurer<>());
}

// 可以接收一个函数式接口来自定义一些信息
public HttpSecurity exceptionHandling(
			Customizer<ExceptionHandlingConfigurer<HttpSecurity>> exceptionHandlingCustomizer) throws Exception {
	exceptionHandlingCustomizer.customize(getOrApply(new ExceptionHandlingConfigurer<>()));
	return HttpSecurity.this;
}

查看 ExceptionHandlingConfigurer 配置类的核心方法 configure() 方法:

@Override
public void configure(H http) {
	// 获取 AuthenticationEntryPoint
	AuthenticationEntryPoint entryPoint = getAuthenticationEntryPoint(http);
	// 核心流程,配置 ExceptionTranslationFilter
	ExceptionTranslationFilter exceptionTranslationFilter = new ExceptionTranslationFilter(entryPoint,
			getRequestCache(http));
	// 获取 AccessDeniedHandler
	AccessDeniedHandler deniedHandler = getAccessDeniedHandler(http);
	exceptionTranslationFilter.setAccessDeniedHandler(deniedHandler);
	exceptionTranslationFilter = postProcess(exceptionTranslationFilter);
	http.addFilter(exceptionTranslationFilter);
}

AccessDeniedHandler getAccessDeniedHandler(H http) {
	AccessDeniedHandler deniedHandler = this.accessDeniedHandler;
	if (deniedHandler == null) {
		// 如果没有配置,会创建默认的 AccessDeniedHandler 
		deniedHandler = createDefaultDeniedHandler(http);
	}
	return deniedHandler;
}

AuthenticationEntryPoint getAuthenticationEntryPoint(H http) {
	AuthenticationEntryPoint entryPoint = this.authenticationEntryPoint;
	if (entryPoint == null) {
		// 如果没有配置,会创建默认的 AuthenticationEntryPoint
		entryPoint = createDefaultEntryPoint(http);
	}
	return entryPoint;
}

查看具体源码,可以看到,默认情况下,无论是 AuthenticationEntryPoint 还是 AccessDeniedHandler 都会返回 403 状态码。并且 AuthenticationEntryPointAuthenticationException 异常的处理类;AccessDeniedHandler AccessDeniedException 异常的处理类。

分析

SpringSecurity 中的异常可以分为两大类,一种是认证异常,一种是授权异常。

1、AuthenticationException(认证异常)

在这里插入图片描述

2、AccessDeniedException(授权异常)

在这里插入图片描述

3、ExceptionTranslationFilter

从上面分析中我们知道,ExceptionHandlingConfigurerconfigure() 方法调用了 ExceptionTranslationFilter 的构造方法,传递了 AuthenticationEntryPoint 参数。查看核心方法 doFilter() 方法:

private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
			throws IOException, ServletException {
	try {
		chain.doFilter(request, response);
	}
	catch (IOException ex) {
		throw ex;
	}
	catch (Exception ex) {
		// Try to extract a SpringSecurityException from the stacktrace
		Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(ex);
		RuntimeException securityException = (AuthenticationException) this.throwableAnalyzer
				.getFirstThrowableOfType(AuthenticationException.class, causeChain);
		if (securityException == null) {
			securityException = (AccessDeniedException) this.throwableAnalyzer
					.getFirstThrowableOfType(AccessDeniedException.class, causeChain);
		}
		if (securityException == null) {
			// 不属于 Security 的两种异常的话在这里抛出
			rethrow(ex);
		}
		if (response.isCommitted()) {
			throw new ServletException("Unable to handle the Spring Security Exception "
					+ "because the response is already committed.", ex);
		}
		// 处理异常,这里的 securityException 已经处理过,只可能是上面两种
		handleSpringSecurityException(request, response, chain, securityException);
	}
}

private void handleSpringSecurityException(HttpServletRequest request, HttpServletResponse response,
			FilterChain chain, RuntimeException exception) throws IOException, ServletException {
	// 处理两大种类的异常
	if (exception instanceof AuthenticationException) {
		handleAuthenticationException(request, response, chain, (AuthenticationException) exception);
	}
	else if (exception instanceof AccessDeniedException) {
		handleAccessDeniedException(request, response, chain, (AccessDeniedException) exception);
	}
}
  • 查看 handleAuthenticationException 逻辑
private void handleAuthenticationException(HttpServletRequest request, HttpServletResponse response,
			FilterChain chain, AuthenticationException exception) throws ServletException, IOException {
	this.logger.trace("Sending to authentication entry point since authentication failed", exception);
	sendStartAuthentication(request, response, chain, exception);
}

protected void sendStartAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
			AuthenticationException reason) throws ServletException, IOException {
	// SEC-112: Clear the SecurityContextHolder's Authentication, as the
	// existing Authentication is no longer considered valid
	// 清除 Authentication 信息
	SecurityContext context = SecurityContextHolder.createEmptyContext();
	SecurityContextHolder.setContext(context);
	this.requestCache.saveRequest(request, response);
	// 核心流程,也是我们可以实现 AuthenticationEntryPoint 自定义处理逻辑的地方
	this.authenticationEntryPoint.commence(request, response, reason);
}
  • 查看 handleAccessDeniedException 逻辑
private void handleAccessDeniedException(HttpServletRequest request, HttpServletResponse response,
			FilterChain chain, AccessDeniedException exception) throws ServletException, IOException {
	// 判断是否是匿名用户访问
	...
		// 否则,处理异常,实现 AccessDeniedHandler 自定义处理逻辑
		this.accessDeniedHandler.handle(request, response, exception);
	}
}

总结

  • 在过滤器链中,ExceptionTranslationFilter 的下一个过滤器是 FilterSecurityInterceptorFilterSecurityInterceptor 是处理授权问题的,这里抛出的异常,会被 ExceptionTranslationFilter 捕获,来让我们做自定义处理
  • 捕获到异常之后,通过 throwableAnalyzer.getFirstThrowableOfType 来判断是认证异常还是授权异常,如果都不是的话,抛出 ServletException 或者 RuntimeException
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值