Spring Security是一种为基于Spring的应用程序提供说明性安全保护的安全框架。它提供全面的安全性解决方案,同时在Web请求级和方法调用级处理身份确认和授权。在Spring Framework基础上,Spring Security充分利用了依赖注入(DI,Dependency Injection)和面向切面技术。
无论你是只在Web请求级需要安全措施,还是需要较低层的方法安全措施,Spring Security都是使用如下图所示的5个主要组件来实施安全措施的。
图1 Spring Security的基本要素
1 安全拦截器
工作了一整天,当你回到家时,需要打开家门上的锁。而为了打开那个锁,你必须先将一把钥匙插到锁孔中,并恰当地拨动锁的制动栓,以打开弹簧锁。如果钥匙和锁不匹配,就无法拨动制动栓,而弹簧锁也就不会被打开。但是如果你有正确的钥匙,那么所有的制动栓就都会接受这把钥匙,弹簧锁就会被打开,从而允许你把门打开。
在Spring Security中,安全拦截器可以被看作是一把弹簧锁,能够阻止对应用程序中受保护资源的访问。为了弹开弹簧锁,从而通过安全拦截器,你必须向系统提供“钥匙”(通常是一对用户名和密码)。该“钥匙”接着会尝试拨开安全拦截器的“制动栓”,从而允许你访问受保护的资源。
安全拦截器的实际实施将取决于所要保护的资源。如果读者正要在某个Web应用程序中保护一个URL,那么相应的安全拦截器将被当做一个servlet过滤器来实施。但是如果你正要保护某个方法调用,那么切面将被用来加强安全性。读者将会在本章稍后部分看到安全拦截器的这两种形式。
除了通过拦截对资源的访问来加强安全性之外,安全拦截器几乎无所事事。它并不实际应用安全规则。相反,它把该职责委托给图7.1底部所示的各种管理器。下面让我们从认证管理器开始,逐个看一下这些管理器。
2 认证管理器
第一道必须打开的安全拦截器的制动栓就是认证管理器。认证管理器负责辨认你是谁。它是通过考虑你的主体(通常是一个用户名)和你的凭证(通常是一个密码)做到这点的。
在Spring Security中,认证管理器负责确定用户的身份。在为应用程序应用安全措施时,决定是否允许用户访问受保护资源之前首先需要判断用户的身份。在绝大多数应用程序中,这意味着为用户显示一个登录界面,并要求他们提供用户名和密码。认证管理器是由org.acegi- security.AuthenticationManager接口定义的:
<bean id="authenticationManager"
class="org.springframework.security.providers.ProviderManager">
<property name="providers">
<list>
<ref local="daoAuthenticationProvider" />
</list>
</property>
</bean>
ProviderManager是认证管理器的一个实现,它将验证身份的责任委托给一个或多个认证提供者,如图7.2所示。
许多应用程序将包括用户名和密码在内的用户信息保存在关系数据库中。如果那就是你的应用程序保留用户信息的方式,那么你会发现,对于你的应用程序来说,选择Spring Security提供的DaoAuthenticationProvider可能会比较好。
DaoAuthenticationProvider是一个简单的认证提供者,它使用数据存取对象(DAO)来从关系数据库中检索用户信息(包括用户的密码)。
在取得了所需的用户名和密码之后,DaoAuthenticationProvider通过比较从数据库中检索到的用户名和密码以及来自认证管理器的通过Authentication对象中传入的主体和凭证完成身份验证(如图7.3所示)。如果上述用户名和密码与主体和凭证相匹配,则用户通过身份验证,同时返回给认证管理器一个已完全填充好的Authentication对象。否则会抛出一个AuthenticationException,表明身份验证失败。
<bean id="daoAuthenticationProvider"
class="org.springframework.security.providers.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="loginService" />
</bean>
这里的userDetailsService属性被用来指定将用于从数据库中检索用户信息的那个Bean。这个属性期望org.acegisecurity.userdetails.UserDetailsService的一个实例。剩下来的问题就是那个userDetailsService Bean是如何配置的了。
这里的UserDetailsService接口要求只执行一个方法:
public UserDetails loadUserByUsername(String loginId)
throws UsernameNotFoundException, DataAccessException
AuthenticationProcessingFilter是基于j_username和j_password参数提供的用户名和密码信息处理身份验证的过滤器。
<bean id="authenticationProcessingFilter" class="org.springframework.security.ui.webapp.AuthenticationProcessingFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationFailureUrl" value="/login.jsp"/>
<property name="defaultTargetUrl" value="/index.jsp"/>
<property name="filterProcessesUrl" value="/j_spring_security_check"></property>
</bean>
这里的filterProcessesUrl属性告诉AuthenticationProcessingFilter应该拦截哪一个URL。这个URL与登录表单action属性中的URL是完全相同的。它的默认值为/j_acegi_security_check,但是这里明确定义了该值,目的是说明你可以根据需要改变这个值。
这里的authenticationFailureUrl属性指定当身份验证失败时,用户应该被送往哪里。在这里,我们将把用户送回到登录页面,同时传递一个参数用于表明身份验证失败(因此在登录页面上可能会显示一个错误消息)。
在正常情况下,当身份验证成功后,AuthenticationProcessingFilter会在当前会话中放置一个Authentication对象,并且将用户重新定向到他们所期望访问的页面。这里的defaultTargetUrl属性定义当出现目标URL不知在何处的异常情况时将发生什么。如果用户直接导航到登录页面,而不是首先试图访问某一受保护的资源时,就可能发生这种情况。
最后,这里的authenticationManager属性被装配了一个指向authenticationManager Bean的引用。正如所有其他的身份验证处理过滤器一样,基于表单的Authentication- ProcessingFilter也依靠一个身份验证管理器来帮助确定当前用户的身份。