十六、【適合中小企業的.Net輕量級開源框架】EnterpriseFrameWork框架核心類庫之單點登錄SSO


回《【開源】EnterpriseFrameWork框架系列文章索引》       

EFW框架源代碼下載:http://pan.baidu.com/s/1qWJjo3U

 

       單點登錄(Single Sign On),簡稱為 SSO,是目前比較流行的企業業務整合的解決方案之一。SSO的定義是在多個應用系統中,用戶只需要登錄一次就可以訪問所有相互信任的應用系統。

 

如上圖,當用戶第一次訪問應用系統1的時候,因為還沒有登錄,會被引導到認證系統中進行登錄;根據用戶提供的登錄信息,認證系統進行身份校驗,如果通過校驗,應該返回給用戶一個認證的憑據--ticket;用戶再訪問別的應用的時候,就會將這個ticket帶上,作為自己認證的憑據,應用系統接受到請求之后會把ticket送到認證系統進行校驗,檢查ticket的合法性。如果通過校驗,用戶就可以在不用再次登錄的情況下訪問應用系統2和應用系統3了。

上面這段文字描敘是從網上摘抄的,覺得基本上把單點登錄的原理講得很清晰了;本章講解EFW框架中是如何實現單點登錄的以及框架中是如何使用的;

 

本文要點:

1.什么情況下會用到單點登錄

2.框架中的三種模式Web、Winform和WCF,分別是怎樣進行用戶驗證

3.單點登錄樣例

4.單點登錄SSO實現代碼

 

1.什么情況下會用到單點登錄

       剛開始框架中也是沒有單點登錄此模塊的,有一次需要在Winform系統中嵌入Web頁面,整合兩個系統。Web頁面的用戶信息驗證一直沒找到什么好的解決辦法,剛開始的辦法是通過往網頁地址后面自動加上登錄用戶名和密碼,發送到后台進行登錄;這樣也達到了整合的目的,但是總感覺比較別扭,直接把用戶名和密碼暴露在地址欄肯定存在安全隱患。后來經過一番波折在網上找到類似的解決辦法,利用單點登錄的方案達到了比較好的效果;

       除了上面說的Winform系統中嵌入Web頁面這種情況,還有經常在自己公司系統中整合一些合作伙伴的系統,如此打包銷售更有市場競爭力,這樣首要解決的問題也是登錄入口統一;如今行業軟件現狀,不像十年前了只有那么一兩套系統,講究着用就行了,現在沒有用上十來個系統就不叫信息化了,所以你能提供一個單點登錄的解決方案也是一大賣點;再就是現在的軟件公司不管大小靠一個產品就能生存的很難了,基本都是最大化的挖掘客戶的需求,最好能提供一整套的解決方案,這些系統能整體銷售更好,而單個產品銷售也得支持。所以不管是客戶的需求還是內部的產品都會存在系統間整合的問題,而利用單點登錄至少能解決用戶統一驗證的問題;

 

2.框架中的三種系統模式Web、Winform和WCF,分別是怎樣進行用戶驗證

先分析一下框架中的三種系統模式的用戶驗證是如何實現的,然后才能正確運用單點登錄的功能;

1)Web系統用戶驗證,輸入用戶名密碼登錄后,登錄界面向后台發送登錄請求調用LoggingController執行用戶密碼驗證代碼,驗證正確后把用戶信息存入Session;之后所有界面操作向后台發送請求,APIHttpHandler對象接收請求后,先判斷Session中是否存在用戶信息,只有存在才執行對應的控制器代碼,否則再返回錯誤信息給前台;

 

這里增加了一個系統配置參數TurnOnLoginRight用來是否打開驗證用戶登錄,這在我們開發系統中調試后台控制器很有用;

 

2)Winform系統用戶驗證,這個比較簡單,用戶登錄后根據后台配置的用戶權限,動態加載系統的菜單,所以后面的操作也不用再進行用戶驗證了;

 

3)WCF系統用戶驗證,就是上面兩種的結合了,客戶端如Winform系統,WCF中間件在WCFHandlerService服務中進行用戶驗證;

 

3.單點登錄樣例

用上面講過的Winform系統嵌入Web頁面這種情況下如何使用單點登錄的功能;

在登錄成功后調用框架中的SsoHelper對象的SignIn方法生成TokenKey;

 

然后把TokenKey值加入Web頁面的URL地址之后,然后web頁面向后台發送Ajax請求的時候把TokenKey當成參數傳入后台,后台進行單點登錄驗證。

 

 

 

4.單點登錄SSO實現代碼

框架源代碼目錄結構:

 

