JAva無状態登録実現方式のThreadLocal+Cookie


注意:本明細書で説明するステータスレスとは、sessionが認証を完了し、ユーザーパッケージ情報を取得する必要がないことを意味します.
ステータスなしのメリット:
1,マルチアプリケーションワンポイントログイン:マルチアプリケーションの場合はログインサーバにログインした後、各サブアプリケーションは再ログインする必要はありません.
2,マルチサーバクラスタ:セッション共有のキャッシュを作成することなく実現できる.
このシナリオの欠点:
1,クッキーに依存し,現在主流のブラウザではクッキーがサポートされているが.
2,ワンポイントログインには,各サブアプリケーションが同一のプライマリドメイン名の下に属する必要がある(プライマリドメイン名にまたがっては実現できない).
実現原理:
ログイン時にユーザ情報をカプセル化し,ユーザ情報をシーケンス化暗号化によりユーザクッキーに書き込む.ユーザが次にアプリケーションサーバを要求すると,フィルタはユーザ情報を逆復号逆シーケンス化してThreadLocalに入れ,ThreadLocalのスレッドセキュリティ特性を利用してユーザ情報を取り込む.
ユーザーカプセル化情報クラス
package com.xxx.commons.framework.bean;

import java.io.Serializable;

public class Principal implements Serializable {
	private static final long serialVersionUID = -1373760761780840081L;
	
	private Long id;
	private String username;
	private Integer userType;
	private Long pharmacyId;
	private Long saleManId;
	private Long ydId;
	private String name;

	public Principal(Long id, String username,Integer userType,Long pharmacyId,Long saleManId,Long ydId,String name) {
		this.id = id;
		this.username = username;
		this.userType = userType;
		this.pharmacyId = pharmacyId;
		this.saleManId = saleManId;
		this.ydId = ydId;
		this.setName(name);
	}

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	@Override
	public String toString() {
		return username;
	}

	public Integer getUserType() {
		return userType;
	}

	public void setUserType(Integer userType) {
		this.userType = userType;
	}

	/**
	 * @return pharmacyId
	 *       
	 */
	public Long getPharmacyId() {
		return pharmacyId;
	}

	/** 
	 * @param pharmacyId  
	 *       
	 */
	public void setPharmacyId(Long pharmacyId) {
		this.pharmacyId = pharmacyId;
	}

	/**
	 * @return saleManId
	 *       
	 */
	public Long getSaleManId() {
		return saleManId;
	}

	/** 
	 * @param saleManId  
	 *       
	 */
	public void setSaleManId(Long saleManId) {
		this.saleManId = saleManId;
	}

	/**
	 * @return ydId
	 *       
	 */
	public Long getYdId() {
		return ydId;
	}

	/** 
	 * @param ydId  
	 *       
	 */
	public void setYdId(Long ydId) {
		this.ydId = ydId;
	}

	/**
	 * get name
	 * @return the name
	 *       
	 */
	public String getName() {
		return name;
	}

	/** 
	 * set name
	 * @param name  
	 *       
	 */
	public void setName(String name) {
		this.name = name;
	}
	
}

ユーザー情報ツールクラス
/**   
 * Copyright RH Corporation 2014       
 * Created 2014 12 18    1:24:27 
 * @version V1.0   
 */
package com.xxx.commons.framework.utils;

import com.xxx.commons.framework.bean.Principal;

/** 
 *      
 * @author		ElongDeo
 * @version		1.0
 * Created		2014 12 18    1:24:27			
 */
public class UserUtil {
	public static final ThreadLocal<Principal> principal = new ThreadLocal<Principal>(); 
	
	public static Principal getUserPrincipal(){
		Principal principal = UserUtil.principal.get();
		return principal;
	}

	public static String getUserName(){
		String userName = "";
		Principal principal = getUserPrincipal();
		if(principal!=null){
			userName = principal.getUsername();
		}
		return userName;
	}
	
	public static String getName(){
		String name = "";
		Principal principal = getUserPrincipal();
		if(principal!=null){
			name = principal.getName();
		}
		return name;
	}
	
