前端關於單點登陸SSO的知識

轉自前端關於單點登陸的知識

什麼是單點登陸

單點登陸(Single Sign On),簡稱爲 SSO,是目前比較流行的企業業務整合的解決方案之一。SSO的定義是在多個應用系統中,用戶只須要登陸一次就能夠訪問全部相互信任的應用系統。javascript

SSO通常都須要一個獨立的認證中心(passport),子系統的登陸均得經過passport,子系統自己將不參與登陸操做,當一個系統成功登陸之後,passport將會頒發一個令牌給各個子系統,子系統能夠拿着令牌會獲取各自的受保護資源,爲了減小頻繁認證,各個子系統在被passport受權之後,會創建一個局部會話,在必定時間內能夠無需再次向passport發起認證。css

舉個例子,好比淘寶、天貓都屬於阿里旗下的產品,當用戶登陸淘寶後,再打開天貓,系統便自動幫用戶登陸了天貓,這種現象背後就是用單點登陸實現的。html

單點登陸流程

1.登陸

  • 用戶訪問系統1的受保護資源,系統1發現用戶未登陸,跳轉至sso認證中心,並將本身的地址做爲參數
  • sso認證中心發現用戶未登陸,將用戶引導至登陸頁面
  • 用戶輸入用戶名密碼提交登陸申請
  • sso認證中心校驗用戶信息,建立用戶與sso認證中心之間的會話,稱爲全局會話,同時建立受權令牌
  • sso認證中心帶着令牌跳轉會最初的請求地址(系統1)
  • 系統1拿到令牌,去sso認證中心校驗令牌是否有效
  • sso認證中心校驗令牌,返回有效,註冊系統1
  • 系統1使用該令牌建立與用戶的會話,稱爲局部會話,返回受保護資源
  • 用戶訪問系統2的受保護資源
  • 系統2發現用戶未登陸,跳轉至sso認證中心,並將本身的地址做爲參數
  • sso認證中心發現用戶已登陸,跳轉回系統2的地址,並附上令牌
  • 系統2拿到令牌,去sso認證中心校驗令牌是否有效
  • sso認證中心校驗令牌,返回有效,註冊系統2
  • 系統2使用該令牌建立與用戶的局部會話,返回受保護資源

用戶登陸成功以後,會與sso認證中心及各個子系統創建會話,用戶與sso認證中心創建的會話稱爲全局會話,用戶與各個子系統創建的會話稱爲局部會話,局部會話創建以後,用戶訪問子系統受保護資源將再也不經過sso認證中心,全局會話與局部會話有以下約束關係前端

  • 局部會話存在,全局會話必定存在
  • 全局會話存在,局部會話不必定存在
  • 全局會話銷燬,局部會話必須銷燬
2.註銷

sso認證中心一直監聽全局會話的狀態,一旦全局會話銷燬,監聽器將通知全部註冊系統執行註銷操做。

  • 用戶向系統1發起註銷請求
  • 系統1根據用戶與系統1創建的會話id拿到令牌,向sso認證中心發起註銷請求
  • sso認證中心校驗令牌有效,銷燬全局會話,同時取出全部用此令牌註冊的系統地址
  • sso認證中心向全部註冊系統發起註銷請求
  • 各註冊系統接收sso認證中心的註銷請求,銷燬局部會話
  • sso認證中心引導用戶至登陸頁面

什麼是CAS

CAS是Central Authentication Service的縮寫,中央認證服務,一種獨立開放指令協議。CAS 是 Yale 大學發起的一個開源項目,旨在爲 Web 應用系統提供一種可靠的單點登陸方法。CAS 包含兩個部分: CAS Server 和 CAS Client。CAS Server 須要獨立部署,主要負責對用戶的認證工做;CAS Client 負責處理對客戶端受保護資源的訪問請求,須要登陸時,重定向到 CAS Server。java

CAS 最基本的協議過程:算法

