當我們的網站采用分布式部署系統時,每個子系統擁有自己獨立的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中配置攔截器:

