sso 登錄,網頁跳轉的實現方式
在昨天的博客中http://www.cnblogs.com/jake1/archive/2013/04/28/sso_scheme.html,我們介紹了幾種單點登錄的方案.不過那些方案我都沒有實現過,只是在自己的一些客戶公司里見過,下面我就要親自介紹,我親身設計和參與的sso方案.
首先,我們看看設計圖吧.
注:1)紅色部分為加密傳送的內容;
2)綠色部分為門戶系統實現的流程;
3)灰色部分為外部應用系統原有已經實現的流程;
4)藍色部分為外部應用系統需要增加或修改的流程;
針對上面的圖:流程看如下解釋.
- 場景一.用戶從門戶單點登錄進入外部應用系統(參看流程圖Step1、Step2)
Step1
1) 用戶在瀏覽器輸入門戶的地址進入門戶系統;
2) 用戶輸入門戶系統的用戶名和密碼;
3) 認證中心向AD發起登錄認證;
4) AD認證失敗返回門戶系統的登錄頁面,系統記錄日志並提示錯誤信息;AD認證成功,門戶系統登錄成功轉到步驟6);
5) 用戶重新輸入門戶系統的用戶名和密碼,返回步驟3)重新登錄;
6) 門戶系統登錄成功跳轉到登錄來源頁,如果沒有登錄來源頁跳轉到門戶系統首頁;
Step2
1) 用戶在門戶系統的頁面,點擊外部應用系統的鏈接,進行單點登錄流程;
2) 門戶系統先判斷門戶登錄憑證是否失效;
3) 門戶系統登錄憑證失效返回Step1的 門戶系統登錄流程;
4) 門戶系統登錄憑證沒有失效,判斷單點登錄的系統是否是集成AD;
5) 集成AD登錄的系統直接獲取當前用戶名和密碼;
6) 沒有集成AD登錄的系統,通過用戶映射關系獲取外部應用系統的用戶名和密碼;
7) 如果不存在對應的外部應用系統的用戶映射關系(沒有做初始化外部應用系統的用戶映射關系情況),彈出單點登錄映射信息填寫頁,用戶填寫外部應用系統的用戶名和密碼;
8) 如果存在對應的外部應用系統的用戶映射關系,獲取外部應用系統的用戶名和密碼;
9) 對用戶填寫的外部應用系統的用戶名和密碼,門戶需要記錄;
10) 把外部應用系統的用戶名和密碼加密(對稱加密)(分別按門戶系統與外部接入系統約定方式進行加密解密);
11) 把加密后的用戶名和密碼以參數形式傳遞給外部應有系統(外部應用系統需要實現接受用戶名和密碼的接口);
12) 外部應用系統先運行原有的登錄認證流程,發現在原有認證流程無法使系統登錄的時候,從參數讀取用戶憑證,也不存在調用門戶系統登錄請求接口;
13) 能獲取用戶憑證,對用戶憑證進行解密(分別按門戶系統與外部接入系統約定方式進行加密解密);
14) 使用用戶憑證進行外部應用系統的登錄認證;
15) 認證成功轉到外部應用系統頁面,並調用門戶記錄日志
16) 認證失敗調用門戶系統登錄請求接口
17) 調用門戶登錄請求會重新執行步驟2);
- 場景二.用戶在瀏覽器直接進入外部應用系統(參看流程圖Step3)
Step3
1) 用戶在瀏覽器直接輸入外部應用系統的地址;
2) 外部應用系統實現的流程和Step2的步驟12)一致,先實現外部應有系統的登錄認證流程,若沒有不能登錄系統,讀取參數,都讀取不到用戶憑證向門戶系統發出登錄請求(參看接口3.6.2),若能讀取對用戶憑證解密,進行外部應用系統的認證登錄;
3) 認證成功轉到外部應用系統頁面,並調用門戶記錄日志
4) 認證失敗調用門戶系統登錄請求接口
這方案的優點是可以實現雙向登錄,而且針對所有web站點都可以實現單點登錄..缺點是實現比較復雜,而且需要更改其他系統.
至於代部分碼我將會在接下來的博客中介紹.敬請各位關注.
C#后台調用跨域MVC服務,帶Cookie驗證
背景
隨着富客戶端框架的盛行,以及眾多優秀的前端js框架,很多情況我們會遇到跨域的問題,而js的ajax請求是不允許直接跨域訪問的,當然你會說可以用JSONP等,但是由於代碼潔癖,不想在前端和后台添加callback,而且很多情況你是無法控制的,需要牽連考慮太多的情況。
所以我直接繞過了,每個前端應用,自帶一個通用后端服務代理,該服務解決跨域問題,自動代理幫前台獲取跨域的數據。
如何算跨域
雖然是個老問題,但是還是要提醒注意下兩點:同IP,不同端口,數據訪問是跨域的,但是Cookie訪問是可以的(這個讓我很難理解)
解決,源碼
CookieContainer cookieContainer = new CookieContainer(); [HttpPost] public string CommonPost(string url) { log.Info(CookieHelper.GetCookie("ITDC_UserName") + "進入方法CommonPost Url=" + url); Uri address = new Uri(System.Configuration.ConfigurationManager.AppSettings["RESTfulAPI"].ToString() + url); HttpWebRequest request = WebRequest.Create(address) as HttpWebRequest; request.Method = "POST"; request.ContentType = "application/x-www-form-urlencoded"; //遠程服務,需要加入cookie驗證 cookieContainer.Add(address, GetCookie("ITDC_UserName")); cookieContainer.Add(address, GetCookie("ITDC_UserRole")); request.CookieContainer = cookieContainer; StringBuilder data = new StringBuilder(); for (int i = 0; i < Request.QueryString.Count; i++) { if (Request.QueryString.Keys[i].ToString() == "url") continue; data.Append("&" + Request.QueryString.Keys[i].ToString() + "=" + Request.QueryString[i].ToString()); } // Create a byte array of the data we want to send byte[] byteData = UTF8Encoding.UTF8.GetBytes(data.ToString().TrimStart('&')); // Set the content length in the request headers request.ContentLength = byteData.Length; // Write data using (Stream postStream = request.GetRequestStream()) { postStream.Write(byteData, 0, byteData.Length); } string result = ""; using (HttpWebResponse response = request.GetResponse() as HttpWebResponse) { StreamReader reader = new StreamReader(response.GetResponseStream()); result = reader.ReadToEnd(); } log.Info(CookieHelper.GetCookie("ITDC_UserName") + " 執行完成 CommonPost Url=" + url); return (result); }
前台調用
Ext.Ajax.request({url: APIUrl + '/Nebula/CommonPost?url=/Nebula/PostComment/&KlId=1&Msg=ok&Author=admin&Title=文章標題', method: "POST", success: function (response) { Ext.Viewport.unmask(); var obj = Ext.decode(response.responseText); Ext.Msg.alert("提示", obj.Msg, Ext.emptyFn); }, failure: function (response) { Ext.Viewport.unmask(); Ext.Msg.alert("提示", "操作失敗,請檢查網絡!", Ext.emptyFn); } });
文章部分內容可能摘自網絡,如果侵犯您的權益,請及時聯系我,謝謝.
作者:JackChain