前面我们讲了 Spring Security 框架中承载着承上启下的作用的 Filter:ExceptionTranslationFilter。其中,当后续 Filter 发生 AuthenticationException 异常,或者是 AccessDeniedException 异常且此时为未登录状态或者是记住我状态,则调用 AuthenticationEntryPoint 来处理。
一般情况下,如无特殊需求,我们不会对 Spring Security 的异常处理做什么配置,那么系统又是如何自动化配置 ExceptionTranslationFilter 的呢?
本文我们就来说一下 ExceptionTranslationFilter 的自动化配置,顺带简介一下 AuthenticationEntryPoint,以及 Spring Security 框架是如何配置 AuthenticationEntryPoint 到 ExceptionTranslationFilter 中的。
其在 Spring Security 中异常处理的配置如下。
http.exceptionHandling()
.accessDeniedPage()
.accessDeniedHandler()
.authenticationEntryPoint()
.defaultAccessDeniedHandlerFor()
.defaultAuthenticationEntryPointFor();
那么,默认情况下是如何配置的呢?
这就要看一下其配置类 ExceptionHandlingConfigurer 了。
正如其在 Spring Security 中异常处理的配置所述,可以设置 accessDeniedPage、accessDeniedHandler、authenticationEntryPoint 等参数。
配置完成后,和其它 Spring Security 中的配置类一样,进行初始化配置。
public void configure(H http) throws Exception {
AuthenticationEntryPoint entryPoint = getAuthenticationEntryPoint(http);
ExceptionTranslationFilter exceptionTranslationFilter = new ExceptionTranslationFilter(
entryPoint, getRequestCache(http));
AccessDeniedHandler deniedHandler = getAccessDeniedHandler(http);
exceptionTranslationFilter.setAccessDeniedHandler(deniedHandler);
exceptionTranslationFilter = postProcess(exceptionTranslationFilter);
http.addFilter(exceptionTranslationFilter);
}
可以看到,正是在此处,Spring Security 框架创建了 ExceptionTranslationFilter,并为其赋了属性的初始化值。
其中,AuthenticationEntryPoint 的初始化逻辑由getAuthenticationEntryPoint 方法完成。
AuthenticationEntryPoint getAuthenticationEntryPoint(H http) {
AuthenticationEntryPoint entryPoint = this.authenticationEntryPoint;
if (entryPoint == null) {
entryPoint = createDefaultEntryPoint(http);
}
return entryPoint;
}
如果没有设置 authenticationEntryPoint 属性,则由 createDefaultEntryPoint 方法创建默认值。
private AuthenticationEntryPoint createDefaultEntryPoint(H http) {
if (this.defaultEntryPointMappings.isEmpty()) {
return new Http403ForbiddenEntryPoint();
}
if (this.defaultEntryPointMappings.size() == 1) {
return this.defaultEntryPointMappings.values().iterator().next();
}
DelegatingAuthenticationEntryPoint entryPoint = new DelegatingAuthenticationEntryPoint(
this.defaultEntryPointMappings);
entryPoint.setDefaultEntryPoint(this.defaultEntryPointMappings.values().iterator()
.next());
return entryPoint;
}
这里我们可以看到,如果默认的 defaultEntryPointMappings 入口映射为空,会创建403类型的入口,返回403状态码。
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException arg2) throws IOException, ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Pre-authenticated entry point called. Rejecting access");
}
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Access Denied");
}
如果只有一个,则就取此入口作为初始化值;如果为多个,则创建 DelegatingAuthenticationEntryPoint 处理多入口逻辑。
而 AccessDeniedHandler 的初始化逻辑也是类似的。首先,也会判断 accessDeniedHandler 参数是否赋值。
AccessDeniedHandler getAccessDeniedHandler(H http) {
AccessDeniedHandler deniedHandler = this.accessDeniedHandler;
if (deniedHandler == null) {
deniedHandler = createDefaultDeniedHandler(http);
}
return deniedHandler;
}
没有赋值的话,会调用 createDefaultDeniedHandler 方法创建默认的 AccessDeniedHandler。
private AccessDeniedHandler createDefaultDeniedHandler(H http) {
if (this.defaultDeniedHandlerMappings.isEmpty()) {
return new AccessDeniedHandlerImpl();
}
if (this.defaultDeniedHandlerMappings.size() == 1) {
return this.defaultDeniedHandlerMappings.values().iterator().next();
}
return new RequestMatcherDelegatingAccessDeniedHandler(
this.defaultDeniedHandlerMappings,
new AccessDeniedHandlerImpl());
}
同 AuthenticationEntryPoint 的默认值创建逻辑相同,如果 defaultDeniedHandlerMappings 属性为空,即 deniedHandler 映射为空,则直接返回 AccessDeniedHandlerImpl 实例;如果只有一个,则就取此值;如果为多个,则创建 RequestMatcherDelegatingAccessDeniedHandler 处理多 Handler 情况。
最后,简单的来说一下 AuthenticationEntryPoint 、AccessDeniedHandler。
关于 AuthenticationEntryPoint,简单点来说,就是 ExceptionTranslationFilter 用于处理不用认证方案的。其实现共有6中。
有些从类名命名即可明白大致何意,感兴趣的可自行查看。
关于 AccessDeniedHandler,则是 ExceptionTranslationFilter 用于处理 AccessDeniedException 异常时使用的。
/**
* Used by {@link ExceptionTranslationFilter} to handle an
* <code>AccessDeniedException</code>.
*/
其实现共有4个。
Spring Security 框架默认配置的即为第一个 AccessDeniedHandlerImpl。其它的,感兴趣的可自行查看。
其它详细源码,请参考文末源码链接,可自行下载后阅读。
我是银河架构师,十年饮冰,难凉热血,愿历尽千帆,归来仍是少年!
如果文章对您有帮助,请举起您的小手,轻轻【三连】,这将是笔者持续创作的动力源泉。当然,如果文章有错误,或者您有任何的意见或建议,请留言。感谢您的阅读!
源码
github
https://github.com/liuminglei/SpringSecurityLearning/tree/master/39
gitee
https://gitee.com/xbd521/SpringSecurityLearning/tree/master/39