CAS Client 與受保護的客戶端應用部署在一塊兒,以 Filter 方式保護受保護的資源。對於訪問受保護資源的每一個 Web 請求,CAS Client 會分析該請求的 Http 請求中是否包含 Service Ticket,若是沒有,則說明當前用戶還沒有登陸,因而將請求重定向到指定好的 CAS Server 登陸地址,並傳遞 Service (也就是要訪問的目的資源地址),以便登陸成功事後轉回該地址。用戶在第 3 步中輸入認證信息,若是登陸成功,CAS Server 隨機產生一個至關長度、惟1、不可僞造的 Service Ticket,並緩存以待未來驗證,以後系統自動重定向到 Service 所在地址,併爲客戶端瀏覽器設置一個 Ticket Granted Cookie(TGC),CAS Client 在拿到 Service 和新產生的 Ticket 事後,在第 5,6 步中與 CAS Server 進行身份覈實,以確保 Service Ticket 的合法性。 在該協議中,全部與 CAS 的交互均採用 SSL 協議,確保,ST 和 TGC 的安全性。協議工做過程當中會有 2 次重定向的過程,可是 CAS Client 與 CAS Server 之間進行 Ticket 驗證的過程對於用戶是透明的。 另外,CAS 協議中還提供了 Proxy (代理)模式,以適應更加高級、複雜的應用場景,具體介紹能夠參考 CAS 官方網站上的相關文檔。

什麼是OAuth2

OAuth(開放受權)是一個開放標準,容許用戶讓第三方應用訪問該用戶在某一網站上存儲的私密的資源(如照片,視頻,聯繫人列表),而無需將用戶名和密碼提供給第三方應用。json

通俗說,OAuth就是一種受權的協議,只要受權方和被受權方遵照這個協議去寫代碼提供服務,那雙方就是實現了OAuth模式。跨域

詳細說就是,OAuth在"客戶端"與"服務提供商"之間,設置了一個受權層(authorization layer)。"客戶端"不能直接登陸"服務提供商",只能登陸受權層,以此將用戶與客戶端區分開來。"客戶端"登陸受權層所用的令牌(token),與用戶的密碼不一樣。用戶能夠在登陸的時候,指定受權層令牌的權限範圍和有效期。"客戶端"登陸受權層之後,"服務提供商"根據令牌的權限範圍和有效期,向"客戶端"開放用戶儲存的資料。瀏覽器

OAuth2是OAuth1.0的下一個版本,OAuth2關注客戶端開發者的簡易性,同時爲Web應用,桌面應用和手機,和起居室設備提供專門的認證流程。原先的OAuth,會發行一個 有效期很是長的token(典型的是一年有效期或者無有效期限制),在OAuth 2.0中,server將發行一個短有效期的access token和長生命期的refresh token。這將容許客戶端無需用戶再次操做而獲取一個新的access token,而且也限制了access token的有效期。緩存

CAS和OAuth2區別

  • CAS的單點登陸時保障客戶端的用戶資源的安全,OAuth2則是保障服務端的用戶資源的安全;
  • CAS客戶端要獲取的最終信息是,這個用戶到底有沒有權限訪問我(CAS客戶端)的資源;oauth2獲取的最終信息是,我(oauth2服務提供方)的用戶的資源到底能不能讓你(oauth2的客戶端)訪問;
  • CAS的單點登陸,資源都在客戶端這邊,不在CAS的服務器那一方。用戶在給CAS服務端提供了用戶名密碼後,做爲CAS客戶端並不知道這件事。隨便給客戶端個ST,那麼客戶端是不能肯定這個ST是用戶僞造仍是真的有效,因此要拿着這個ST去服務端再問一下,這個用戶給個人是有效的ST仍是無效的ST,是有效的我才能讓這個用戶訪問。
  • OAuth2認證,資源都在OAuth2服務提供者那一方,客戶端是想索取用戶的資源。因此在最安全的模式下,用戶受權以後,服務端並不能直接返回token,經過重定向送給客戶端,由於這個token有可能被黑客截獲,若是黑客截獲了這個token,那用戶的資源也就暴露在這個黑客之下了。因而聰明的服務端發送了一個認證code給客戶端(經過重定向),客戶端在後臺,經過https的方式,用這個code,以及另外一串客戶端和服務端預先商量好的密碼,才能獲取到token和刷新token,這個過程是很是安全的。若是黑客截獲了code,他沒有那串預先商量好的密碼,他也是沒法獲取token的。這樣oauth2就能保證請求資源這件事,是用戶贊成的,客戶端也是被承認的,能夠放心的把資源發給這個客戶端了。
  • CAS登陸和OAuth2在流程上的最大區別就是,經過ST或者code去認證的時候,需不須要預先商量好的密碼。
