1. 程式人生 > >20單點登入

20單點登入

 

傳統登入問題:

之前實現的登入和註冊是在同一個tomcat內部完成。我們現在的系統架構是每一個系統都是由一個團隊進行維護,每個系統都是單獨部署執行一個單獨的tomcat,所以,不能將使用者的登入資訊儲存到session中(多個tomcat的session是不能共享的),所以我們需要一個單獨的系統來維護使用者的登入資訊。

如何解決?

         解決的關鍵就在於如何實現多個Tomcat之間的資料共享,Session是無法完成這一點的,因此我們需要用其它東西來代替Session的功能。

要代替Session,需要具備怎樣的特點?

  1. 記憶體中儲存,速度快
  2. 資料具有時效性
  3. 多專案資料共享

而這些特性Redis都具備,因此我們的解決方案就是使用Redis來代替Session。在一個獨立系統中完成登入註冊的邏輯,並完成使用者登入時資料儲存到Redis中。這樣的一個系統就是單點登入系統(SSO)

====================

之前的登入流程

 

這套流程只能執行在單Tomcat中,因為Session無法共享

現在的登入流程

 

這套流程中,用Redis代替了Session,實現多系統之間的資料共享

 

 

登入成功後,將生成的token儲存自cookie 和Redis中,訪問其他系統時候,拿著cookie去Redis查,如果能查到,說明是該使用者。

==========================

工具類: CookieUtils

package com.taotao.store.web.util;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 
 * Cookie 工具類
 *
 */
public final class CookieUtils {

	protected static final Logger logger = LoggerFactory.getLogger(CookieUtils.class);

	/**
	 * 得到Cookie的值, 不編碼
	 * 
	 * @param request
	 * @param cookieName
	 * @return
	 */
	public static String getCookieValue(HttpServletRequest request, String cookieName) {
		return getCookieValue(request, cookieName, false);
	}

	/**
	 * 得到Cookie的值,
	 * 
	 * @param request
	 * @param cookieName
	 * @return
	 */
	public static String getCookieValue(HttpServletRequest request, String cookieName, boolean isDecoder) {
		Cookie[] cookieList = request.getCookies();
		if (cookieList == null || cookieName == null){
			return null;			
		}
		String retValue = null;
		try {
			for (int i = 0; i < cookieList.length; i++) {
				if (cookieList[i].getName().equals(cookieName)) {
					if (isDecoder) {
						retValue = URLDecoder.decode(cookieList[i].getValue(), "UTF-8");
					} else {
						retValue = cookieList[i].getValue();
					}
					break;
				}
			}
		} catch (UnsupportedEncodingException e) {
			logger.error("Cookie Decode Error.", e);
		}
		return retValue;
	}

	/**
	 * 得到Cookie的值,
	 * 
	 * @param request
	 * @param cookieName
	 * @return
	 */
	public static String getCookieValue(HttpServletRequest request, String cookieName, String encodeString) {
		Cookie[] cookieList = request.getCookies();
		if (cookieList == null || cookieName == null){
			return null;			
		}
		String retValue = null;
		try {
			for (int i = 0; i < cookieList.length; i++) {
				if (cookieList[i].getName().equals(cookieName)) {
					retValue = URLDecoder.decode(cookieList[i].getValue(), encodeString);
					break;
				}
			}
		} catch (UnsupportedEncodingException e) {
			logger.error("Cookie Decode Error.", e);
		}
		return retValue;
	}

