《Spring Security3》第六章第四部分翻译(异常处理)(转载)

理解和配置异常处理

         Spring Security 使用简单的分发器模式将框架抛出的异常转移到明确的处理行为中,这将会影响用户对安全资源的访问。 Spring Security 过滤器链中最后几个过滤器之一的 o.s.s.web.access.ExceptionTranslationFilter 负责检查在认证和授权过程中(在过滤器链的最后一个过滤器即 FilterSecurityInterceptor 里)抛出的异常并采取适当的行为。

         标准的 ExceptionTranslationFilter 支持分发处理三种常规类型的失败,如下图所示:



 我们能够看到 ExceptionTranslationFilter 处理如下的场景:

l  抛出 AuthenticationException 异常,用户需要登录(在大多数场景下——取决于 AuthenticationEntryPoint ,我们将会在本章稍后介绍);

l  抛出 AccessDeniedException ,用户已经尚未登录;

l  抛出 AccessDeniedException ,用户已经登录,在这种场景下,展现给用户的要么是出错页面,要么是通用的 HTTP 403 响应。

让我们看一下 AccessDeniedHandler 的配置。

配置“Access Denied ”处理

         到此为止,当一个认证过的用户访问受保护的资源时,因为缺少 GrantedAuthority 或其它需要的权限被拒绝的时候,他们看到的是 servlet 容器的默认 HTTP 403 (访问拒绝)页面。这个页面是 o.s.s.web.access.AccessDeniedHandler 默认行为的结果,它被 ExceptionTranslationFilter 所触发以响应框架抛出的一个 AccessDeniedException 异常。

         尽管这个简单的出错页面有效,但是并不具有吸引力和也不对用户友好。最好能够将这个页面与我们站点整体的外观和风格一致,并为用户提供信息告诉他发生了什么。

         基本的处理用户访问拒绝报告的方法是配置自定义的 URL Spring Security 会在请求拒绝时,将用户带到这个 URL 。我们会发现这个功能与初始登录时,用户被定向到 <form-login> 元素声明的 login-page 很类似。

配置“ Access Denied ”的目标地址

         配置用户被定向到的地址是这个练习中最简单的部分。只需在 <http> 声明中,添加一个元素 <access-denied-handler> ,它指明了我们的访问拒绝处理 URL ,如下:

 

Xml代码   收藏代码
  1. < http   auto-config = "true"  ... >   
  2.   < access-denied-handler   error-page = "/accessDenied.do" />   
  3. </ http >   

 注意——还没完事呢,因为我们还没有将这个 URL 与任何的 Spring MVC 应用代码关联起来。我们需要在 LoginLogoutController 添加增强代码以处理这个 URL ,并推送一些有用的信息到 model 中供 view 展现给用户。

添加对 AccessDeniedException 处理的控制器

         我们需要添加一个控制器 action 处理方法以响应刚刚配置的 URL 。另外,我们会从 AccessDeniedException 抽取一些细节信息,这可能在用户看到我们自定义访问拒绝页面时有用。

 

Java代码   收藏代码
  1. @Controller   
  2. public   class  LoginLogoutController  extends  BaseController{  
  3.   // Ch 6 Access Denied   
  4.   @RequestMapping (method=RequestMethod.GET, value= "/accessDenied.do" ).  
  5.   public   void  accessDenied(ModelMap model, HttpServletRequest request) {  
  6.     AccessDeniedException ex = (AccessDeniedException)   
  7.     request.getAttribute(AccessDeniedHandlerImpl   
  8.     .SPRING_SECURITY_ACCESS_DENIED_EXCEPTION_KEY);  
  9.     StringWriter sw = new  StringWriter();  
  10.     model.addAttribute("errorDetails" , ex.getMessage());  
  11.     ex.printStackTrace(new  PrintWriter(sw));  
  12.     model.addAttribute("errorTrace" , sw.toString());  
  13.   }  
  14. }  

 注意的是,需要引用 AccessDeniedHandlerImpl 来获取 request 中指定名字的属性,它被用来临时存储当前 request 范围内的异常。

         遗憾的是, AccessDeniedException 并没有在它的 message 中提供足够的细节信息,而这对系统管理员和用户本身可能会有用。你可能会对使用 Spring Security AccessDeniedException 感兴趣或者可能扩展它以提供更多的上下文信息,以得到当授权检查不通过时用户正在试图进行什么操作。

 

