ExceptionTranslationFilter
ExceptionTranslationFilter
(Security Filter
)允许将AccessDeniedException
和AuthenticationException
转换为HTTP
响应。ExceptionTranslationFilter
作为Security Filters
之一插入到FilterChainProxy
中。
- 首先,
ExceptionTranslationFilter
调用FilterChain.doFilter(request, response)
,即调用应用程序的其余部分(出现异常才执行自己的逻辑)。
- 如果用户未经身份验证或是身份验证异常,则启动身份验证。
- 清除
SecurityContextHolder
的身份验证(SEC-112
:清除SecurityContextHolder
的身份验证,因为现有身份验证不再有效)。 - 将
HttpServletRequest
保存在RequestCache
中。当用户成功进行身份验证时,RequestCache
用于重现原始请求。 AuthenticationEntryPoint
用于从客户端请求凭据。例如,它可能会重定向到登录页面或发送WWW-Authenticate
标头。
- 清除
- 否则,如果是
AccessDeniedException
,则拒绝访问。调用AccessDeniedHandler
来处理拒绝的访问。
想要了解Spring Security
的过滤器链如何在Spring
应用程序中发挥作用,可以阅读下面这篇博客:
AuthenticationEntryPoint
ExceptionTranslationFilter
会使用AuthenticationEntryPoint
启动身份验证方案。
public interface AuthenticationEntryPoint {
/**
* 启动身份验证方案
* 实现应根据需要修改ServletResponse的标头以开始身份验证过程
*/
void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException;
}
BasicAuthenticationEntryPoint
由ExceptionTranslationFilter
用于通过BasicAuthenticationFilter
开始身份验证。一旦使用BASIC
对用户代理进行身份验证,可以发送未经授权的 (401
) 标头,最简单的方法是调用BasicAuthenticationEntryPoint
类的commence
方法。 这将向浏览器指示其凭据不再被授权,导致它提示用户再次登录。
public class BasicAuthenticationEntryPoint implements AuthenticationEntryPoint,
InitializingBean {
// 领域名称
private String realmName;
// 检查属性
public void afterPropertiesSet() {
Assert.hasText(realmName, "realmName must be specified");
}
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException {
// 填充响应
response.addHeader("WWW-Authenticate", "Basic realm="" + realmName + """);
response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
}
...
}
DelegatingAuthenticationEntryPoint
AuthenticationEntryPoint
实现,它根据RequestMatcher
匹配(委托)一个具体的AuthenticationEntryPoint
。
public class DelegatingAuthenticationEntryPoint implements AuthenticationEntryPoint,
InitializingBean {
private final Log logger = LogFactory.getLog(getClass());
// RequestMatcher与AuthenticationEntryPoint的映射
private final LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> entryPoints;
// 默认AuthenticationEntryPoint
private AuthenticationEntryPoint defaultEntryPoint;
// 构造方法
public DelegatingAuthenticationEntryPoint(
LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> entryPoints) {
this.entryPoints = entryPoints;
}
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
// 遍历entryPoints
for (RequestMatcher requestMatcher : entryPoints.keySet()) {
if (logger.isDebugEnabled()) {
logger.debug("Trying to match using " + requestMatcher);
}
// 如果RequestMatcher匹配请求
if (requestMatcher.matches(request)) {
// 获取匹配请求的RequestMatcher对应的AuthenticationEntryPoint
Auth