Acegi中的HttpSessionEvent监听机制的详细内容介绍

Acegi中的HttpSessionEvent监听机制的详细内容介绍

先推荐一下SpringSide,写得有点乱,欢迎指正错误。msn:qian_bj@hotmail.com


Acegi 中的HttpSessionEvent 监听机制 窥视Acegi 工作流程。

Acegi结合了Spring 提供了不错的HttpSessionEvent监听机制

使用AcegiHttpSessionEvent监听机制,我首先要在web.xml中增加一个Listener

org.acegisecurity.ui.session.HttpSessionEventPublisher

有人要问这不是一个Listener么,类名应该是一个*Listener什么的埃的确它实现了javax.servlet.ServletContextListener接口和javax.servlet.http.HttpSessionListener接口。前者是在web应用创建时,可以可以通过ServletContext来获取一个SpringApplicationContext。后者是在HttpSession创建和超时时发布利用SpringApplicationContext. publishEvent()方法发布事件的,注意并不时发布的HttpSesionEvent,而是有HttpSession作为构造参数,创建了HttpSessionCreatedEventHttpSessionDestroyedEvent,这两个Event都是SpringApplicationEvent的子类,这样Spring才会处理这些Event。事件发布后,Spring会在上下文查找实现了ApplicationListener接口的接口子类来处理这两个Event

到这,你心里或许有些疑惑,HttpSessionEvent发布了,那个ApplicationListener来处理这个HttpSessionEvent呢?注意,重要人物出场了。

org.acegisecurity.concurrent. SessionRegistryImpl出场了。看了它的源代码,你会发现它还实现另外一个接口org.acegisecurity.concurrent. SessionRegistry

这个定义如下一些方法

 

public interface SessionRegistry {

public Object[] getAllPrincipals();

public SessionInformation[] getAllSessions(Object principal, boolean includeExpiredSessions);

public SessionInformation getSessionInformation(String sessionId);

public void refreshLastRequest(String sessionId);

public void registerNewSession(String sessionId, Object principal)

throws SessionAlreadyUsedException;

public void removeSessionInformation(String sessionId);

}

这些方法的功能通过看方法名就能知道了。

看一下SessionRegistryImpl. onApplicationEvent()方法,看它做了些什么

 

public void onApplicationEvent(ApplicationEvent event) {

if (event instanceof HttpSessionDestroyedEvent) {

String sessionId = ((HttpSession) event.getSource()).getId();

removeSessionInformation(sessionId);

}

}

它怎么只处理HttpSessionDestoryedEvent,这就说SessionRegistryImpl只处理HttpSession的销毁,也就是这又是为什么呢。如果你仔细看了SessionRegistry的接口定义,或许能明白一些,SessionRegistryImpl需要存储客户端用户的SessionInformationSessionInformation里面又有什么,SesssionInformation有四个字段

 

private Date lastRequest;

private Object principal;

private String sessionId;

private boolean expired = false;

除了principal,其他三个字段应该很容易理解,那这个Principal是什么东西呢,Principal可以理解为当前客户端用户的身份信息,这个身份信息可以包含用户名,用户密码,用户在系统里面拥有的权限。Acegi提供了一个UserDetail来表示用户的身份。

 

public interface UserDetails extends Serializable {

public GrantedAuthority[] getAuthorities();

public String getPassword();

public String getUsername();

public boolean isAccountNonExpired();

public boolean isAccountNonLocked();

public boolean isCredentialsNonExpired();

public boolean isEnabled();

}

顺便提一下SessionRegistryImpl处理HttpSessionDestoryedEvent是,只是把这个HttpSesion的有关信息也就是SessionInformation从存储中移出。

通过上面的分析,你觉得每当新的HttpSession创建时,SessionRegistryImpl也处理这个Event还有意义吗?事实上SessionRegistryImpl没有处理HttpSessionCreatedEventAcegi也没有提供其他的Spring ApplicationListener来处理这个HttpSessionCreatedEvent

注意,我们的程序一般并不用直接与SessionRegistryImpl打交道,你只需在Spring的配置文件定义一个Bean就行了,如

 

<bean id="sessionRegistry" class="org.acegisecurity.concurrent.SessionRegistryImpl"/>

只是如此而已。

那么当HttpSession创建时,Acegi并不做处理,还会有其他的咚咚来处理么;SessionRegistryImpl还需要存储用户的Principal,那么什么咚咚与SessionRegistryImpl打交道呢?

有,但不过不是ApplicationListener了,别忘了,SpringSide还定义了一系列的FilterAcegi来做过滤(详情请见org.springside.bookstore.plugins.security. applicationContext-security-acegi.xml),那什么Filter与实现了SessionRegistry接口的SessionRegistryImpl打交道呢?下面我来介绍一下ConcurrentSessionFilter,这个Filter是与SessionRegistryImpl打交道的。我们先看一下该FilterdoFilter()的方法(截取了部分代码)

 

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)

throws IOException, ServletException {

------------------

HttpServletRequest httpRequest = (HttpServletRequest) request;

HttpServletResponse httpResponse = (HttpServletResponse) response;

HttpSession session = httpRequest.getSession(false);

if (session != null) {

SessionInformation info = sessionRegistry.getSessionInformation(session.getId());

if (info != null) {

if (info.isExpired()) {

// Expired - abort processing

session.invalidate();

String targetUrl = httpRequest.getContextPath() + expiredUrl;

//Session已经过期,转向一个targetUrl,比如登陆页面

httpResponse.sendRedirect(httpResponse.encodeRedirectURL(targetUrl));

return;

} else {

// Non-expired - update last request date/time

info.refreshLastRequest();

}

}

}

chain.doFilter(request, response);

}

各位看官可能要问为什么要调用