/* ==================================================================
* 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.web.filter;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import com.jinhe.tss.core.exception.BusinessException;
import com.jinhe.tss.core.exception.BusinessServletException;
import com.jinhe.tss.core.exception.UserIdentificationException;
import com.jinhe.tss.core.sso.ILoginCustomizer;
import com.jinhe.tss.core.sso.IUserIdentifier;
import com.jinhe.tss.core.sso.IdentityCard;
import com.jinhe.tss.core.sso.LoginCustomizerFactory;
import com.jinhe.tss.core.sso.UserIdentifierFactory;
import com.jinhe.tss.core.sso.context.Context;
import com.jinhe.tss.core.sso.context.RequestContext;
import com.jinhe.tss.core.sso.identifier.AnonymousUserIdentifier;
import com.jinhe.tss.core.sso.identifier.OnlineUserIdentifier;
/**
* <p> AutoLoginFilter.java </p>
*
* <pre>
* 身份认证:<br/>
* 检验用户身份是否合法,如果合法顺利通过;<br/>
* 如果不合法,通过判断是否登录其他系统,使用单点登录方式自动登录系统,并顺利通过;<br/>
* 如果不能使用单点登录方式登录,判断是否可以匿名访问,如果可以,使用匿名用户登录,并顺利通过;<br/>
* 如果不能匿名访问或过程中出现错误信息,则抛出异常,返回客户端<br/>
* 关于匿名访问:<br/>
* 将匿名用户和普通用户完全一样处理<br/>
* </pre>
*
*/
public class AutoLoginFilter implements Filter {
private static Logger log = Logger.getLogger(AutoLoginFilter.class);
private Set<String> ignoreServletPaths = new HashSet<String>();
public void init(FilterConfig filterConfig) throws ServletException {
String paths = filterConfig.getInitParameter("ignoreServletPaths");
if (paths != null) {
ignoreServletPaths.addAll(Arrays.asList(paths.split(",")));
}
log.info("自动登录过滤器初始化完成!appCode=" + Context.getApplicationContext().getCurrentAppCode());
}
public void destroy() {
ignoreServletPaths = null;
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
try {
String servletPath = RequestContext.getServletPath((HttpServletRequest)request);
log.debug("当前请求ServletPath: " + servletPath);
if (!ignoreServletPaths.contains(servletPath)) {
IdentityCard card = authenticate();
/*
* 初次登录或从其他系统SSO过来时,会新生成一个在本系统内通行的IdentityCard 。
* 身份认证通过,进入系统(身份认证合法以后,根据普通用户或匿名用户分别处理登录过程)。
*/
if (card != null) {
/* 登录系统,初始化用户身份信息 */
Context.initIdentityInfo(card);
/* 登录自定义操作 */
customizerExcuteAfterLogin();
/* 保存Cookie信息到客户端 */
setCookie((HttpServletResponse)response, RequestContext.USER_TOKEN, card.getToken());
}
}
chain.doFilter(request, response);
} catch (UserIdentificationException e) {
throw new BusinessServletException(e, true);
} catch (Exception e) {
throw new BusinessServletException(e);
}
}
/**
* 验证用户的登录信息,确认当前请求的用户是否已经已经登录(根据token和在线用户库判断),或者登录信息是否正确(userIdentifierClassName != null时,一般为初次登录)
*/
private IdentityCard authenticate() throws UserIdentificationException {
String currentAppCode = Context.getApplicationContext().getCurrentAppCode();
IdentityCard card = null;
/* 正常登录过程,正常登陆时在用户输入loginName请求系统返回userName的时同时返回的还有“用户对应的身份认证对象类名” */
String userIdentifierClassName = Context.getRequestContext().getUserIdentifierClassName();
if (userIdentifierClassName != null) {
card = validate(userIdentifierClassName);
log.debug(currentAppCode + "【登录模块】用户登录:" + card.getLoginName());
return card;
}
// 已登录、超时(跳转)或者是匿名访问
String token = Context.getRequestContext().getUserToken();
if (token == null) {
// 匿名用户登录
card = validate(AnonymousUserIdentifier.class.getName());
log.debug(currentAppCode + "【登录模块】匿名用户登录");
return card;
}
/* 如果请求中带的token和session中的token一致,说明此token已成功登陆过,pass直接访问 */
if (token.equals(Context.getRequestContext().getAgoToken())) {
log.debug(currentAppCode + "【登录模块】已在线,直接访问");
}
else {
/*
* 如果请求中带的token和session中的token不一致,有两种可能:
* 1、session中AgoToken为空(当应用跳转时(如CMS跳到TSS时)会出现)
* 2、请求中的token是伪造的
*
* 执行在线用户自动登录(用户已经登陆过,已经在在线用户库中注册)。。。。。。
*/
card = validate(OnlineUserIdentifier.class.getName());
if (card != null) {
log.debug(currentAppCode + "【登录模块】在线用户登录:" + card.getLoginName());
} else {
/* 如果token验证不通过(token没在在线用户库中注册过,过期了或伪造的),则采用匿名用户登录 */
card = validate(AnonymousUserIdentifier.class.getName());
log.debug(currentAppCode + "【登录模块】匿名用户登录");
}
}
return card;
}
/**
* <p>
* 验证用户是否合法,包括正常登录、通过在线用户库单点登录、匿名用户登录等的身份验证。 <br/>
* 1、如果身份合法,返回身份证对象; <br/>
* 2、如果验证过程中产生需要重新登录的情况,都抛出UserIdentificationException异常 <br/>
* 3、如果用户Token在在线用户库里没注册,说明Token令牌是伪造的或是已经过期的,返回null,表示验证不通过(见OnlineUserIdentifier)。<br/>
* </p>
*
* @param userIdentifierClassName 身份验证器对象全类名
* @return
* @throws UserIdentificationException
*/
private IdentityCard validate(String userIdentifierClassName) throws UserIdentificationException {
IUserIdentifier identifier = UserIdentifierFactory.instance().getUserIdentifier(userIdentifierClassName);
return identifier.identify();
}
/**
* <p>
* 执行用户登录自定义类,只有用户登录时才执行
* </p>
*/
private void customizerExcuteAfterLogin() {
ILoginCustomizer customizer = LoginCustomizerFactory.instance().getCustomizer();
try {
customizer.execute();
} catch (Throwable e) {
String customizerName = customizer.getClass().getName();
String msg = "自定义登录操作(" + customizerName + ")失败,请先检查数据源配置是否正确。";
throw new BusinessException(msg, e);
}
}
/**
* <p>
* 设置需要返回的Cookie对象到response中
* </p>
* @param res HttpServletResponse 返回对象
* @param name String 名称
* @param value String 值
*/
private void setCookie(HttpServletResponse response, String name, String value) {
HttpServletRequest req = Context.getRequestContext().getRequest();
Cookie cookie = new Cookie(name, value);
cookie.setPath(req.getContextPath());
cookie.setMaxAge(-1);
cookie.setSecure(req.isSecure());
response.addCookie(cookie);
}
}