技术开发 频道

基于jsessionid的单点登录经验总结

  3. 基于jsessionid的URL重写

  当程序需要为某个客户端的请求创建一个session的时候,服务器首先检查这个客户端的请求里是否已包含了一个session标识,称为 session id,如果已包含一个session id则说明以前已经为此客户端创建过session,服务器就按照session id把这个session检索出来使用(如果检索不到,可能会新建一个),如果客户端请求不包含session id,则为此客户端创建一个session并且生成一个与此session相关联的session id,session id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个 session id将被在本次响应中返回给客户端保存。

  保存这个session id的方式可以采用cookie,这样在交互过程中浏览器可以自动的按照规则把这个标识发挥给服务器。由于cookie可以被人为的禁止,必须有其他机制以便在cookie被禁止时仍然能够把session id传递回服务器。经常被使用的一种技术叫做URL重写,就是把session id直接附加在URL路径的后面,附加方式也有两种,一种是作为URL路径的附加信息,表现形式为http://...../xxx;jsessionid=ByOK3vjFD75aPnrF7C2H。

  利用这一特性,我们在登录SSO后,就不需要再进行身份认证就可以访问子系统的URL。由于子系统的session id一直都在变化,因此URL重写时添加的jsessionid需要以AJAX的方式根据子系统的KEY值动态地从SSO的Session中获取,具体实现见图3。

基于jsessionid的单点登录经验总结
▲图3 URL重写步骤

  4 Session同步机制与Session过期处理

  4.1 Session同步

  session同步机制本质是就是定时刷新SSO的Session保证其不过期。为了记录SSO的session,我们定义了一个抽象类AbsAppHttpSession,主要用来方便获取SSO端的sessionid,登录的用户信息,及其他session生命周期相关的辅助信息。

public abstract class AbsAppHttpSession {
protected HttpSession session;
public AbsAppHttpSession(HttpSession session) {
this.session = session;
}
/**该session是否关联了登录用户信息,关联了表示登录,未关联表示游客身份登录*/
public abstract boolean isUserLogin();
/**是否是upfw登录的,有可能是本地app系统入口登录的*/
public abstract boolean isUpfwLogin();
/**返回upfwSessionId*/
public abstract String getUpfwSessionId();
/**返回登录用户Id*/
public abstract Long getUserId();
/**返回登录用户名*/
public abstract String getLoginName();
/**返回登录用户姓名*/
public abstract String getUserName();
/**最近session访问时间,按单位秒返回*/
public long getLastAccessedTime(){
return this.session.getLastAccessedTime()/1000;
}
/**该session会话超时时间,按单位秒返回*/
public int getSessionTimeOut(){
return this.session.getMaxInactiveInterval();
}
/**当前session剩余存活时间,按单位秒返回*/
public long getRemainLiveTime(){
long currentTime = System.currentTimeMillis();
long lastAccessedTime = this.session.getLastAccessedTime();
return (currentTime- lastAccessedTime)/1000;
}
/**返回appSessionId*/
public String getAppSessionId(){
return this.session.getId();
}
public HttpSession getHttpSession(){
return this.session;
}
}

  而Session的创建与销毁都由抽象类AbsAppSessionListener来监听,该抽象类实现HttpSessionListener接口,这样就记录下为SSO端的Session列表,能够轻松地实现Session刷新,本质就是对SSO发起一个空的请求。SSO端刷新由abstract class AbsUpfwTaskServlet extends HttpServlet implements Runnable定时地去刷新,其核心方法即实现Runnable接口的方法run。

@Override
public void run() {
Timer timer
= new Timer();
TimerTask timerTask
= new TimerTask() {
@Override
public void run() {
...
}
};
timer.schedule(timerTask, delay,period);
}

  4.2 Session过期处理

  由于SSO的Session一直由APP在定时刷新,因此不存在过期现象;而APP的session过期时则由SSO重新登录生成新的Session,其过程类似于同步登录的过程。该过程主要由拦截器abstract class AbsUPFWAppFilter来实现,其核心方法如下。

@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
...
boolean b
= httpRequest.isRequestedSessionIdFromURL();
if(b){
Cookie cookie
=
new Cookie("JSESSIONID", httpRequest.getSession().getId());
httpResponse.addCookie(cookie);
String upfwSessionId
=
httpRequest.getParameter(Constants.REQ_UPFW_SESSIONID_KEY);
if(upfwSessionId != null && !"".equals(upfwSessionId)){
...
}
}
b
= this.hasSessionUser(httpRequest);
if(b)//如果用户已经登录则放行
chain.doFilter(request, response);
else{
//该请求是否允许游客访问
b = this.canVisitorAccess(httpRequest);
if(b)
//允许允许游客访问则放行
chain.doFilter(request, response);
else{
//判断cookie中是否包含upfwSessionId
b = this.hasCookieUpfwSession(httpRequest);
if(!b){
this.appLoginTimeOut(httpRequest, httpResponse);
}
else{
//判断upfwSession是否过期
b = this.isUpfwSessionTimeOut(this.getUpfwSessionIdFromCookie(httpRequest));
if(b)
//upfwSession过期,则跳到upfwSession过期处理
this.upfwLoginTimeOut(httpRequest, httpResponse);
else{
//重新登录成功后继续放行
this.reLoginUpfw(httpRequest, httpResponse);
chain.doFilter(request, response);
}
}
}
}
}

  5. 小结

  本文使用Web容器中jsessionid特性实现了一种折中的单点登录方案,即不侵入原有系统的认证方案,同时又能保证SSO登录其他APP都可以使用。这一单点登录方案,主要包括同步登录、URL重写、Session同步、Session过期处理,每一步都存在SSO的sessionid与APP的sessionid的交换,SSO的session因为有APP端的定时刷新不存在过期现象,而APP端session过期后,SSO会重新登录APP,从而建立起SSO与APP的session映射。这一方案对快速集成已经投入使用的系统身份认证具有一定的参考意义。

  6. 作者简介

  俞超 软件开发工程师

  任职于某大型IT外资企业,主要从事J2EE开发、设计工作。

  参考资料

  http://www.cnblogs.com/kevinGao/archive/2012/08/10/2671010.html

  http://www.cnblogs.com/zsuxiong/archive/2011/11/09/2241622.html

  http://wentao365.iteye.com/blog/1242768

  http://sunxboy.iteye.com/blog/217262

7