史上最简单的Spring Security教程(三十九):ExceptionTranslationFilter自动化配置及其简介

 

前面我们讲了 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

 

 

 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值