	/**
	 * 設定Cookie的值 不設定生效時間預設瀏覽器關閉即失效,也不編碼
	 */
	public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue) {
		setCookie(request, response, cookieName, cookieValue, -1);
	}

	/**
	 * 設定Cookie的值 在指定時間內生效,但不編碼
	 */
	public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, int cookieMaxage) {
		setCookie(request, response, cookieName, cookieValue, cookieMaxage, false);
	}

	/**
	 * 設定Cookie的值 不設定生效時間,但編碼
	 */
	public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, boolean isEncode) {
		setCookie(request, response, cookieName, cookieValue, -1, isEncode);
	}

	/**
	 * 設定Cookie的值 在指定時間內生效, 編碼引數
	 */
	public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, int cookieMaxage, boolean isEncode) {
		doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, isEncode);
	}

	/**
	 * 設定Cookie的值 在指定時間內生效, 編碼引數(指定編碼)
	 */
	public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, int cookieMaxage, String encodeString) {
		doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, encodeString);
	}

	/**
	 * 刪除Cookie帶cookie域名
	 */
	public static void deleteCookie(HttpServletRequest request, HttpServletResponse response, String cookieName) {
		doSetCookie(request, response, cookieName, "", -1, false);
	}

	/**
	 * 設定Cookie的值,並使其在指定時間內生效
	 * 
	 * @param cookieMaxage
	 *            cookie生效的最大秒數
	 */
	private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, int cookieMaxage, boolean isEncode) {
		try {
			if (cookieValue == null) {
				cookieValue = "";
			} else if (isEncode) {
				cookieValue = URLEncoder.encode(cookieValue, "utf-8");
			}
			Cookie cookie = new Cookie(cookieName, cookieValue);
			if (cookieMaxage > 0)
				cookie.setMaxAge(cookieMaxage);
			if (null != request)// 設定域名的cookie
				cookie.setDomain(getDomainName(request));
			cookie.setPath("/");
			response.addCookie(cookie);
		} catch (Exception e) {
			logger.error("Cookie Encode Error.", e);
		}
	}

	/**
	 * 設定Cookie的值,並使其在指定時間內生效
	 * 
	 * @param cookieMaxage
	 *            cookie生效的最大秒數
	 */
	private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, int cookieMaxage, String encodeString) {
		try {
			if (cookieValue == null) {
				cookieValue = "";
			} else {
				cookieValue = URLEncoder.encode(cookieValue, encodeString);
			}
			Cookie cookie = new Cookie(cookieName, cookieValue);
			if (cookieMaxage > 0)
				cookie.setMaxAge(cookieMaxage);
			if (null != request)// 設定域名的cookie
				cookie.setDomain(getDomainName(request));
			cookie.setPath("/");
			response.addCookie(cookie);
		} catch (Exception e) {
			logger.error("Cookie Encode Error.", e);
		}
	}

	/**
	 * 得到cookie的域名
	 */
	private static final String getDomainName(HttpServletRequest request) {
		String domainName = null;

		String serverName = request.getRequestURL().toString();
		if (serverName == null || serverName.equals("")) {
			domainName = "";
		} else {
			serverName = serverName.toLowerCase();
			serverName = serverName.substring(7);
			final int end = serverName.indexOf("/");
			serverName = serverName.substring(0, end);
			final String[] domains = serverName.split("\\.");
			int len = domains.length;
			if (len > 3) {
				// www.xxx.com.cn
				domainName = "." + domains[len - 3] + "." + domains[len - 2] + "." + domains[len - 1];
			} else if (len <= 3 && len > 1) {
				// xxx.com or xxx.cn
				domainName = "." + domains[len - 2] + "." + domains[len - 1];
			} else {
				domainName = serverName;
			}
		}

		if (domainName != null && domainName.indexOf(":") > 0) {
			String[] ary = domainName.split("\\:");
			domainName = ary[0];
		}
		return domainName;
	}

}

=====================================

1登入的程式碼:

controller:

service層:

 

 

 

======================================

其他系統執行的時候,要確保在登入的狀態下進行,所以可以寫攔截器。再呼叫其他介面的時候,先判斷是否登入

package com.taotao.web.interceptor;

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

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import com.taotao.common.utils.CookieUtils;
import com.taotao.web.bean.User;
import com.taotao.web.bean.UserThreadLocal;
import com.taotao.web.service.UserService;
/**
 * 訂單的相關操作都要求使用者必須登入 ,所以前置攔截判斷
 * 攔截器邏輯:
 *  1.獲取cookie中的token
 *  2.如果token為空,說明未登入,攔截,重定向到登入頁
 *  3.如果token不為空,呼叫SSO系統介面獲取使用者資訊
 *  4.如果User為null,說明未登入,攔截,重定向到登入頁
 *  5.user不為null,說明登入有效,放行
 * @author Henry
 *
 */
public class UserLoginInterceptor implements HandlerInterceptor{

	@Autowired
	private UserService userService;
	private final String COOKIE_NAME = "TT_TOKEN";
	@Override
	public boolean preHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler) throws Exception {
		// 前置方法 負責攔截  return true(通過,不攔截),return false(攔截,請求結束)
		String token = CookieUtils.getCookieValue(request, COOKIE_NAME);
		if(StringUtils.isEmpty(token)){
			response.sendRedirect("http://sso.taotao.com/user/page/login.html");
			return false;
		}
		User user = this.userService.queryUserByToken(token);
		if(user == null){
			response.sendRedirect("http://sso.taotao.com/user/page/login.html");
			return false;
		}
		
		//此處User物件已經獲取到 
		UserThreadLocal.set(user);
		return true;
	}

	@Override
	public void postHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		// 後置方法
		
	}

	@Override
	public void afterCompletion(HttpServletRequest request,
			HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		// 完成方法 擦除本地執行緒中的User物件
		UserThreadLocal.set(null);
	}

}