	public static Long getUserId(){
		Long userId = null;
		Principal principal = getUserPrincipal();
		if(principal!=null){
			userId = principal.getId();
		}
		return userId;
	}
	
	public static Integer getUserType(){
		Integer userType = null;
		Principal principal = getUserPrincipal();
		if(principal!=null){
			userType = principal.getUserType();
		}
		return userType;
	}
	
	public static Long getPharmacyId(){
		Long pharmacyId = null;
		Principal principal = getUserPrincipal();
		if(principal!=null){
			pharmacyId = principal.getPharmacyId();
		}
		return pharmacyId;
	}
	
	public static Long getSaleManId(){
		Long saleManId = null;
		Principal principal = getUserPrincipal();
		if(principal!=null){
			saleManId = principal.getSaleManId();
		}
		return saleManId;
	}
	
	public static Long getYdId(){
		Long ydId = null;
		Principal principal = getUserPrincipal();
		if(principal!=null){
			ydId = principal.getYdId();
		}
		return ydId;
	}
	
	public static Long getBuyerId(){
		Long buyerId = null;
		Integer userType = getUserType();
		if(userType != null && userType > Constants.USER_ADMIN_TYPE){
			if(userType.equals(Constants.USER_PHARMARY_TYPE)){
				buyerId = getPharmacyId();
			}else{
				buyerId = getYdId();
			}
		}
		return buyerId;
	}
	
	public static String getCartYn(){
		String cartYn = "no";
		Integer userType = getUserType();
		if(userType > Constants.USER_ADMIN_TYPE){
			cartYn = "yes";
		}
		return cartYn;
	}
	
}

Cookiesツールクラス(ユーザー情報のカプセル化/解析に使用)
/**
 * CookieUtils.java Copyright © 2008-2013 lefeng.com Inc. All Rights Reserved.
 */
package com.xxx.commons.framework.utils;


import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.codec.binary.Base64;

import com.xxx.commons.framework.bean.Principal;
import com.xxx.commons.items.PropertiesFileLoader;

/**
 * <pre>
 * <P>Author : ElongDeo</P> 
 * <P>Date : 2014-3-10 </P>
 * <P>Cookie     </P>
 * </pre>
 */
public class CookieUtils {
	public static String DOMAIN = ".xxx.com";
	public static final String COOKIE_TOKEN_LOGIN = "xxx_token";
	public static final String COOKIE_USER_INFO = "xxx_user";
	
	static {
		PropertiesFileLoader instance = PropertiesFileLoader.getInstance();
		DOMAIN = instance.getProerties("config/user.properties","domain");
	}
	/**
	 *   cookie
	 * @param response
	 * @param name  cookie  
	 * @param value cookie 
	 * @param maxAge cookie           
	 */
	public static void addCookie(HttpServletResponse response,String name,String value,int maxAge, String domain){
	    Cookie cookie = new Cookie(name,value);
	    cookie.setDomain(domain);
	    cookie.setPath("/");
	    if(maxAge>0)  cookie.setMaxAge(maxAge);
	    response.addCookie(cookie);
	}
	
	/**
	 *       cookie
	 * @param request
	 * @param name cookie  
	 * @return
	 */
	public static Cookie getCookieByName(HttpServletRequest request,String name){
	    Map<String,Cookie> cookieMap = readCookieMap(request);
	    if(cookieMap.containsKey(name)){
	        Cookie cookie = (Cookie)cookieMap.get(name);
	        return cookie;
	    }else{
	        return null;
	    }   
	}
	
	/**
	 *  cookie   Map  
	 * @param request
	 * @return
	 */
	public static Map<String,Cookie> readCookieMap(HttpServletRequest request){  
	    Map<String,Cookie> cookieMap = new HashMap<String,Cookie>();
	    Cookie[] cookies = request.getCookies();
	    if(null!=cookies){
	        for(Cookie cookie : cookies){
	            cookieMap.put(cookie.getName(), cookie);
	        }
	    }
	    return cookieMap;
	}
	
	public static Principal getPrincipal(HttpServletRequest request) {
		Cookie cookie = getCookieByName(request, COOKIE_USER_INFO);
		if (cookie != null && !"".equals(cookie.getValue())) {
			try {
				return (Principal) SerializeUtils.deserialize(Base64.decodeBase64(cookie.getValue()));
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return null;
	}
	
	public static void setPrincipal(HttpServletResponse response, Principal principal) {
		try {
			addCookie(response, COOKIE_USER_INFO, Base64.encodeBase64String(SerializeUtils.serialize(principal)), 0, DOMAIN);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public static void removePrincipal(HttpServletResponse response) {
		try {
			addCookie(response, COOKIE_USER_INFO, null, 0, DOMAIN);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
}

ログイン書き込みクッキーコードクリップ
			Principal principal = new Principal(userId,  login, userType, pharmacyId, saleManId, ydId, name);
			//    ,   cookie      
			try {
				
				CookieUtils.setPrincipal(response, principal);
				redirect = StringUtils.isEmpty(request.getParameter("redirect"))?LOGIN_REDIRECT_URL:request.getParameter("redirect");//       
				PrintUtils.printToMobile(response, new ResultObject<Object>(1, redirect), "json");
				return;
			} catch (Exception e) {
				e.printStackTrace();
			}

フィルタはユーザ情報を取得してThreadLocalに入れる
/**
 * 
 */
package com.xxx.commons.framework.filters;

import java.io.IOException;
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.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;

import com.xxx.commons.framework.utils.CookieUtils;
import com.xxx.commons.framework.utils.StringUtils;
import com.xxx.commons.framework.utils.UserUtil;



/**
 * Servlet Filter implementation class AuthenticationFilter
 */
public class PrincipalFilter implements Filter {
	
	Logger logger = Logger.getLogger(PrincipalFilter.class);
	private static String notLoginUrl = null;

	//       URL.
	private static Set<String> mobjIgnoredUrls = new HashSet<String>();

	/**
	 * @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
	 */
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		UserUtil.principal.set(CookieUtils.getPrincipal((HttpServletRequest)request));
		if(notLoginUrl != null && UserUtil.principal.get() == null && !isIgnoreUrl((HttpServletRequest)request)){
			((HttpServletResponse)response).sendRedirect(notLoginUrl);
			return;
		}
		chain.doFilter(request, response);
	}

	/**
	 *      
	 * @author ElongDeo 2015 6 26 
	 * @param filterConfig
	 * @throws ServletException
	 */
	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		notLoginUrl = filterConfig.getInitParameter("notLoginUrl");
		//        URL
		String urlText = filterConfig.getInitParameter("ignoredUrls");
		if(urlText != null){
			urlText = urlText.replaceAll("\r
", "").replaceAll("\t", "").trim(); String[] urls = urlText.split(","); for (int i = 0; i < urls.length; i++) { mobjIgnoredUrls.add(urls[i]); } } } /** * <pre> * URL. * </pre> * * @param pobjRequest * the pobjRequest * @return true, if is ignore url * @author guotianchi 2011-4-20 */ private boolean isIgnoreUrl(HttpServletRequest pobjRequest) { String objRequestUri = pobjRequest.getRequestURI(); if (StringUtils.isNotEmpty(objRequestUri)) { int index = objRequestUri.lastIndexOf('/'); if (index >= 0 && index < (objRequestUri.length() - 1) && mobjIgnoredUrls.contains(objRequestUri.substring( index + 1, objRequestUri.length()))) { return true; } } return false; } /** * * @author ElongDeo 2015 6 26 */ @Override public void destroy() { } }

アプリケーションサーバweb.xml構成
    <filter>
		<filter-name>PrincipalFilter</filter-name>
		<filter-class>com.xxx.commons.framework.filters.PrincipalFilter</filter-class>
		<init-param>
			<param-name>notLoginUrl</param-name>
			<param-value>/common/logout.htm</param-value>
		</init-param>
		<init-param>
			<param-name>ignoredUrls</param-name>
			<param-value>logout.htm</param-value>
		</init-param>
	</filter>

    <filter-mapping>
		<filter-name>PrincipalFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>