Acegi中的HttpSessionEvent监听机制的详细内容介绍
Acegi中的HttpSessionEvent监听机制的详细内容介绍
先推荐一下SpringSide,写得有点乱,欢迎指正错误。msn:qian_bj@hotmail.com
Acegi 中的HttpSessionEvent 监听机制 窥视Acegi的 工作流程。
Acegi结合了Spring 提供了不错的HttpSessionEvent监听机制
使用Acegi的HttpSessionEvent监听机制,我首先要在web.xml中增加一个Listener
org.acegisecurity.ui.session.HttpSessionEventPublisher
有人要问这不是一个Listener么,类名应该是一个*Listener什么的埃的确它实现了javax.servlet.ServletContextListener接口和javax.servlet.http.HttpSessionListener接口。前者是在web应用创建时,可以可以通过ServletContext来获取一个Spring的ApplicationContext。后者是在HttpSession创建和超时时发布利用Spring的ApplicationContext. publishEvent()方法发布事件的,注意并不时发布的HttpSesionEvent,而是有HttpSession作为构造参数,创建了HttpSessionCreatedEvent、HttpSessionDestroyedEvent,这两个Event都是Spring的ApplicationEvent的子类,这样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需要存储客户端用户的SessionInformation,SessionInformation里面又有什么,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没有处理HttpSessionCreatedEvent,Acegi也没有提供其他的Spring ApplicationListener来处理这个HttpSessionCreatedEvent。
注意,我们的程序一般并不用直接与SessionRegistryImpl打交道,你只需在Spring的配置文件定义一个Bean就行了,如
<bean id="sessionRegistry" class="org.acegisecurity.concurrent.SessionRegistryImpl"/> |
只是如此而已。
那么当HttpSession创建时,Acegi并不做处理,还会有其他的咚咚来处理么;SessionRegistryImpl还需要存储用户的Principal,那么什么咚咚与SessionRegistryImpl打交道呢?
有,但不过不是ApplicationListener了,别忘了,SpringSide还定义了一系列的Filter让Acegi来做过滤(详情请见org.springside.bookstore.plugins.security. applicationContext-security-acegi.xml),那什么Filter与实现了SessionRegistry接口的SessionRegistryImpl打交道呢?下面我来介绍一下ConcurrentSessionFilter,这个Filter是与SessionRegistryImpl打交道的。我们先看一下该Filter的doFilter()的方法(截取了部分代码)。
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);
} |
各位看官可能要问为什么要调用