性能優化(SSO單點登陸)

基本概念

什麼是SSOjava

SSO技術難點git

大中型Web應用基本都是多系統組成的應用羣,SSO是必須面對的基本問題。
Cookie有做用域限制,頂級域名Cookie不能共享,故登陸會話不能共享。
直接改造各子系統共享Session,通用性靈活性不強,或對原系統入侵性大,不是解決SSO根本辦法。github

單Web系統登陸機理web

認證(Authentication)操做,就是證實這個瀏覽器請求用戶是合法系統用戶,通常狀況就是驗證用戶名和密碼。
受權(Authorization),就是根據該用戶在此係統中的權限定義,綁定正確的權限信息,爲用戶後續正確使用系統功能提供安全保障。
創建會話,Session機制或本身基於Cookie開發的相似功能,創建起本次會話。
登陸成功後,服務器需進行登陸狀態判斷,識別操做是不是本次登陸用戶的操做。
登出時,服務端取消會話,本次登陸用戶會話結束。下次請求時,系統即判斷是非登陸用戶。spring

SSO基本實現思路數據庫

單Web應用登陸,主要涉及到認證、受權、會話創建、取消會話等幾個關鍵環節。瀏覽器

關鍵問題安全

1、登陸信息傳遞問題服務器

因爲認證中心繫統與應用系統分開,須要傳遞是否登陸及相關登陸信息,以便後續操做。cookie

2、登陸狀態判斷問題

一樣,當用戶已在其餘地方登陸,訪問該應用時。應用系統須要瞭解登陸狀態。

3、登出問題

認證中心註銷後,全部應用系統同時註銷。

登陸信息傳遞問題

1.用戶瀏覽器訪問系統A需登陸受限資源。
2.系統A發現該請求須要登陸,將請求重定向到認證中心,進行登陸。
3.認證中心呈現登陸頁面,用戶登陸,登陸成功後,認證中心重定向請求到系統A,並附上認證經過令牌。
4.系統A與認證中心通訊,驗證令牌有效,證實用戶已登陸。
5.系統A將受限資源返給用戶。

登陸狀態判斷問題

1. 瀏覽器訪問另外一應用B需登陸受限資源。
2. 系統B發現該請求須要登陸,將請求重定向到認證中心,進行登陸。
3. 認證中心發現已經登陸,即重定向請求響應到系統B,附帶上認證令牌。
4. 系統B與認證中心通訊,驗證令牌有效,證實用戶已登陸。
5. 系統B將受限資源返回給客戶端。

登出問題

1.客戶端嚮應用A發送登出Logout請求。
2.應用A取消本地會話,同時通知認證中心,用戶已登出。
3.應用A返回客戶端登出請求。
4.認證中心通知全部用戶登陸訪問的應用,用戶已登出。

用戶到認證中心登陸後,用戶和認證中心之間創建起了會話,咱們把這個會話稱爲全局會話。當用戶後續訪問系統應用時,咱們不可能每次應用請求都到認證中心去斷定是否登陸,這樣效率很是低下,這也是單Web應用不須要考慮的。
咱們能夠在系統應用和用戶瀏覽器之間創建起局部會話,局部會話保持了客戶端與該系統應用的登陸狀態,局部會話依附於全局會話存在,全局會話消失,局部會話必須消失。
用戶訪問應用時,首先判斷局部會話是否存在,如存在,即認爲是登陸狀態,無需再到認證中心去判斷。如不存在,就重定向到認證中心判斷全局會話是否存在,如存在,按1提到的方式通知該應用,該應用與客戶端就創建起它們之間局部會話,下次請求該應用,就不去認證中心驗證了。

 

核心實現

服務端實現

1.驗證用戶的登陸信息
2.建立全局會話
3.建立受權令牌
4.與sso-client通訊發送令牌
5.校驗sso-client令牌有效性
6.系統註冊
7.接收sso-client註銷請求,註銷全部會話。

四大接口

一、面向用戶的登陸接口

@RequestMapping("/login")
public String login(String username, String password,HttpServletRequest req) {
    this.checkLoginInfo(username, password);
    req.getSession().setAttribute("isLogin", true);
    return "success";
}

String token = UUID.randomUUID().toString();

二、面向應用系統令牌校驗接口

@RequestMapping("/verify")
@ResponseBody
public String verify(String token, HttpServletRequest req) {
    this.verify(token);   
    return 「ok";
}

HttpPost httpPost = new HttpPost("sso-server-verify-url-with-token");
HttpResponse httpResponse = httpClient.execute(httpPost);

if (verifyResult) {
  session.setAttribute("isLogin", true);
}

三、面向應用系統用戶驗證接口

Verify(String token) {
    TokenInfo token = getByToken(token);
    return !token.isExpired();
}

四、面向應用系統註銷接口

@RequestMapping("/logout")
public String logout(HttpServletRequest req) {
    HttpSession session = req.getSession();
    if (session != null) {
        session.invalidate();//觸發LogoutListener
    }
    return "redirect:/";
}

public class LogoutListener implements HttpSessionListener {

    @Override
    public void sessionCreated(HttpSessionEvent event) {
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent event){
        //經過httpClient向全部註冊系統發送註銷請求
    }
}

客戶端實現

1.攔截子系統未登陸用戶請求,跳轉至sso認證中心
2.接收並存儲sso認證中心發送的令牌
3.與sso-server通訊,校驗令牌的有效性
4.創建局部會話
5.攔截用戶註銷請求,向sso認證中心發送註銷請求
6.接收sso認證中心發出的註銷請求,銷燬局部會話

兩個功能

一、用戶驗證過濾器

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    HttpServletRequest req = (HttpServletRequest) request;
    HttpServletResponse res = (HttpServletResponse) response;
    HttpSession session = req.getSession();
   
    if (session.getAttribute("isLogin")) {
        chain.doFilter(request, response);
        return;
    }
    //跳轉至sso認證中心
    res.sendRedirect("sso-server-login-url");
}

二、登出接口

String logout = req.getParameter("logout");
if (logout != null) {
    this.ssoServer.logout(token);
}

 

CAS架設

四種方式

N個系統,可是 一級域名 是一致的。

PS:這個方案比較簡單,只要提供公共的 SDK 便可,不須要第三個系統的出現,這個 SDK 的工做須要管理 Cookie 和用戶信息。
原理:其實質這裏就是利用了 二級域名 寫 一級域名 的Cookie 。
優勢:輕量級、可插拔、效率很是高。
缺點:侷限性限於一級域名是同樣的。

域名比較亂,有同一個一級域名的(www.xxx.com、a.xxx.com),也有不一樣域名的

原理:經過SSO 系統(登陸、退出), Iframe 引用的方式引入Cookie.domain.com的方式,利用 Javascript 操做(寫入 / 刪除 / 修改) cookie ,而這個cookie.domain.com 域名是放入 CDN 上 ,獲取用戶信息當前系統直接經過 Redis (只讀)獲取。
優勢:由於是採用壓力分化,Cookie.domain.com 部署在CDN上,這樣的話,對各個系統形成的壓力是 0 ,用第三方系統(SSO)維護,權限更大,操做性更強,但又Cookie 信息在當前域名的一級域下,獲取簡單,大量減小對 sso 的訪問量。
缺點:若是瀏覽器安全性太高,Iframe 的方式操做 Cookie 將會失敗。好比IE瀏覽器,目前正在攻克IE瀏覽器。

 

域名比較亂,有同一個一級域名的(www.xxx.com、a.xxx.com),也有不一樣域名的。

原理:全部的請求(登陸、退出、獲取用戶信息、當前用戶狀態)都請求sso 系統,sso 系統維護用戶信息,Session ,UserInfo。
優勢:實現較爲簡單。
缺點:SSO 壓力很是大。

 

CAS是中央認證服務Central Authentication Service的簡稱。最初由耶魯大學的Shawn Bayern 開發,後由Jasig社區維護,通過十多年發展,目前已成爲影響最大、普遍使用的、基於Java實現的、開源SSO解決方案。
2012年,Jasig和另外一個有影響的組織Sakai Foundation合併,組成Apereo。Apereo是一個由高等學術教育機構發起組織的聯盟,旨在爲學術教育機構提供高質量軟件,固然不少軟件也被大量應用於商業環境,譬如CAS。目前CAS由Apereo社區維護。

