http://book.csdn.net/bookfiles/167/1001677406.shtml
11.2.4 基于Acegi和Yale CAS实现单次登录
你有多少个密码?如果你和大多数人一样,你很可能使用十余个乃至更多的密码来登录日常访问的各种系统。保管所有这些密码是一个挑战,而被迫登录多个系统则令人厌烦。如果能够只登录一次就自动登录了所有需要使用的系统,那该有多好。
单次登录(Single Sign On,SSO)是一个热门的安全话题。这个名字就已经表达了一切:一次登录,访问一切。耶鲁大学技术和规划组已经创建了一个名为中心认证服务(Central Authentication Service,CAS)的优秀SSO解决方案,它可以和Acegi共同工作。
关于设置和使用CAS的细节远远超出了本书的范围。但我们将讨论CAS采用的基本身份验证方式,并探讨如何与CAS一起使用Acegi。如果想知道关于CAS的更多信息,我们强烈推荐你访问CAS的主页http://tp.its.yale.edu/tiki/tiki-index.php?page= CentralAuthenticationService。
为了理解Acegi在一个CAS身份验证应用中的作用,重要的是先理解一个典型的CAS身份验证场景是如何工作的。考虑一个请求受保护服务的流程,如图11.5所示。
图11.5 使用Yale CAS保护一个应用
当Web浏览器请求一个服务时①,服务通过在请求中寻找一个CAS票据来判断用户身份是否已通过验证。如果未找到票据,则意味着该用户尚未通过身份验证。作为结果,用户被重定向到CAS的登录页面②。
在CAS的登录页面,用户输入他/她的用户名和密码。如果CAS成功认证了该用户,则创建一个与请求的服务相关联的票据。接着,CAS服务器将用户重定向到用户原先请求的服务(此时请求中已经有票据了)③。
服务再次在请求中寻找票据。这一次它找到了票据,并与CAS服务器联系以确认票据是有效的④。如果CAS的响应表明票据对当前请求的服务而言是有效的,服务就会允许用户访问应用。
以后,当用户请求访问另一个支持CAS的应用系统时,那个应用仍会与CAS联系。由于用户之前已经认证,CAS会返回一个对该新应用有效的服务票据,而不会提示用户再次登录。
你应该理解的关于CAS的关键概念之一是:被保护的应用从不处理用户的凭证。当用户被提示登录应用系统时,他们实际上是登录到了CAS服务器。应用系统自已从来没有看到过用户的凭证。应用系统使用的惟一安全形式是通过询问CAS服务器来验证用户的票据。这意味着只有一个应用(即CAS服务器)负责处理用户认证,因此是非常好的解决方案。
当Acegi与CAS共同使用时,它承担着帮助应用系统向CAS服务器验证CAS票据的任务。这就使应用系统本身从CAS认证过程中解脱了出来。
Acegi通过使用CasAuthenticationProvider来完成这一任务。这是一个不关心用户名和密码的认证提供者,它只接受CAS票据作为凭证。你可以在Spring配置文件中按如下方式配置一个CasAuthenticationProvider Bean:
<bean id="casAuthenticationProvider"
➥class="net.sf.acegisecurity.providers.cas.CasAuthenticationProvider">
<property name="ticketValidator">
<ref bean="ticketValidator"/>
</property>
<property name="casProxyDecider">
<ref bean="casProxyDecider"/>
</property>
<property name="statelessTicketCache">
<ref bean="statelessTicketCache"/>
</property>
<property name="casAuthoritiesPopulator">
<ref bean="casAuthoritiesPopulator"/>
</property>
<property name="key">
<value>some_unique_key</value>
</property>
</bean>
正如你所看到的,CasAuthenticationProvider是通过和其他若干个Bean相互合作来完成它的工作的。这些Bean中的第一个是ticketValidator Bean,它被装配到ticketValidator属性中。在Spring配置文件中它是这样声明的:
<bean id="ticketValidator" class="net.sf.acegisecurity.
➥providers.cas. ticketvalidator.CasProxyTicketValidator">
<property name="casValidate">
<value>https://localhost:8443/cas/proxyValidate</value>
</property>
<property name="serviceProperties">
<ref bean="serviceProperties"/>
</property>
</bean>
CasProxyTicketValidator通过联系CAS服务器来验证CAS服务票据。属性casValidate指定了CAS服务器处理验证请求的URL。
配置中引用的serviceProperities Bean中包含了与CAS相关的Bean的重要配置信息:
<bean id="serviceProperties"
class="net.sf.acegisecurity.ui.cas.ServiceProperties">
<property name="service">
<value>https://localhost:8443/training/
➥j_acegi_cas_security_check</value>
</property>
</bean>
属性service指定了一个URL,CAS在用户成功登录之后应该将用户重定向至该URL。以后,在第11.4.3节中,你会看到该URL是如何被服务的。
回到casAuthenticationProvider Bean,属性casProxyDecider装配了一个指向casProxyDecider Bean的引用,即一个到类型为net.sf.acegisecurity.providers.cas. CasProxyDecider的Bean的引用。为了理解casProxyDecider Bean的作用,你必须理解CAS如何支持代理服务。
CAS支持代理服务的概念,代理服务帮助另一个应用程序实现用户的身份验证。一个典型的代理服务的例子是门户,门户帮助由它所代表的portlet应用程序完成用户身份验证。当用户登录到一个门户时,门户通过代理票据也确保用户隐含地登录到它包含的应用系统中。
CAS如何处理代理票据是一个高级话题。我们建议你查阅CAS的文档(http://tp.its.yale.edu/tiki/tiki-index.php?page=CasTwoOverview)获取关于代理票据的更详细的信息。在这里,我们只需要指出CasProxyDecider负责决定是否接受代理票据。Acegi提供了CasProxyDecider的三个实现类:
n AcceptAnyCasProxy——接受来自任何服务的代理请求;
n NamedCasProxyDecider——接受来自一个已命名服务的列表的代理请求;
n RejectProxyTickets——拒绝任何代理请求。
为简单起见,让我们假设你的应用系统不涉及代理服务。在这种情况下,RejectProxyTicket就成为对casProxyDecider Bean来说最合适的CasProxyDecider:
<bean id="casProxyDecider"class="net.sf.acegisecurity.
➥ providers.cas.proxy.RejectProxyTickets"/>
属性statelessTicketCache用于支持无状态的客户端(比如远程服务的客户端),它们无法在HttpSession中存储CAS票据。不幸的是,即使没有无状态客户端要访问你的应用系统,statelessTicketCache属性也是必不可少的。Acegi仅仅提供了一个实现,所以声明一个statelessTicketCache相当简单:
<bean id="statelessTicketCache"class="net.sf.acegisecurity.
➥ providers.cas.cache.EhCacheBasedTicketCache">
<property name="minutesToIdle"><value>20</value></property>
</bean>
最后一个与CasAuthenticationProvider协同工作的Bean是casAuthoritiesPopulator Bean。作为一个SSO实现,CAS只负责身份验证——它不关心权限是如何分配给用户的。为了弥补这一差距,你需要一个net.sf.acegisecurity.providers.cas.CasAuthoritiesPopulator Bean。
Acegi只提供了一个CasAuthoritiesPopulator的实现。DaoCasAuthoritiesPopulator使用一个认证DAO(如11.2.2节中讨论的)从数据库中加载用户明细信息。可以这样声明一个casAuthoritiesPopulator Bean:
<bean id="casAuthoritiesPopulator" class="net.sf.acegisecurity.
➥providers.cas.populator.DaoCasAuthoritiesPopulator">
<property name="authenticationDao">
<ref bean="inMemoryDaoImpl"/>
</property>
</bean>
最后,CasAuthenticationManager的key属性指定了一个 String 值,认证管理器使用该字符串来识别之前已经认证的标志。你可以把这个属性设为任意值。
关于使用CAS和Acegi实现SSO还有比CasAuthenticationManager更多的内容。我们仅仅讨论了CasAuthenticationProvider是如何进行身份验证的。在第11.4.3节中,你将看到当CasAuthenticationManager验证用户身份失败时,是如何将用户重定向到CAS登录页面的。
但就现在而言,让我们先看一下Acegi是如何判断一个己通过身份验证的用户是否拥有访问受保护资源所需的恰当权限的。