總結:
CAS:受權服務器,被受權客戶端
  1. 受權服務器(一個)保存了全局的一份session,客戶端(多個)各自保存本身的session;
  2. 客戶端登陸時判斷本身的session是否已登陸,若未登陸,則(告訴瀏覽器)重定向到受權服務器(參數帶上本身的地址,用於回調);
  3. 受權服務器判斷全局的session是否已登陸,若未登陸則定向到登陸頁面,提示用戶登陸,登陸成功後,受權服務器重定向到客戶端(參數帶上ticket【一個憑證號】);
  4. 客戶端收到ticket後,請求服務器獲取用戶信息;
  5. 服務器贊成客戶端受權後,服務端保存用戶信息至全局session,客戶端將用戶保存至本地session
OAuth2:主系統,受權系統(給主系統受權用的,也能夠跟主系統是同一個系統),第三方系統
  1. 第三方系統須要使用主系統的資源,第三方重定向到受權系統;
  2. 根據不一樣的受權方式,受權系統提示用戶受權;
  3. 用戶受權後,受權系統返回一個受權憑證(accessToken)給第三方系統【accessToken是有有效期的】;
  4. 第三方使用accessToken訪問主系統資源【accessToken失效後,第三方需從新請求受權系統,以獲取新的accessToken】。

什麼是JWT

JSON Web Token(JWT)是一個開放標準(RFC 7519),它定義了一種緊湊且獨立的方式,能夠在各方之間做爲JSON對象安全地傳輸信息。此信息能夠經過數字簽名進行驗證和信任。JWT可使用祕密(使用HMAC算法)或使用RSA或ECDSA的公鑰/私鑰對進行簽名。

JSON WEB令牌結構由三部分組成:

  • Header(頭部):包括令牌的類型及正在使用的散列算法。
  • Payload(負載):聲明是關於實體(一般是用戶)和其餘數據的聲明。索賠有三種類型:標準註冊聲明,公共的聲明和私有的聲明。
  • Signature(簽名):必須採用編碼標頭,編碼的有效負載,祕密,標頭中指定的算法,並對其進行簽名。
  1. 負載-標準的聲明:
  • iss:JWT簽發者
  • sub:JWT所面向的用戶
  • aud:接收JWT的一方
  • exp:JWT的過時時間,這個過時時間必需要大於簽發時間,這是一個秒數
  • nbf:定義在什麼時間以前,該JWT都是不可用的
  • iat:JWT的簽發時間
  1. 負載-公共的聲明:能夠添加任何信息,通常添加用戶的相關信息或其餘業務須要的必要信息,但不建議添加敏感信息,由於該部分在客戶端可解密。
  2. 負載-私有聲明:提供者和消費者所共同定義的聲明,通常不建議存放敏感信息,由於base64是對稱解密的,意味着該部分信息能夠歸類爲明文信息。

建立簽名須要使用編碼後的headerpayload以及一個祕鑰,使用header中指定簽名算法進行簽名。例如若是但願使用HMAC SHA256算法,那麼簽名應該使用下列方式建立HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload), secret)簽名用於驗證消息的發送者以及消息是沒有通過篡改的。完整的JWT格式輸出是以.分隔的三段Base64編碼, 密鑰secret是保存在服務端的,服務端會根據這個密鑰進行生成token和驗證,因此須要保護好,更多信息請移步官網

單點登陸關於前端的部分