其中外部調用SSO功能只需要調用SsoHelper對象就可以了,SsoHelper把SSO封裝成外部調用的類;包括SignIn、SignOut、ValidateToken等方法;TokenManager類存儲所有登錄用戶的信息,TokenInfo類封裝的用戶信息結構;
SsoHelper文件

  1 /// <summary>
  2     /// 單點登錄輔助類
  3     /// </summary>
  4     public class SsoHelper
  5     {        
  6         /// <summary>
  7         /// 登錄
  8         /// </summary>
  9         /// <param name="userId"></param>
 10         /// <param name="tokenid"></param>
 11         /// <returns></returns>
 12         public static bool SignIn(string userId,string userName, out Guid tokenid)
 13         {
 14             TokenInfo existToken = TokenManager.GetToken(userId);
 15             if (existToken != null)
 16             {
 17                 tokenid = existToken.tokenId;
 18                 return true;
 19             }
 20 
 21             TokenInfo token = new TokenInfo()
 22             {
 23                 tokenId = Guid.NewGuid(),
 24                 IsValid = true,
 25                 CreateTime = DateTime.Now,
 26                 ActivityTime=DateTime.Now,
 27                 UserId = userId,
 28                 UserName=userName//,
 29                 //RemoteIp = Utility.RemoteIp
 30             };
 31             tokenid = token.tokenId;
 32             return TokenManager.AddToken(token);
 33         }
 34         /// <summary>
 35         /// 注銷
 36         /// </summary>
 37         /// <param name="token"></param>
 38         /// <returns></returns>
 39         public static bool SignOut(Guid token)
 40         {
 41             return TokenManager.RemoveToken(token);
 42         }
 43         /// <summary>
 44         /// 是否有效登錄
 45         /// </summary>
 46         /// <param name="token"></param>
 47         /// <returns></returns>
 48         public static AuthResult ValidateToken(string token)
 49         {
 50             Guid guid= ConvertHelper.GetGuid(token, Guid.NewGuid());
 51 
 52             AuthResult result = new AuthResult() { ErrorMsg = "Token不存在" };
 53             TokenInfo existToken = TokenManager.GetToken(guid);
 54 
 55             if (existToken != null)
 56             {
 57                 #region 客戶端IP不一致
 58                 //if (existToken.RemoteIp != entity.RemoteIp)
 59                 //{
 60                 //    result.ErrorMsg = "客戶端IP不一致";
 61                 //}
 62                 #endregion
 63 
 64                 if (existToken.IsValid == false)
 65                 {
 66                     result.ErrorMsg = "Token已過期" + existToken.ActivityTime.ToLongTimeString() + ":" + DateTime.Now.ToLocalTime();
 67                     TokenManager.RemoveToken(existToken.tokenId);//移除
 68                 }
 69                 else  
 70                 {
 71                     result.User = new UserInfo() { UserId = existToken.UserId,UserName=existToken.UserName, CreateDate = existToken.CreateTime };
 72                     result.ErrorMsg = string.Empty;
 73                 }
 74             }
 75 
 76             return result;
 77         }
 78 
 79         /// <summary>
 80         /// 定時觸發登錄碼的活動時間,頻率必須小於4分鍾
 81         /// </summary>
 82         /// <param name="token"></param>
 83         public static void UserActivity(Guid token)
 84         {
 85             TokenInfo existToken = TokenManager.GetToken(token);
 86             existToken.ActivityTime = DateTime.Now;
 87         }
 88 
 89         /// <summary>
 90         /// 用戶是否在線
 91         /// </summary>
 92         /// <param name="userId"></param>
 93         /// <returns></returns>
 94         public static bool IsUserOnline(string userId)
 95         {
 96             TokenInfo existToken = TokenManager.GetToken(userId);
 97             if (existToken != null) return true;
 98             return false;
 99         }
100     }
View Code

TokenManager文件

 1  public class TokenManager
 2     {
 3         private const int _TimerPeriod = 60000;//60秒
 4         private static Timer thTimer;
 5 
 6         static List<TokenInfo> tokenList = null;
 7 
 8         static TokenManager()
 9         {
10             tokenList = new List<TokenInfo>();
11             thTimer = new Timer(_ThreadTimerCallback, null, _TimerPeriod, _TimerPeriod);
12         }
13 
14         public static bool AddToken(TokenInfo entity)
15         {
16             tokenList.Add(entity);
17             return true;
18         }
19 
20         public static bool RemoveToken(Guid token)
21         {
22             TokenInfo existToken = tokenList.SingleOrDefault(t => t.tokenId ==token);
23             if (existToken != null)
24             {
25                 tokenList.Remove(existToken);
26                 return true;
27             }
28 
29             return false;
30         }
31 
32         public static TokenInfo GetToken(Guid token)
33         {
34             TokenInfo existToken = tokenList.SingleOrDefault(t => t.tokenId == token);
35             return existToken;
36         }
37 
38         public static TokenInfo GetToken(string userId)
39         {
40             TokenInfo existToken = tokenList.SingleOrDefault(t => (t.UserId == userId && t.IsValid==true));
41             return existToken;
42         }
43 
44         private static void _ThreadTimerCallback(Object state)
45         {
46             DateTime now = DateTime.Now;
47 
48             Monitor.Enter(tokenList);
49             try
50             {
51                 // Searching for expired users
52                 foreach (TokenInfo t in tokenList)
53                 {
54                     if (((TimeSpan)(now - t.ActivityTime)).TotalMilliseconds > _TimerPeriod)
55                     {
56                         t.IsValid = false;//失效
57                     }
58                 }
59             }
60             finally
61             {
62                 Monitor.Exit(tokenList);
63             }
64         }
65     }
View Code

TokenInfo文件

 1  public class TokenInfo
 2     {
 3         public Guid tokenId { get; set; }
 4 
 5         public DateTime CreateTime { get; set; }
 6 
 7         public DateTime ActivityTime { get; set; }
 8 
 9         public string RemoteIp { get; set; }
10 
11         public string UserId { get; set; }
12 
13         public string UserName { get; set; }
14 
15         public bool IsValid { get; set; }
16     }
View Code

 

參考資料:

編寫你自己的單點登錄(SSO)服務

http://blog.csdn.net/javachannel/article/details/752437


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM