ExceptionTranslationFilter详解

ExceptionTranslationFilter异常转换过滤器位于整个springSecurityFilterChain的后方,用来转换整个链路中出现的异常,将其转化,顾名思义,转化以意味本身并不处理。一般其只处理两大类异常:AccessDeniedException访问异常和AuthenticationException认证异常。

这个过滤器非常重要,因为它将Java中的异常和HTTP的响应连接在了一起,这样在处理异常时,我们不用考虑密码错误该跳到什么页面,账号锁定该如何,只需要关注自己的业务逻辑,抛出相应的异常便可。如果该过滤器检测到AuthenticationException,则将会交给内部的AuthenticationEntryPoint去处理,如果检测到AccessDeniedException,需要先判断当前用户是不是匿名用户,如果是匿名访问,则和前面一样运行AuthenticationEntryPoint,否则会委托给AccessDeniedHandler去处理,而AccessDeniedHandler的默认实现,是AccessDeniedHandlerImpl。所以ExceptionTranslationFilter内部的AuthenticationEntryPoint是至关重要的,顾名思义:认证的入口点。

源码分析

public class ExceptionTranslationFilter extends GenericFilterBean {
  //处理异常转换的核心方法
  private void handleSpringSecurityException(HttpServletRequest request,
        HttpServletResponse response, FilterChain chain, RuntimeException exception)
        throws IOException, ServletException {
     if (exception instanceof AuthenticationException) {
       	//重定向到登录端点
        sendStartAuthentication(request, response, chain,
              (AuthenticationException) exception);
     }
     else if (exception instanceof AccessDeniedException) {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authenticationTrustResolver.isAnonymous(authentication) || authenticationTrustResolver.isRememberMe(authentication)) {
		  //重定向到登录端点
           sendStartAuthentication(
                 request,
                 response,
                 chain,
                 new InsufficientAuthenticationException(
                       "Full authentication is required to access this resource"));
        }
        else {
           //交给accessDeniedHandler处理
           accessDeniedHandler.handle(request, response,
                 (AccessDeniedException) exception);
        }
     }
  }
}

剩下的便是要搞懂AuthenticationEntryPoint和AccessDeniedHandler就可以了。
选择了几个常用的登录端点,以其中第一个为例来介绍,看名字就能猜到是认证失败之后,让用户跳转到登录页面。还记得我们一开始怎么配置表单登录页面的吗?

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/", "/home").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()//FormLoginConfigurer
                .loginPage("/login")
                .permitAll()
                .and()
            .logout()
                .permitAll();
    }
}

我们顺着formLogin返回的FormLoginConfigurer往下找,看看能发现什么,最终在FormLoginConfigurer的父类AbstractAuthenticationFilterConfigurer中有了不小的收获:

public abstract class AbstractAuthenticationFilterConfigurer extends ...{
   ...
   //formLogin不出所料配置了AuthenticationEntryPoint
   private LoginUrlAuthenticationEntryPoint authenticationEntryPoint;
   //认证失败的处理器
   private AuthenticationFailureHandler failureHandler;
   ...
}

具体如何配置的就不看了,我们得出了结论,formLogin()配置了之后最起码做了两件事,其一,为UsernamePasswordAuthenticationFilter设置了相关的配置,其二配置了AuthenticationEntryPoint。

登录端点还有Http401AuthenticationEntryPoint,Http403ForbiddenEntryPoint这些都是很简单的实现,有时候我们访问受限页面,又没有配置登录,就看到了一个空荡荡的默认错误页面,上面显示着401,403,就是这两个入口起了作用。

还剩下一个AccessDeniedHandler访问决策器未被讲解,简单提一下:AccessDeniedHandlerImpl这个默认实现类会根据errorPage和状态码来判断,最终决定跳转的页面

org.springframework.security.web.access.AccessDeniedHandlerImpl#handle

public void handle(HttpServletRequest request, HttpServletResponse response,
      AccessDeniedException accessDeniedException) throws IOException,
      ServletException {
   if (!response.isCommitted()) {
      if (errorPage != null) {
         // Put exception into request scope (perhaps of use to a view)
         request.setAttribute(WebAttributes.ACCESS_DENIED_403,
               accessDeniedException);
         // Set the 403 status code.
         response.setStatus(HttpServletResponse.SC_FORBIDDEN);
         // forward to error page.
         RequestDispatcher dispatcher = request.getRequestDispatcher(errorPage);
         dispatcher.forward(request, response);
      }
      else {
         response.sendError(HttpServletResponse.SC_FORBIDDEN,
               accessDeniedException.getMessage());
      }
   }

}

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
序言 I. 入门 1. 介绍 1.1. Spring Security是什么? 1.2. 历史 1.3. 发行版本号 1.4. 获得Spring Security 1.4.1. 项目模块 1.4.1.1. Core - spring-security-core.jar 1.4.1.2. Web - spring-security-web.jar 1.4.1.3. Config - spring-security-config.jar 1.4.1.4. LDAP - spring-security-ldap.jar 1.4.1.5. ACL - spring-security-acl.jar 1.4.1.6. CAS - spring-security-cas-client.jar 1.4.1.7. OpenID - spring-security-openid.jar 1.4.2. 获得源代码 2. Security命名空间配置 2.1. 介绍 2.1.1. 命名空间的设计 2.2. 开始使用安全命名空间配置 2.2.1. 配置web.xml 2.2.2. 最小 <http>配置 2.2.2.1. auto-config包含了什么? 2.2.2.2. 表单和基本登录选项 2.2.3. 使用其他认证提供器 2.2.3.1. 添加一个密码编码器 2.3. 高级web特性 2.3.1. Remember-Me认证 2.3.2. 添加HTTP/HTTPS信道安全 2.3.3. 会话管理 2.3.3.1. 检测超时 2.3.3.2. 同步会话控制 2.3.3.3. 防止Session固定攻击 2.3.4. 对OpenID的支持 2.3.4.1. 属性交换 2.3.5. 添加你自己的filter 2.3.5.1. 设置自定义 AuthenticationEntryPoint 2.4. 保护方法 2.4.1. <global-method-security>元素 2.4.1.1. 使用protect-pointcut添加安全切点 2.5. 默认的AccessDecisionManager 2.5.1. 自定义AccessDecisionManager 2.6. 验证管理器和命名空间 3. 示例程序 3.1. Tutorial示例 3.2. Contacts 3.3. LDAP例子 3.4. CAS例子 3.5. Pre-Authentication例子 4. Spring Security社区 4.1. 任务跟踪 4.2. 成为参与者 4.3. 更多信息 II. 结构和实现 5. 技术概述 5.1. 运行环境 5.2. 核心组件 5.2.1. SecurityContextHolder, SecurityContext 和 Authentication对象 5.2.1.1. 获得当前用户的信息 5.2.2. UserDetailsService 5.2.3. GrantedAuthority 5.2.4. 小结 5.3. 验证 5.3.1. 什么是Spring Security的验证呢? 5.3.2. 直接设置SecurityContextHolder的内容 5.4. 在web应用中验证 5.4.1. ExceptionTranslationFilter 5.4.2. AuthenticationEntryPoint 5.4.3. 验证机制 5.4.4. 在请求之间保存SecurityContext。 5.5. Spring Security中的访问控制(验证) 5.5.1. 安全和AOP建议 5.5.2. 安全对象和AbstractSecurityInterceptor 5.5.2.1. 配置属性是什么? 5.5.2.2. RunAsManager 5.5.2.3. AfterInvocationManager 5.5.2.4. 扩展安全对象模型 5.6. 国际化 6. 核心服务 6.1. The AuthenticationManager, ProviderManager 和 AuthenticationProviders 6.1.1. DaoAuthenticationProvider 6.2. UserDetailsService实现 6.2.1. 内存认证 6.2.2. JdbcDaoImpl 6.2.2.1. 权限分组 6.3. 密码加密 6.3.1. 什么是散列加密? 6.3.2. 为散列加点儿盐 6.3.3. 散列和认证 III. web应用安全 7. 安全过滤器链 7.1. DelegatingFilterProxy 7.2. FilterChainProxy 7.2.1. 绕过过滤器链 7.3. 过滤器顺序 7.4. 使用其他过滤器 —— 基于框架 8. 核心安全过滤器 8.1. FilterSecurityInterceptor 8.2. ExceptionTranslationFilter 8.2.1. AuthenticationEntryPoint 8.2.2. AccessDeniedHandler 8.3. SecurityContextPersistenceFilter 8.3.1. SecurityContextRepository 8.4. UsernamePasswordAuthenticationFilter 8.4.1. 认证成功和失败的应用流程 9. Basic(基本)和Digest(摘要)验证 9.1. BasicAuthenticationFilter 9.1.1. 配置 9.2. DigestAuthenticationFilter 9.2.1. Configuration 10. Remember-Me认证 10.1. 概述 10.2. 简单基于散列标记的方法 10.3. 持久化标记方法 10.4. Remember-Me接口和实现 10.4.1. TokenBasedRememberMeServices 10.4.2. PersistentTokenBasedRememberMeServices 11. 会话管理 11.1. SessionManagementFilter 11.2. SessionAuthenticationStrategy 11.3. 同步会话 12. 匿名认证 12.1. 概述 12.2. 配置 12.3. AuthenticationTrustResolver IV. 授权 13. 验证架构 13.1. 验证 13.2. 处理预调用 13.2.1. AccessDecisionManager 13.2.2. 基于投票的AccessDecisionManager实现 13.2.2.1. RoleVoter 13.2.2.2. AuthenticatedVoter 13.2.2.3. Custom Voters 13.3. 处理后决定 14. 安全对象实现 14.1. AOP联盟 (MethodInvocation) 安全拦截器 14.1.1. 精确的 MethodSecurityIterceptor 配置 14.2. AspectJ (JoinPoint) 安全拦截器 15. 基于表达式的权限控制 15.1. 概述 15.1.1. 常用内建表达式 15.2. Web 安全表达式 15.3. 方法安全表达式 15.3.1. @Pre 和 @Post 注解 15.3.1.1. 访问控制使用 @PreAuthorize 和 @PostAuthorize 15.3.1.2. 过滤使用 @PreFilter 和 @PostFilter V. 高级话题 16. 领域对象安全(ACLs) 16.1. 概述 16.2. 关键概念 16.3. 开始 17. 预认证场景 17.1. 预认证框架类 17.1.1. AbstractPreAuthenticatedProcessingFilter 17.1.2. AbstractPreAuthenticatedAuthenticationDetailsSource 17.1.2.1. J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource 17.1.3. PreAuthenticatedAuthenticationProvider 17.1.4. Http403ForbiddenEntryPoint 17.2. 具体实现 17.2.1. 请求头认证(Siteminder) 17.2.1.1. Siteminder示例配置 17.2.2. J2EE容器认证 18. LDAP认证 18.1. 综述 18.2. 在Spring Security里使用LDAP 18.3. 配置LDAP服务器 18.3.1. 使用嵌入测试服务器 18.3.2. 使用绑定认证 18.3.3. 读取授权 18.4. 实现类 18.4.1. LdapAuthenticator实现 18.4.1.1. 常用功能 18.4.1.2. BindAuthenticator 18.4.1.3. PasswordComparisonAuthenticator 18.4.1.4. 活动目录认证 18.4.2. 链接到LDAP服务器 18.4.3. LDAP搜索对象 18.4.3.1. FilterBasedLdapUserSearch 18.4.4. LdapAuthoritiesPopulator 18.4.5. Spring Bean配置 18.4.6. LDAP属性和自定义UserDetails 19. JSP标签库 19.1. 声明Taglib 19.2. authorize标签 19.3. authentication 标签 19.4. accesscontrollist 标签 20. Java认证和授权服务(JAAS)供应器 20.1. 概述 20.2. 配置 20.2.1. JAAS CallbackHandler 20.2.2. JAAS AuthorityGranter 21. CAS认证 21.1. 概述 21.2. CAS是如何工作的 21.3. 配置CAS客户端 22. X.509认证 22.1. 概述 22.2. 把X.509认证添加到你的web系统中 22.3. 为tomcat配置SSL 23. 替换验证身份 23.1. 概述 23.2. 配置 A. 安全数据库表结构 A.1. User表 A.1.1. 组权限 A.2. 持久登陆(Remember-Me)表 A.3. ACL表 A.3.1. Hypersonic SQL A.3.1.1. PostgreSQL B. 安全命名空间 B.1. Web应用安全 - <http>元素 B.1.1. <http>属性 B.1.1.1. servlet-api-provision B.1.1.2. path-type B.1.1.3. lowercase-comparisons B.1.1.4. realm B.1.1.5. entry-point-ref B.1.1.6. access-decision-manager-ref B.1.1.7. access-denied-page B.1.1.8. once-per-request B.1.1.9. create-session B.1.2. <access-denied-handler> B.1.3. <intercept-url>元素 B.1.3.1. pattern B.1.3.2. method B.1.3.3. access B.1.3.4. requires-channel B.1.3.5. filters B.1.4. <port-mappings>元素 B.1.5. <form-login>元素 B.1.5.1. login-page B.1.5.2. login-processing-url B.1.5.3. default-target-url B.1.5.4. always-use-default-target B.1.5.5. authentication-failure-url B.1.5.6. authentication-success-handler-ref B.1.5.7. authentication-failure-handler-ref B.1.6. <http-basic>元素 B.1.7. <remember-me>元素 B.1.7.1. data-source-ref B.1.7.2. token-repository-ref B.1.7.3. services-ref B.1.7.4. token-repository-ref B.1.7.5. key属性 B.1.7.6. token-validity-seconds B.1.7.7. user-service-ref B.1.8. <session-management> 元素 B.1.8.1. session-fixation-protection B.1.9. <concurrent-control>元素 B.1.9.1. max-sessions属性 B.1.9.2. expired-url属性 B.1.9.3. error-if-maximum-exceeded属性 B.1.9.4. session-registry-alias和session-registry-ref属性 B.1.10. <anonymous>元素 B.1.11. <x509>元素 B.1.11.1. subject-principal-regex属性 B.1.11.2. user-service-ref属性 B.1.12. <openid-login>元素 B.1.13. <logout>元素 B.1.13.1. logout-url属性 B.1.13.2. logout-success-url属性 B.1.13.3. invalidate-session属性 B.1.14. <custom-filter>元素 B.2. 认证服务 B.2.1. <authentication-manager>元素 B.2.1.1. <authentication-provider>元素 B.2.1.2. 使用 <authentication-provider> 来引用一个 AuthenticationProvider Bean B.3. 方法安全 B.3.1. <global-method-security>元素 B.3.1.1. secured-annotations和jsr250-annotations属性 B.3.1.2. 安全方法使用<protect-pointcut> B.3.1.3. <after-invocation-provider> 元素 B.3.2. LDAP命名空间选项 B.3.2.1. 使用<ldap-server>元素定义LDAP服务器 B.3.2.2. <ldap-provider>元素 B.3.2.3. <ldap-user-service>元素

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值