编写 Access Denied 页面

         控制器写好后,接下来是访问拒绝页面。

 

Html代码   收藏代码
  1. < %@ page  language = "java"   contentType = "text/html; charset=ISO-8859-1"   
  2.   pageEncoding = "ISO-8859-1" % >   
  3. < jsp:include   page = "common/header.jsp" >   
  4.   < jsp:param   name = "pageTitle"   value = "Access Denied" />   
  5. </ jsp:include >   
  6. < h1 > Access Denied </ h1 >   
  7. < p >   
  8.   Access to the specified resource has been denied for   
  9.   the following reason: < strong > ${errorDetails} </ strong > .  
  10. </ p >   
  11. < em > Error Details (for Support Purposes only): </ em > < br   />   
  12. < blockquote >   
  13.   < pre > ${errorTrace} </ pre >   
  14. </ blockquote >   
  15. < jsp:include   page = "common/footer.jsp" />   

          你可以看到我们使用了在控制器设置的模型属性 errorDetails errorTrace 。尽管不是很漂亮,但是这个页面完成了它的任务即用通用的站点导航,将用户带到了站点的其它区域并给他们提供了导致出错的提示信息。

什么会触发AccessDeniedException

         当设计异常处理时,进行一些分析并理解目标异常的原理很重要。对于 Spring Security 的用户来说,通常很迷惑的一件事就是 AccessDeniedException (默认会导致 HTTP 403 页面)和 AuthenticationException (一般当用户根本没有登录时抛出)。以下的指南可能会帮助你分清框架在什么时间抛出每种类型的异常:

异常类型

谁抛出以及原因

AuthenticationException

AuthenticationProvider ,当提供的凭证不合法或用户失效、过期;

DaoAuthenticationProvider ,当访问 DAO 数据存储时出错;

RememberMeServices ,当 remember me cookie 被篡改;

各种特定的认证类( CAS NTLM 等)在用户特定的场景下。

AccessDeniedException

AccessDecisionManager ,当配置的 Voter 投票拒绝访问——注意这可能在任何投票场景下

         要记住的是,我们前面提到的 ExceptionTranslationFilter 是区分这两种类型异常的关键点,因为这关系到应用用户的请求和响应流程。

【注意过滤器链中在 ExceptionTranslationFilter 之前的那些过滤器。 ExceptionTranslationFilter 只会处理和响应过滤器链中在此之后的过滤器所抛出的异常。用户可能会感到迷惑,尤其是将自定义过滤器进行了不正确排序的时候,不明白为什么期望的行为与应用实际的异常处理不一致——在很多场景下,过滤器的顺序是原因所在。】

         尽管内置的处理流程在大多数情况下是可预测且满足需要的,但有时候你会需要自定义异常处理,尤其是在引入基类异常的自定义子类时,这需要过滤器链的特殊处理。

AuthenticationEntryPoint 的重要性

         AuthenticationEntryPoint (在 ExceptionTranslationFilter 我们看到它是工作流程中的一个辅助类)在处理未认证用户请求中很重要。当 ExceptionTranslationFilter 确定用户需要认证时,它请求 AuthenticationEntryPoint 以了解下一步要做什么。在基于 form 的认证中, o.s.s.web.authentication.LoginUrlAuthenticationEntryPoint 负责将用户定向到登录 form

         我们将会在后面的章节中看到 AuthenticationEntryPoint 被用在各种认证机制中,从而实现更个性化的行为——例如,在中心认证服务( CAS )单点登录中, AuthenticationEntryPoint 要确保用户被定向到 CAS 门户进行认证。

         在很多环境下,当实现 Spring Security 与第三方认证系统(独立于 web 应用)集成时,你需要实现自己的 AuthenticationEntryPoint

猜你喜欢

转载自kongcodecenter.iteye.com/blog/1320052