此代碼採用OAuth2。關於token存儲問題,參考了網上許多教程,大部分都是將token存儲在cookie中,而後將cookie設爲頂級域來解決跨域問題,但我司業務需求是某些產品頂級域也各不相同。故實現思路是將token存儲在localStorage中,而後經過H5的新屬性postMessage來實現跨域共享,對跨域不瞭解的能夠看我這篇文章

實現思路:當用戶訪問公司某系統(如product.html)時,在product中會首先加載一個iframe,iframe中能夠獲取存儲在localStorage中的token,若是沒有取到或token過時,iframe中內部將把用戶將重定向到登陸頁,用戶在此頁面登陸,仍將去認證系統取得token並保存在iframe頁面的localStorage

<!--product.html-->
<head>
    <script src="auth_1.0.0.js"></script>
</head>
<body>
    <h2>產品頁面</h2>
    <a onClick="login()" id="login">登陸</a>
    <h3 id="txt"></h3>
</body>
<script> var opts = { origin: 'http://localhost:8080', login_path: '/login.html', path: '/cross_domain.html' } // 加載iframe,將src值爲cross_domain.html的iframe加載到本頁 var auth = new ssoAuth(opts); function getTokenCallback(data) { //若是沒有token則跳到登陸頁 if(!data.value){ auth.doWebLogin(); } //若是有token,直接在頁面顯示,而後作其它操做 document.getElementById('txt').innerText = 'token=' + data.value; } // 獲取存儲在名爲cross_domain的iframe中的token auth.getToken(getTokenCallback); </script>
複製代碼複製代碼

講解:在product.html中實例化了ssoAuth後,此頁面便將iframe引入了當前頁,名爲opts.path的值,即cross_domain.html。auth.getToken()是獲取此iframe頁面中的localStorage值。

//auth_1.0.0.js function ssoAuth(opts) { this._origin = opts.origin, this._iframe_path = opts.path, this._iframe = null, this._iframe_ready = false, this._queue = [], this._auth = {}, this._access_token_msg = { type: "get", key: "access_token" }, this._callback = undefined, that = this;
<span class="hljs-comment">//判斷是否支持postMessage及localStorage</span>
複製代碼

var supported = (function () { try { return window.postMessage && window.JSON && 'localStorage' in window && window['localStorage'] !== null; } catch (e) { return false; } })();

_iframeLoaded = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    that._iframe_ready = <span class="hljs-literal">true</span>
    <span class="hljs-keyword">if</span> (that._queue.length) {
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>, len = that._queue.length; i &lt; len; i++) {
            _sendMessage(that._queue[i]);
        }
        that._queue = [];
    }
}

_sendMessage = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">data</span>) </span>{
    <span class="hljs-comment">// 經過contentWindow屬性,腳本能夠訪問iframe元素所包含的HTML頁面的window對象。</span>
    that._iframe.contentWindow.postMessage(<span class="hljs-built_in">JSON</span>.stringify(data), that._origin);
}

<span class="hljs-comment">//獲取token,但由於此時iframe尚未加載完成,先將消息存儲在隊列_queue中</span>
<span class="hljs-keyword">this</span>._auth.getToken = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">callback</span>) </span>{
    that._callback = callback
    <span class="hljs-keyword">if</span> (that._access_token_msg &amp;&amp; that._iframe_ready) {
        <span class="hljs-comment">//當iframe加載完成,給iframe所在的頁面發送消息</span>
        _sendMessage(that._access_token_msg);
    } <span class="hljs-keyword">else</span> {
        that._queue.push(that._access_token_msg);
    }
}

<span class="hljs-keyword">var</span> _handleMessage = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">event</span>) </span>{
    <span class="hljs-keyword">if</span> (event.origin === that._origin) {
        <span class="hljs-keyword">var</span> data = <span class="hljs-built_in">JSON</span>.parse(event.data);
        <span class="hljs-keyword">if</span> (data.error) {
            <span class="hljs-built_in">console</span>.error(event.data)
            that._callback({ <span class="hljs-attr">value</span>: <span class="hljs-literal">null</span> });
            <span class="hljs-keyword">return</span>;
        }
        <span class="hljs-keyword">if</span> (that._callback &amp;&amp; <span class="hljs-keyword">typeof</span> that._callback === <span class="hljs-string">'function'</span>) {
            that._callback(data);
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"callback is null or not a function, please "</span>);
        }
    }
}

<span class="hljs-keyword">this</span>._auth.doWebLogin = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-built_in">window</span>.location.href = opts.origin + opts.login_path + <span class="hljs-string">"?redirect_url="</span> + <span class="hljs-built_in">window</span>.location.href
}
<span class="hljs-comment">//初始化了一個iframe,並追加到父頁面的底部</span>
<span class="hljs-keyword">if</span> (!<span class="hljs-keyword">this</span>._iframe &amp;&amp; supported) {
    <span class="hljs-keyword">this</span>._iframe = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"iframe"</span>);
    <span class="hljs-keyword">this</span>._iframe.style.cssText = <span class="hljs-string">"position:absolute;width:1px;height:1px;left:-9999px;"</span>;
    <span class="hljs-built_in">document</span>.body.appendChild(<span class="hljs-keyword">this</span>._iframe);

    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">window</span>.addEventListener) {
        <span class="hljs-keyword">this</span>._iframe.addEventListener(<span class="hljs-string">"load"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
            _iframeLoaded();
        }, <span class="hljs-literal">false</span>);
        <span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">"message"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">event</span>) </span>{
            _handleMessage(event)
        }, <span class="hljs-literal">false</span>);
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>._iframe.attachEvent) {
        <span class="hljs-keyword">this</span>._iframe.attachEvent(<span class="hljs-string">"onload"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
            _iframeLoaded();
        }, <span class="hljs-literal">false</span>);
        <span class="hljs-built_in">window</span>.attachEvent(<span class="hljs-string">"onmessage"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">event</span>) </span>{
            _handleMessage(event)
        });
    }
    <span class="hljs-keyword">this</span>._iframe.src = <span class="hljs-keyword">this</span>._origin + <span class="hljs-keyword">this</span>._iframe_path;
}
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>._auth;
複製代碼
複製代碼<span class="hljs-comment">//判斷是否支持postMessage及localStorage</span> 複製代碼_iframeLoaded = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{ that._iframe_ready = <span class="hljs-literal">true</span> <span class="hljs-keyword">if</span> (that._queue.length) { <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>, len = that._queue.length; i &lt; len; i++) { _sendMessage(that._queue[i]); } that._queue = []; } } _sendMessage = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">data</span>) </span>{ <span class="hljs-comment">// 經過contentWindow屬性,腳本能夠訪問iframe元素所包含的HTML頁面的window對象。</span> that._iframe.contentWindow.postMessage(<span class="hljs-built_in">JSON</span>.stringify(data), that._origin); } <span class="hljs-comment">//獲取token,但由於此時iframe尚未加載完成,先將消息存儲在隊列_queue中</span> <span class="hljs-keyword">this</span>._auth.getToken = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">callback</span>) </span>{ that._callback = callback <span class="hljs-keyword">if</span> (that._access_token_msg &amp;&amp; that._iframe_ready) { <span class="hljs-comment">//當iframe加載完成,給iframe所在的頁面發送消息</span> _sendMessage(that._access_token_msg); } <span class="hljs-keyword">else</span> { that._queue.push(that._access_token_msg); } } <span class="hljs-keyword">var</span> _handleMessage = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">event</span>) </span>{ <span class="hljs-keyword">if</span> (event.origin === that._origin) { <span class="hljs-keyword">var</span> data = <span class="hljs-built_in">JSON</span>.parse(event.data); <span class="hljs-keyword">if</span> (data.error) { <span class="hljs-built_in">console</span>.error(event.data) that._callback({ <span class="hljs-attr">value</span>: <span class="hljs-literal">null</span> }); <span class="hljs-keyword">return</span>; } <span class="hljs-keyword">if</span> (that._callback &amp;&amp; <span class="hljs-keyword">typeof</span> that._callback === <span class="hljs-string">'function'</span>) { that._callback(data); } <span class="hljs-keyword">else</span> { <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"callback is null or not a function, please "</span>); } } } <span class="hljs-keyword">this</span>._auth.doWebLogin = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{ <span class="hljs-built_in">window</span>.location.href = opts.origin + opts.login_path + <span class="hljs-string">"?redirect_url="</span> + <span class="hljs-built_in">window</span>.location.href } <span class="hljs-comment">//初始化了一個iframe,並追加到父頁面的底部</span> <span class="hljs-keyword">if</span> (!<span class="hljs-keyword">this</span>._iframe &amp;&amp; supported) { <span class="hljs-keyword">this</span>._iframe = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"iframe"</span>); <span class="hljs-keyword">this</span>._iframe.style.cssText = <span class="hljs-string">"position:absolute;width:1px;height:1px;left:-9999px;"</span>; <span class="hljs-built_in">document</span>.body.appendChild(<span class="hljs-keyword">this</span>._iframe); <span class="hljs-keyword">if</span> (<span class="hljs-built_in">window</span>.addEventListener) { <span class="hljs-keyword">this</span>._iframe.addEventListener(<span class="hljs-string">"load"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{ _iframeLoaded(); }, <span class="hljs-literal">false</span>); <span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">"message"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">event</span>) </span>{ _handleMessage(event) }, <span class="hljs-literal">false</span>); } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>._iframe.attachEvent) { <span class="hljs-keyword">this</span>._iframe.attachEvent(<span class="hljs-string">"onload"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{ _iframeLoaded(); }, <span class="hljs-literal">false</span>); <span class="hljs-built_in">window</span>.attachEvent(<span class="hljs-string">"onmessage"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">event</span>) </span>{ _handleMessage(event) }); } <span class="hljs-keyword">this</span>._iframe.src = <span class="hljs-keyword">this</span>._origin + <span class="hljs-keyword">this</span>._iframe_path; } <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>._auth; 複製代碼} 複製代碼複製代碼

