當我們的網站采用分布式部署系統時,每個子系統擁有自己獨立的session,如果不實現session共享,當用戶切換系統訪問的時候,會不停的提示登錄,這對於用戶體驗是非常不好的。因此對於多個子系統的的訪問,為了達到用戶登錄一次即可以訪問其他各個子系統,我們采用了sso單點登錄系統。之前文章介紹了單點登錄系統的實現功能1,現在我們來看下當訪問子系統時如何攔截用戶,當用戶的session過期了,如何提示用戶登錄,這里采用了SpringMVC的攔截器的實現。
(1)當登錄頁面時,用戶登錄成功后,在頁面的首頁可以顯示:某某,歡迎您訪問此網站。這時候我們是將之前生成的用戶的token值寫入Cookie中,因為只要瀏覽器沒關,cookie就存在,當不設置cookie的過期時間時,瀏覽器關閉,cookie就消逝。將token的內容寫入cookie,然后我們利用jsonp訪問sso系統的利用token讀取用戶信息。然后顯示在頁面即可。
另外這里還用到一個技術:就是分布式系統的cookie的共享問題。這個將cookie的屬性domain設置成全局共享即可。即不同的子系統的域名是不同的,sso.taotao.com;search.taotao.com;我們需要將其域名設置為**.taotao.com,這樣可以實現cookie的共享。
具體實現如下:
if (null != request) {// 設置域名的cookie String domainName = getDomainName(request); System.out.println(domainName); if (!"localhost".equals(domainName)) { cookie.setDomain(domainName); } } cookie.setPath("/"); response.addCookie(cookie); } catch (Exception e) { e.printStackTrace(); } } /** * 得到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; }
(2)當用戶訪問訂單系統的時候,如果用戶沒有登錄,這時候需要攔截用戶提示用戶登錄此系統。這時候用了攔截器的作用。
package com.taotao.portal.interceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; 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.pojo.TbUser; import com.taotao.portal.service.impl.UserServiceImpl; public class LoginInterceptor implements HandlerInterceptor { @Autowired private UserServiceImpl userService; //執行之前攔截 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { /* handler執行之前攔截,根據true或者false來判斷是否攔截。 瀏覽器沒關,這個cookie就存在,也就說token在里面,至於token的session是否有效,這個需要再判斷。 當用戶登錄子系統時,需要提示用戶進行登錄,這是好用到攔截器,攔截這個頁面,原理的具體實現是這樣的 第一步:當用戶訪問頁面時,判斷cookie中是否有該用戶的token。即先從cookie中獲取token對象。 * 第二步:根據token查詢用戶是否存在,調用sso的從token中查詢用戶的接口來查詢用戶(這個過程是先從redis中檢查token是否過期,如果過期則沒有用戶,需要登錄,如果沒有過期,則表示存在用戶,因為之前用戶信息寫入了redis) *通過查詢用戶,如果用戶存在,則放行,如果不存在,則進行攔截,則跳轉到sso的登錄系統,提示登錄 */ String token = CookieUtils.getCookieValue(request,"TT_TOKEN"); //根據token來獲取用戶的信息,調用sso接口 TbUser user= userService.getBySSO(token); //判斷用戶是否存在 if (user==null) { //如果用戶為空,則攔截到登錄界面,頁面重定向到的登錄頁面 response.sendRedirect(userService.SSO_BASE_URL+userService.SSO_PAGE_LOGIN+"?redirect="+request.getRequestURL()); //response.sendRedirect(userService.SSO_BASE_URL + userService.SSO_PAGE_LOGIN + "?redirect=" + request.getRequestURL()); return false; } return true; } //之后攔截 @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // handler執行之后攔截 } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // TODO Auto-generated method stub } }
調用sso根據token獲取用戶信息
package com.taotao.portal.service.impl; import org.springframework.beans.factory.annotation.Value; import com.taotao.common.utils.HttpClientUtil; import com.taotao.common.utils.JsonUtils; import com.taotao.common.utils.TaotaoResult; import com.taotao.pojo.TbUser; import com.taotao.portal.service.UserService; //通過token來獲取用戶信息,需要調用sso的接口信息 public class UserServiceImpl implements UserService { @Value("${SSO_BASE_URL}") public String SSO_BASE_URL; @Value("${SSO_TOKEN_URL}") public String SSO_TOKEN_URL; @Value("${SSO_PAGE_LOGIN}") public String SSO_PAGE_LOGIN; //這是根據token獲取用戶信息 @Override public TbUser getBySSO(String token) {
//不同子系統之間的訪問,跨域訪問,利用httpclient來實現 String json = HttpClientUtil.doGet(SSO_BASE_URL+SSO_TOKEN_URL+token); TaotaoResult result=TaotaoResult.formatToPojo(json, TbUser.class); if (result!=null) { TbUser user=(TbUser) result.getData(); return user; } return null; } }
最后不要忘了重要的一步:在springMVC中配置攔截器。這個配置就關系到到了哪個功能模塊,使用攔截器,比如我們提交訂單的時候,如果用戶沒有登錄,則此處增加攔截器。因為攔截器是在我們商城的大的一個客戶端,淘淘商城中相當於taotao-portal中,當我們寫好了訂單服務的接口時,url是order/***,用httpclientUtil來調用訂單服務的接口,此時我們在springMVC.xml配置文件中,設置攔截order,如果提交訂單時如果沒有登錄就會跳轉到登錄窗口。
只需要將這里的
在springMVC中配置攔截器: