/* ================================================================== * Created [2009-4-27 下午11:32:55] by Jon.King * ================================================================== * TSS * ================================================================== * mailTo:jinpujun@hotmail.com * Copyright (c) Jon.King, 2009-2012 * ================================================================== */ package com.jinhe.tss.core.sso.context; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.log4j.Logger; import com.jinhe.tss.core.Config; import com.jinhe.tss.core.sso.IdentityCard; import com.jinhe.tss.core.web.wrapper.AvoidRepeatHttpServletResponseWrapper; /** * <p> Context.java </p> * <p> * 应用上下文环境静态对象。<br/> * 利用线程变量(ThreadLocal)保存用户每次请求的request、response、身份证、令牌等信息,<br/> * 以便其它地方程序需要获取这些信息的时候可以方便的通过Environment或者Context对象获取。<br/> * 这些信息在ContextFilter中完成设置,所以ContexFilter需在AutoLoginFilter、HttpProxyFilter等filter之前配置好。<br/> * </p> */ public final class Context { static Logger log = Logger.getLogger(Context.class); /** * 应用系统上下文信息,保存当前应用系统信息,基于应用 */ private static ApplicationContext appContext = null; /** * 当前请求上下文信息,基于request(每次请求),所以它的容器采用ThreadLocal */ private static ThreadLocal<RequestContext> requestLocal = new ThreadLocal<RequestContext>(); /** * 当前响应对象信息,基于request(每次请求) */ private static ThreadLocal<HttpServletResponse> responseLocal = new ThreadLocal<HttpServletResponse>(); /** * 用户令牌,基于request(每次请求)。<br/> * 令牌信息(基于session不变)本应该只存放与session中即可,<br/> * 为了满足非web情况(也就是无session时)线程可以方便的存放令牌,故有此tokenLocal,类似cardsMap作用<br/> */ private static ThreadLocal<String> tokenLocal = new ThreadLocal<String>(); /** * 用来存放card,当类似CMS中定时器操作时,也需要Context.initIdentityInfo(card)来设置用户信息,而这种情况下<br/> * 没有servlet请求所以没法生成RequestContext,也就无法往session里存放card信息,故由此Map。当用户退出session销毁时,<br/> * 调用destroyIdentityCard(token)。<br/> * TODO 有隐患:如上述情况放入进来的card,如何去除?<br/> */ private static Map<String, IdentityCard> cardsMap = new HashMap<String, IdentityCard>(); /** * <p> * 获取当前系统上下文信息 * </p> * @return */ public static ApplicationContext getApplicationContext() { if (appContext == null) { appContext = new ApplicationContext(); } return appContext; } /** * <p> * 初始化应用系统上下文信息 * </p> * @param context */ public static void initApplicationContext(ApplicationContext context) { appContext = context; } /** * <p> * 初始化当前请求上下文信息 * </p> * @param request */ public static void initRequestContext(HttpServletRequest request) { RequestContext rc = new RequestContext(request); requestLocal.set(rc); // 令牌token(sessionId + userId组成)生成后放在session当中, // 每次请求都需要将其设置token线程变量中,因为线程变量每次线程结束就会被垃圾回收掉 IdentityCard card = rc.getIdentityCard(); if(card != null){ setToken(card.getToken()); } //log.debug("完成初始化应用【" + Config.getAttribute(ApplicationContext.APPLICATION_CODE) + "】里当前请求的RequestContext。"); } /** * <p> * 获取当前请求上下文信息,如果没有初始化则返回Null * </p> * @return */ public static RequestContext getRequestContext() { return requestLocal.get(); } /** * <p> * 获取用户令牌。不管是web环境请求还是直接执行JAVA方法,<br/> * 前者会在ContexFilter调用到initRequestContext来设置tokenLocal(如果是首次登陆,则登陆成功后会调用initIdentityInfo);<br/> * 后者需要手动创建一个IdentityCard对象,然后调用initIdentityInfo方法来设置tokenLocal。<br/> * 这些操作都需要在第一步执行(所以ContexFilter需在AutoLoginFilter、HttpProxyFilter等filter之前配置)。<br/> * 如此可保证每次需要调用Context.getToken时token都已经存在在tokenLocal中。<br/> * </p> * @see Context.initRequestContext(HttpServletRequest request) * @see Context.initIdentityInfo(IdentityCard card) * @return */ public static String getToken() { return tokenLocal.get(); } /** * <p> * 设置用户令牌 * </p> * @param token */ public static void setToken(String token) { tokenLocal.set(token); } /** * 用户登陆成功后初始化用户的身份证信息。将身份证放入到session中,同时也放入到cardsMap中(非web情况即无session时该map会用到),<br/> * 最后还需将令牌信息放入到tokenLocal中。<br/> * @param card */ public static void initIdentityInfo(IdentityCard card) { RequestContext rc = getRequestContext(); if (rc != null) { HttpSession session = rc.getSession(); // 判断是否是注册用户(包括匿名用户)登录系统。是的话将令牌等信息放入session if (session != null && card != null) { session.setAttribute(RequestContext.IDENTITY_CARD, card); session.setAttribute(RequestContext.USER_TOKEN, card.getToken()); } } if(card != null){ cardsMap.put(card.getToken(), card); //在用户注销的时候将其去除,在Session监听器里去 setToken(card.getToken()); } log.debug("完成在应用【" + Config.getAttribute(Config.APPLICATION_CODE) + "】里设置用户(" + card + ")的身份证、令牌等信息。"); } /** * 在用户注销的时候将其去除,在Session监听器里去 */ public static void destroyIdentityCard(String token) { cardsMap.remove(token); tokenLocal.set(null); } /** * 获取用户的身份证对象。web环境下存放在session里和cardsMap里。 * 非web环境(单元测试环境、定时器操作等)只存放与cardsMap中。 * @see Environment.getOperatorId() * @return */ public static IdentityCard getIdentityCard(){ IdentityCard card = (IdentityCard) cardsMap.get(getToken()); if(card == null){ RequestContext rc = getRequestContext(); if(rc != null){ card = rc.getIdentityCard(); } } return card; } /** * 判断用户是否在线。根据用户的身份证信息是否为空 * @return */ public static boolean isOnline(){ return getIdentityCard() != null; } /** * <p> * 销毁用户登录、访问相关上下文信息 * </p> */ public static void destroy() { RequestContext rc = getRequestContext(); if (rc != null) { rc.destroy(); } requestLocal.set(null); responseLocal.set(null); } /** * <p> * 获取response的值 * </p> * @return 返回response的值 */ public static HttpServletResponse getResponse() { return responseLocal.get(); } /** * <p> * 设置response的值。 * 将response封装成AvoidRepeatHttpServletResponseWrapper,可防止写入同名的cookie。 * </p> * @param 将response的值赋值给response */ public static void setResponse(HttpServletResponse response) { responseLocal.set(new AvoidRepeatHttpServletResponseWrapper(response)); } }