<!--cross_domain.html--> <script type="text/javascript"> (function () {

    <span class="hljs-comment">//白名單</span>
    <span class="hljs-keyword">var</span> whitelist = [<span class="hljs-string">"localhost"</span>, <span class="hljs-string">"127.0.0.1"</span>, <span class="hljs-string">"^.*\.domain\.com"</span>];

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">verifyOrigin</span>(<span class="hljs-params">origin</span>) </span>{
        <span class="hljs-keyword">var</span> domain = origin.replace(<span class="hljs-regexp">/^https?:\/\/|:\d{1,4}$/g</span>, <span class="hljs-string">""</span>).toLowerCase(),
            i = <span class="hljs-number">0</span>,
            len = whitelist.length;

        <span class="hljs-keyword">while</span> (i &lt; len) {
            <span class="hljs-keyword">if</span> (domain.match(<span class="hljs-keyword">new</span> <span class="hljs-built_in">RegExp</span>(whitelist[i]))) {
                <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
            }
            i++;
        }
        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleRequest</span>(<span class="hljs-params">event</span>) </span>{
        <span class="hljs-comment">// 白名單較驗</span>
        <span class="hljs-keyword">if</span> (verifyOrigin(event.origin)) {
            <span class="hljs-keyword">var</span> request = <span class="hljs-built_in">JSON</span>.parse(event.data);
            <span class="hljs-keyword">if</span> (request.type == <span class="hljs-string">'get'</span>) {
                <span class="hljs-keyword">var</span> idi = sessionStorage.getItem(<span class="hljs-string">"idi"</span>);
                <span class="hljs-keyword">if</span> (!idi) {
                    <span class="hljs-comment">// source:對發送消息的窗口對象的引用</span>
                    event.source.postMessage(<span class="hljs-built_in">JSON</span>.stringify({ <span class="hljs-attr">key</span>: request.key, <span class="hljs-attr">value</span>: <span class="hljs-literal">null</span> }), event.origin);
                    <span class="hljs-keyword">return</span>;
                }
                value = <span class="hljs-built_in">JSON</span>.parse(idi)[request.key];
                event.source.postMessage(<span class="hljs-built_in">JSON</span>.stringify({ <span class="hljs-attr">key</span>: request.key, <span class="hljs-attr">value</span>: value }), event.origin);
            } <span class="hljs-keyword">else</span> {
                event.source.postMessage(<span class="hljs-built_in">JSON</span>.stringify({ <span class="hljs-attr">error</span>: <span class="hljs-string">"Not supported"</span>, <span class="hljs-attr">error_description</span>: <span class="hljs-string">"Not supported message type"</span> }), event.origin);
            }
        }
    }
    <span class="hljs-comment">// 接收iframe傳來的消息</span>
    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">window</span>.addEventListener) {
        <span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">"message"</span>, handleRequest, <span class="hljs-literal">false</span>);
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-built_in">window</span>.attachEvent) {
        <span class="hljs-built_in">window</span>.attachEvent(<span class="hljs-string">"onmessage"</span>, handleRequest);
    }
})();
複製代碼
複製代碼<span class="hljs-comment">//白名單</span> <span class="hljs-keyword">var</span> whitelist = [<span class="hljs-string">"localhost"</span>, <span class="hljs-string">"127.0.0.1"</span>, <span class="hljs-string">"^.*\.domain\.com"</span>]; <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">verifyOrigin</span>(<span class="hljs-params">origin</span>) </span>{ <span class="hljs-keyword">var</span> domain = origin.replace(<span class="hljs-regexp">/^https?:\/\/|:\d{1,4}$/g</span>, <span class="hljs-string">""</span>).toLowerCase(), i = <span class="hljs-number">0</span>, len = whitelist.length; <span class="hljs-keyword">while</span> (i &lt; len) { <span class="hljs-keyword">if</span> (domain.match(<span class="hljs-keyword">new</span> <span class="hljs-built_in">RegExp</span>(whitelist[i]))) { <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>; } i++; } <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>; } <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleRequest</span>(<span class="hljs-params">event</span>) </span>{ <span class="hljs-comment">// 白名單較驗</span> <span class="hljs-keyword">if</span> (verifyOrigin(event.origin)) { <span class="hljs-keyword">var</span> request = <span class="hljs-built_in">JSON</span>.parse(event.data); <span class="hljs-keyword">if</span> (request.type == <span class="hljs-string">'get'</span>) { <span class="hljs-keyword">var</span> idi = sessionStorage.getItem(<span class="hljs-string">"idi"</span>); <span class="hljs-keyword">if</span> (!idi) { <span class="hljs-comment">// source:對發送消息的窗口對象的引用</span> event.source.postMessage(<span class="hljs-built_in">JSON</span>.stringify({ <span class="hljs-attr">key</span>: request.key, <span class="hljs-attr">value</span>: <span class="hljs-literal">null</span> }), event.origin); <span class="hljs-keyword">return</span>; } value = <span class="hljs-built_in">JSON</span>.parse(idi)[request.key]; event.source.postMessage(<span class="hljs-built_in">JSON</span>.stringify({ <span class="hljs-attr">key</span>: request.key, <span class="hljs-attr">value</span>: value }), event.origin); } <span class="hljs-keyword">else</span> { event.source.postMessage(<span class="hljs-built_in">JSON</span>.stringify({ <span class="hljs-attr">error</span>: <span class="hljs-string">"Not supported"</span>, <span class="hljs-attr">error_description</span>: <span class="hljs-string">"Not supported message type"</span> }), event.origin); } } } <span class="hljs-comment">// 接收iframe傳來的消息</span> <span class="hljs-keyword">if</span> (<span class="hljs-built_in">window</span>.addEventListener) { <span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">"message"</span>, handleRequest, <span class="hljs-literal">false</span>); } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-built_in">window</span>.attachEvent) { <span class="hljs-built_in">window</span>.attachEvent(<span class="hljs-string">"onmessage"</span>, handleRequest); } })(); 複製代碼</script> 複製代碼複製代碼

