java无状态登录实现方式之ThreadLocal+Cookie

Posted lxjshuju

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java无状态登录实现方式之ThreadLocal+Cookie相关的知识,希望对你有一定的参考价值。

注:本文提到的无状态指的是无需session完毕认证、取用户封装信息。

无状态的优点:

  1。多应用单点登录:在多应用的时候仅仅需在登录server登录后。各子应用无需再次登录。

  2。多server集群:无需制作会话共享的缓存就可以实现。

此方案的缺点:

  1,依赖于cookie,尽管如今主流浏览器都支持cookie。

  2。单点登录须要各子应用属于同一主域名下(跨主域名无法实现)。

实现原理:

  登录时封装用户信息,并将用户信息通过序列化加密写到用户cookie。当用户下次请求应用server时,过滤器将用户信息取到反解密反序列化后放入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();
		}
	}
	
}


登录写入cookie代码片段

			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\n", "").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() {
	}
}

应用serverweb.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>
技术分享









以上是关于java无状态登录实现方式之ThreadLocal+Cookie的主要内容,如果未能解决你的问题,请参考以下文章

DRF框架之用户登录状态保持

Java 200+ 面试题补充 ThreadLocal 模块

使用ThreadLocal实现当前登录信息的存取

ThreadLocal学习笔记

Java面试题必备知识之ThreadLocal

java web 之Session