回《【開源】EnterpriseFrameWork框架系列文章索引》 html
EFW框架源代碼下載:http://pan.baidu.com/s/1qWJjo3Ujava
單點登陸(Single Sign On),簡稱爲 SSO,是目前比較流行的企業業務整合的解決方案之一。SSO的定義是在多個應用系統中,用戶只須要登陸一次就能夠訪問全部相互信任的應用系統。web
如上圖,當用戶第一次訪問應用系統1的時候,由於尚未登陸,會被引導到認證系統中進行登陸;根據用戶提供的登陸信息,認證系統進行身份校驗,若是經過校驗,應該返回給用戶一個認證的憑據--ticket;用戶再訪問別的應用的時候,就會將這個ticket帶上,做爲本身認證的憑據,應用系統接受到請求以後會把ticket送到認證系統進行校驗,檢查ticket的合法性。若是經過校驗,用戶就能夠在不用再次登陸的狀況下訪問應用系統2和應用系統3了。安全
上面這段文字描敘是從網上摘抄的,以爲基本上把單點登陸的原理講得很清晰了;本章講解EFW框架中是如何實現單點登陸的以及框架中是如何使用的;框架
本文要點:ide
1.什麼狀況下會用到單點登陸ui
2.框架中的三種模式Web、Winform和WCF,分別是怎樣進行用戶驗證spa
3.單點登陸樣例.net
4.單點登陸SSO實現代碼3d
剛開始框架中也是沒有單點登陸此模塊的,有一次須要在Winform系統中嵌入Web頁面,整合兩個系統。Web頁面的用戶信息驗證一直沒找到什麼好的解決辦法,剛開始的辦法是經過往網頁地址後面自動加上登陸用戶名和密碼,發送到後臺進行登陸;這樣也達到了整合的目的,可是總感受比較彆扭,直接把用戶名和密碼暴露在地址欄確定存在安全隱患。後來通過一番波折在網上找到相似的解決辦法,利用單點登陸的方案達到了比較好的效果;
除了上面說的Winform系統中嵌入Web頁面這種狀況,還有常常在本身公司系統中整合一些合做夥伴的系統,如此打包銷售更有市場競爭力,這樣首要解決的問題也是登陸入口統一;現在行業軟件現狀,不像十年前了只有那麼一兩套系統,講究着用就好了,如今沒有用上十來個系統就不叫信息化了,因此你能提供一個單點登陸的解決方案也是一大賣點;再就是如今的軟件公司無論大小靠一個產品就能生存的很難了,基本都是最大化的挖掘客戶的需求,最好能提供一整套的解決方案,這些系統能總體銷售更好,而單個產品銷售也得支持。因此無論是客戶的需求仍是內部的產品都會存在系統間整合的問題,而利用單點登陸至少能解決用戶統一驗證的問題;
先分析一下框架中的三種系統模式的用戶驗證是如何實現的,而後才能正確運用單點登陸的功能;
1)Web系統用戶驗證,輸入用戶名密碼登陸後,登陸界面向後臺發送登陸請求調用LoggingController執行用戶密碼驗證代碼,驗證正確後把用戶信息存入Session;以後全部界面操做向後臺發送請求,APIHttpHandler對象接收請求後,先判斷Session中是否存在用戶信息,只有存在才執行對應的控制器代碼,不然再返回錯誤信息給前臺;
這裏增長了一個系統配置參數TurnOnLoginRight用來是否打開驗證用戶登陸,這在咱們開發系統中調試後臺控制器頗有用;
2)Winform系統用戶驗證,這個比較簡單,用戶登陸後根據後臺配置的用戶權限,動態加載系統的菜單,因此後面的操做也不用再進行用戶驗證了;
3)WCF系統用戶驗證,就是上面兩種的結合了,客戶端如Winform系統,WCF中間件在WCFHandlerService服務中進行用戶驗證;
用上面講過的Winform系統嵌入Web頁面這種狀況下如何使用單點登陸的功能;
在登陸成功後調用框架中的SsoHelper對象的SignIn方法生成TokenKey;
而後把TokenKey值加入Web頁面的URL地址以後,而後web頁面向後臺發送Ajax請求的時候把TokenKey當成參數傳入後臺,後臺進行單點登陸驗證。
框架源代碼目錄結構:
其中外部調用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 }
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 }
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 }
參考資料:
編寫你本身的單點登陸(SSO)服務