技术开发 频道

使用Acegi进行身份认证[之二]

成功登录系统的后置处理

一般的业务系统在用户登录成功后,需要在数据库中记录一条相应的用户登录日志。我们可以通过Acegi提供的事件机制来完成这一功能。当用户身份认证成功后,Acegi会产生一个AuthenticationSuccessEvent事件,该事件是org.springframework.context.ApplicationEvent的子类,所以AuthenticationSuccessEvent是一个Spring容器事件。我们可以定义一个监听器响应该事件以记录用户登录日志,请看LoginSuccessListener的代码:

代码清单 9 LoginSuccessListener

package com.baobaotao.service; import org.acegisecurity.Authentication; import org.acegisecurity.event.authentication.AuthenticationSuccessEvent; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; public class LoginSuccessListener implements ApplicationListener { public void onApplicationEvent(ApplicationEvent event) { if (event instanceof AuthenticationSuccessEvent) {①判断是否是认证成功的事件 AuthenticationSuccessEvent authEvent = (AuthenticationSuccessEvent)event; Authentication auth = authEvent.getAuthentication(); String userName = auth.getName(); ②这里,我们仅通过一条输出信息的语句模拟记录登录日志的操作 System.out.println("模拟记录用户["+userName+"]成功登录日志..."); } } }

通过扩展Spring的ApplicationListener接口定义一个监听Acegi AuthenticationSuccessEvent事务的监听器。在实际系统中,你可以使用DAO替换②处的逻辑,执行记录用户登录日志的操作。在编写好LoginSuccessListener后,剩下的工作就是在Spring容器中声明这个监听器,仅需要一行简单的配置就可以了:

<bean class="com.baobaotao.service.LoginSuccessListener"/>

这样,当用户登录并通过了Acegi的用户身份认证后, LoginSuccessListener监听器的onApplicationEvent()方法将接收到AuthenticationSuccessEvent事件。

在多个请求之间共享SecurityContext
我们知道SecurityContext保持着Acegi框架重要的Authentication对象,而SecurityContext保存在SecurityContextHolder中。然而SecurityContextHolder只为SecurityContext对象提供请求线程范围内的生命周期。也就是说,当一个登录认证请求结束后,用户相关的SecurityContext对象将从SecurityContextHolder中清除了。当用户发起下一个请求时,又必须重新进行身价认证,如何使SecurityContext在Session级别中进行共享呢?

Acegi通过HttpSessionContextIntegrationFilter解决这个问题,当一个请求到达时,它尝试从Session中获取用户关联的SecurityContext并将其放入到SecurityContextHolder中,当请求结束时,HttpSessionContextIntegrationFilter又将SecurityContext转存到HttpSession中。这样,Acegi就通过HttpSessionContextIntegrationFilter将SecurityContext对象在请求级的SecurityContextHolder和Session级的HttpSession中摆渡,从而保证SecurityContext可以在多个请求之间共享。

在过滤器链中,HttpSessionContextIntegrationFilter必须位于认证处理过滤器之前,这样认证处理过滤器当发现SecurityContextHolder中已经拥有和用户关联的经过认证的Authentication时,就可以短路掉用户身份认证的步骤:

代码清单 10 applicationContext-acegi-plugin.xml

通过HttpSession转存请求之间的SecurityContext

<bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy"> <property name="filterInvocationDefinitionSource"> <value>/**=httpSessionContextIntegrationFilter,authenticationProcessingFilter ① </value> </property> </bean> <bean id="httpSessionContextIntegrationFilter" ②通过HttpSession转存SecurityContext的过滤器 class="org.acegisecurity.context.HttpSessionContextIntegrationFilter"/> …

在①处,我们将httpSessionContextIntegrationFilter放置在authenticationProcessingFilter之前。如果用户还未通过身份认证,httpSessionContextIntegrationFilter在HttpSession中找不到对应的SecurityContext,这时authenticationProcessingFilter将启用正常的认证流程。反之,如果已经通过了身份认证,SecurityContext将直接从HttpSession中获取。

0
相关文章