实际上,Spring Security并不关心如何将 Authentication对象放到SecurityContextHolder中。唯一的关键就是 SecurityContextHolder需要在用户操作认证的 AbstractSecurityInterceptor 之前已经有了Authentication对象。
对于那些不是Spring Security的系统,你可以自己写过滤器或MVC控制器与身份认证系统进行集成。比如,你可能使用容器管理的身份认证系统从ThreadLocal或JNDI中得到用户。也可能你在一个拥有遗留的身份认证系统的公司工作,这是一个企业的“标准”,对此你是无能为力的。在这种情形下,使用Spring Security提供身份认证是非常容易的,你只需要写一个过滤器读取第三方的用户信息,然后构建一个Spring Security特定的 Authentication对象,并把它放到AuthenticationContextHolder中即可。在这种情况下,你需要考虑自带的身份认证的基础信息。比如,你需要在响应到客户端之前,先创建一个HTTP session会话在请求之间缓存上下文。
3 在Web应用中使用身份认证
接下来,我们探究一下Web应用不配置web.xml安全策略的情况下如何使用Spring Security进行身份认证,如何建立用户身份认证和安全上下文?
下面是web应用身份认证的流程:
(1)访问某应用的首页,点击某个链接。
(2)发送一个请求到服务器,服务器判断用户是否正在访问受保护的资源。
(3)由于用户之前并未进行身份认证,服务器发送一个响应(该响应可能是HTTP响应代码,也可能直接跳转到某web页面)告诉用户必须进行身份认证。
(4)身份认证机制决定了浏览器是跳转到特定的web页面让用户填写form表单,或者浏览器以某种方式(基本的身份认证对话框、cookie或X.509证书)检索用户身份。
(5)浏览器发送响应(包含表单信息的HTTP POST请求或是包含用户身份认证详细信息的HTTP表头)回服务器。
(6)接下来,服务器会决定之前的凭证是否有效。如果有效的话,会进行下一步。否则的话,浏览器通常会询问是否需要重试。
(7)原始的请求会导致身份认证流程重新进行,重新判断用户有足够的权限访问受保护的资源,如果用户有权限的话,请求就是成功的。否则的话,会返回HTTP错误码403,表示用户没有权限操作。
Spring Security有具体的类负责上面的步骤,主要的类有 ExceptionTranslationFilter , AuthenticationEntryPoint 和调用AuenticationManager 的“身份认证机制”。
3.1 ExceptionTranslationFilter
顾名思义,ExceptionTranslationFilter是处理Spring Security中异常的过滤器,这些异常都是由提供身份认证服务的 AbstractSecurityInterceptor 抛出。
3.2 AuthenticationEntryPoint
上面步骤3的操作中是 AuenticationEntryPoint 的职责,你能想象每个web应用都有默认的身份认证测试,每个主要的身份认证系统都有 AuthenticationEntryPoint 实现,通常执行步骤3中描述的行动之一。
3.3 身份认证机制
一旦你的浏览器提交了验证证书(HTTP表单 POST或 HTTP头),这需要服务器上的一些东西保存这些权限信息。但是现在进入上面的第6步,在Spring Security中我们有一个特定的名称,为了手机验证信息的操作。从一个用户代理中(通常是浏览器),引用它作为一个“验证机制”。例如基于表单的登陆或BASIC验证。一旦从用户代理出收集到验证细节, Authentication请求对象就会建立,然后提交给AuthenticationManager。
身份认证机制收到填充好的 Authentication 对象之后,它会认为请求合法,把 Authentication放到SecurityContextHolder中,然后重试原始的请求(第7步)。如另一方面, AuthenticationManager拒绝了请求,身份认证机制会让用户代理重试(第2步)。
3.4 在请求间保存 SecurityContext
根据应用类型,需要一个策略在用户操作之间保存security上下文。在典型的web应用中,一次用户登陆日志随后就由它的session id所确定,服务器为保持session会话会缓存主体信息。在Spring Security中,在请求间存储SecurityContext的职责落在了 SecurityContextPersistenceFilter上,默认情况下,SecurityContextPersistenceFilter会在HTTP请求中将上下文存储在HttpSession属性上。每次请求的上下文都会存储在 SecurityContextHolder上,而且,最重要的是,当请求完成时它会清除 SecurityContextHolder。为了安全方面考虑,用户不应该直接操作 HttpSession,这里有简单的方法实现-使用 SecurityContextHolder代替。