<!--login.html--> <head> <script src="auth_1.0.0.js"></script> </head> <body> <form> <input type="text" placeholder="用戶名" id="user"> <input type="password" placeholder="密碼" id="pwd"> </form> <button onClick="login()">登 錄</button> </body> <script> function login() { var name = document.getElementById('user') var pwd = document.getElementById('pwd')

    <span class="hljs-keyword">var</span> expires_in = <span class="hljs-number">7200</span>
    <span class="hljs-comment">//假如這是登陸成功後,後臺開發人員返回的json數據</span>
    <span class="hljs-keyword">var</span> res = { 
        <span class="hljs-attr">access_token</span>: <span class="hljs-string">"xxxxx.yyyyy.zzzzz"</span>, 
        <span class="hljs-attr">expires_at</span>: expires_in * <span class="hljs-number">1000</span> + <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().getTime(), 
        <span class="hljs-attr">refresh_token</span>: <span class="hljs-string">"yyyyyyyyyyyyyyyyyyyyyyyyyyyy"</span> 
    };
    localStorage.setItem(<span class="hljs-string">"idi"</span>, <span class="hljs-built_in">JSON</span>.stringify(res))
    <span class="hljs-comment">//登陸成功後再返回原頁面</span>
    <span class="hljs-built_in">window</span>.location.href = getQueryString(<span class="hljs-string">"redirect_url"</span>)
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getQueryString</span>(<span class="hljs-params">name</span>) </span>{
    <span class="hljs-keyword">var</span> reg = <span class="hljs-keyword">new</span> <span class="hljs-built_in">RegExp</span>(<span class="hljs-string">"(^|&amp;)"</span> + name + <span class="hljs-string">"=([^&amp;]*)(&amp;|$)"</span>);
    <span class="hljs-keyword">var</span> r = <span class="hljs-built_in">window</span>.location.search.substr(<span class="hljs-number">1</span>).match(reg);
    <span class="hljs-keyword">if</span> (r != <span class="hljs-literal">null</span>) <span class="hljs-keyword">return</span> <span class="hljs-built_in">unescape</span>(r[<span class="hljs-number">2</span>]); <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
}
複製代碼
複製代碼<span class="hljs-keyword">var</span> expires_in = <span class="hljs-number">7200</span> <span class="hljs-comment">//假如這是登陸成功後,後臺開發人員返回的json數據</span> <span class="hljs-keyword">var</span> res = { <span class="hljs-attr">access_token</span>: <span class="hljs-string">"xxxxx.yyyyy.zzzzz"</span>, <span class="hljs-attr">expires_at</span>: expires_in * <span class="hljs-number">1000</span> + <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().getTime(), <span class="hljs-attr">refresh_token</span>: <span class="hljs-string">"yyyyyyyyyyyyyyyyyyyyyyyyyyyy"</span> }; localStorage.setItem(<span class="hljs-string">"idi"</span>, <span class="hljs-built_in">JSON</span>.stringify(res)) <span class="hljs-comment">//登陸成功後再返回原頁面</span> <span class="hljs-built_in">window</span>.location.href = getQueryString(<span class="hljs-string">"redirect_url"</span>) } <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getQueryString</span>(<span class="hljs-params">name</span>) </span>{ <span class="hljs-keyword">var</span> reg = <span class="hljs-keyword">new</span> <span class="hljs-built_in">RegExp</span>(<span class="hljs-string">"(^|&amp;)"</span> + name + <span class="hljs-string">"=([^&amp;]*)(&amp;|$)"</span>); <span class="hljs-keyword">var</span> r = <span class="hljs-built_in">window</span>.location.search.substr(<span class="hljs-number">1</span>).match(reg); <span class="hljs-keyword">if</span> (r != <span class="hljs-literal">null</span>) <span class="hljs-keyword">return</span> <span class="hljs-built_in">unescape</span>(r[<span class="hljs-number">2</span>]); <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>; } 複製代碼</script> 複製代碼複製代碼

PS:註銷暫時沒作。另外postMessage有兼容性問題,若是其它小夥伴有更好的方法,望分享一下,謝謝~

參考:

相關文章
相關標籤/搜索