CAS的官方網址是: https://www.apereo.org/projects/cas
工程代碼網址:https://github.com/Jasig/cas
客戶端下載地址: www.ja-sig.org/downloads/cas-clients/

安裝

一、解壓到硬盤如e:\cas-4.1.0,只導入三個重要工程:父工程cas-server、核心包工程cas-server-core、運行web包工程cas-server-webapp。

二、此時pom.xml可能提示有錯誤,按eclipse提示更正信息ignore便可。而後咱們利用maven compile、package cas-server-webapp工程。正常狀況下會生成cas.war 認證中心安裝包。

配置

三、找到WEB-INF\deployerConfigContext.xml,找到primaryAuthenticationHandler段落,修改登陸用戶名爲admin,密碼123123 作登陸體驗用。(CAS缺省使用的是配置文件方式管理用戶名和密碼,實際應用中使用最多的是利用數據庫管理,CAS提供相應接口)

四、找到WEB-INF\spring-configuration\ticketGrantingTicketCookieGenerator.xml文件,將p:cookieSecure的值改成false。CAS缺省要求使用https協議,咱們將此改成false,使其在http協議下也能工做

5 在本機的host文件中配置一個域名,如www.cas.com,瀏覽器輸入該網址,便可出現cas登陸頁

 

客戶端配置

1.咱們在host文件中再配置一個域名,www.ssoclient.com用於表示此應用系統登陸網址。
2.創建一個java web maven工程,端口號設置爲81,將CASClient包配置到pom.xml中。

<dependency>
<groupId>org.jasig.cas.client</groupId>
<artifactId>cas-client-core</artifactId>
<version>3.3.3</version>
</dependency>

首先配置登出listener和filter

<listener>
<listener-
class>org.jasig.cas.client.session.SingleSignOutHttpSessionListene
r</listener-class>
</listener>
<filter>
<filter-name>CAS Single Sign Out Filter</filter-name>
<filter-
class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CAS Single Sign Out Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

接着配置認證filter,即受限資源須要先通過此fiter.注意這裏面要配置認證中心登陸網址,以及標識此係統應用的服務名稱。

<filter>
<filter-name>CAS Authentication Filter</filter-name>
<filter-
class>org.jasig.cas.client.authentication.AuthenticationFilter</filte
r-class>
<init-param>
<param-name>casServerLoginUrl</param-name>
<param-value>http://www.cas.com/login</param-value>
</init-param>
<init-param>
<param-name>serverName</param-name>
<param-value>http://www.ssoclient.com:81</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CAS Authentication Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

接下來配置驗證票據ticket的filter令牌token:

<filter>
<filter-name>CAS Validation Filter</filter-name>
<filter-
class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketVal
idationFilter</filter-class>
<init-param>
<param-name>casServerUrlPrefix</param-name>
<param-value>http://www.cas.com</param-value>
</init-param>
<init-param>
<param-name>serverName</param-name>
<param-value>http://www.ssoclient.com:81</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CAS Validation Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

最後咱們配置一個Filter封裝標準的HttpRequest,使得request.getRemoteUser()和request.getUserPrincipal()這兩個方法可用。

<filter>
<filter-name>CAS HttpServletRequest Wrapper Filter</filter-
name>
<filter-
class>org.jasig.cas.client.util.HttpServletRequestWrapperFilter</f
ilter-class>
</filter>
<filter-mapping>
<filter-name>CAS HttpServletRequest Wrapper Filter</filter-
name>
<url-pattern>/*</url-pattern>
</filter-mapping>

在這樣配置下,整個應用系統都是受限資源,咱們定義一個一句話的首頁JSP:

${user},  你好啊! <a href="http://www.cas.com/logout"> 登出
</a>

對應的controller也很簡單:

@RequestMapping("/index.do")
public String showIndex(HttpServletRequest request,
HttpServletResponse response) {
Principal principal = request.getUserPrincipal();
request.getSession().setAttribute("user", principal.getName());
return "/index";

 

應用場景

異構環境(PC)

多個認證中心的處理

應用系統包含JAVA,PHP等多語言,異構系統的認證

應用系統移動端的處理

多端點登陸

相關文章
相關標籤/搜索