.net core實踐系列之SSO-跨域實現
前言
接著上篇的《.net core實踐系列之SSO-同域實現》,這次來聊聊SSO跨域的實現方式。這次雖說是.net core實踐,但是核心點使用jquery居多。
建議看這篇文章的朋友可以先看上篇《.net core實踐系列之SSO-同域實現》做一個SSO大概了解。
源碼地址:https://github.com/SkyChenSky/Core.SSO.git
效果圖
知識點回顧
實現原則
只要統一Token的產生和校驗方式,無論授權與認證的在哪(認證系統或業務系統),也無論用戶信息存儲在哪(瀏覽器、服務器),其實都可以實現單點登錄的效果
實現關鍵點
- Token的生成
- Token的共享
- Token校驗
Token共享復雜度
- 同域
- 跨域
Token認證方式
- 業務系統自認證
- 轉發給認證中心認證
同源策略
所有支持JavaScript 的瀏覽器,都必須遵守的安全策略,也是瀏覽器最基本的安全功能。
如果沒有處理過發起跨域請求,就算服務器接收到了,響應成功了瀏覽器也是會攔截的。
同源
指域名,協議,端口相同
目的
瀏覽器為了阻止惡意腳本獲取不同源上的的敏感信息。
跨域請求
然而在實際情況下跨域請求的場景也是存在的,解決方案有兩種:
- JSONP
- 響應頭設置“Access-Control-Allow-Origin”
Cookie
Cookie的讀取和發送也是必須遵循同源策略的。
雖說請求共享可以設置響應頭Access-Control-Allow-Credentials、Access-Control-Allow-Origin與Ajax請求屬性xhrFields: {withCredentials: true}進行解決,但是!
就算響應頭有set-cookie瀏覽器也是無法正常保存的。
SSO跨域解決方式
針對cookie認證,我唯一能找到的解決方案就是跳轉頁面。
具體步驟:
- 認證中心登錄成功後,請求登錄中心接口獲得token
- 攜帶token逐個跳轉到業務系統的中轉頁面。
- 跳轉完成後,返回到認證中心登錄頁面進行引導。
PS:如果哪位朋友有更加好的方案,可以及時與我溝通,非常感謝
實現方式
登錄中心授權
<script> $(function () { $("#submit").click(function () { $("#postForm").ajaxSubmit(function (result) { if (result.success) { var token = getToken(); if (token) { var authorizeHostArray = new Array( "http://www.web1.com/Token/Authorization", "http://www.web2.com/Token/Authorization" ); var authorizeHostParams = ""; authorizeHostArray.forEach(function (item) { authorizeHostParams += "&hostAuthorization=" + item; }); window.location.href = authorizeHostArray[0] + "?token=" + token + authorizeHostParams; } } else { alert(result.msg); } }); }); function getToken() { var token = null; $.ajax({ url: "/api/Token", type: "GET", async: false, success: function (d) { token = d.token; } }); return token; } }); </script>
業務系統Token保存與註銷
public class TokenController : Controller { public static TokenCookieOptions CookieOptions { get; set; } public IActionResult Authorization(string token, List<string> hostAuthorization = null) { if (CookieOptions == null || string.IsNullOrEmpty(token)) return BadRequest(); HttpContext.Response.Cookies.Append(CookieOptions.Name, token, new CookieOptions { Domain = CookieOptions.Domain, Expires = DateTimeOffset.UtcNow.Add(CookieOptions.Expires), HttpOnly = CookieOptions.HttpOnly, IsEssential = CookieOptions.IsEssential, MaxAge = CookieOptions.MaxAge, Path = CookieOptions.Path, SameSite = CookieOptions.SameSite }); if (hostAuthorization.Any()) hostAuthorization = hostAuthorization.Where(a => !a.Contains(HttpContext.Request.Host.Host)).ToList(); if (!hostAuthorization.Any()) hostAuthorization = new List<string> { "http://www.sso.com" }; return View(new TokenViewData { Token = token, HostAuthorization = hostAuthorization }); } public IActionResult Logout(List<string> hostAuthorization = null) { HttpContext.Response.Cookies.Delete(CookieOptions.Name); if (hostAuthorization.Any()) hostAuthorization = hostAuthorization.Where(a => !a.Contains(HttpContext.Request.Host.Host)).ToList(); if (!hostAuthorization.Any()) hostAuthorization = new List<string> { "http://www.sso.com" }; return View(new TokenViewData { HostAuthorization = hostAuthorization }); } }
Token生成與認證
與同域的實現的方式一致。
生成與認證是一對的,與之對應的就是AES的加密與解密。
public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(options => { options.Cookie.Name = "Token"; options.Cookie.HttpOnly = true; options.ExpireTimeSpan = TimeSpan.FromMinutes(30); options.LoginPath = "/Account/Login"; options.LogoutPath = "/Account/Logout"; options.SlidingExpiration = true; //options.DataProtectionProvider = DataProtectionProvider.Create(new DirectoryInfo(@"D:\sso\key")); options.TicketDataFormat = new TicketDataFormat(new AesDataProtector()); TokenController.CookieName = options.Cookie.Name; }); }
internal class AesDataProtector : IDataProtector { private const string Key = "!@#13487"; public IDataProtector CreateProtector(string purpose) { return this; } public byte[] Protect(byte[] plaintext) { return AESHelper.Encrypt(plaintext, Key); } public byte[] Unprotect(byte[] protectedData) { return AESHelper.Decrypt(protectedData, Key); } }
業務系統自主認證的方式,對於系統的代碼復用率與維護性都很低。如果想進行轉發到認證系統進行認證,可以對[Authorize]進行重寫。
大致思路是:
訪問業務系統時,由自定義的[Authorize]進行攔截
獲取到Token設置到請求頭進行HttpPost到認證系統提供的/api/token/Authentication接口
響應給業務系統如果是成功則繼續訪問,如果是失敗則401或者跳轉到登錄頁。
結尾
最近事情比較多,demo與文章寫的比較倉促,如果朋友們有更好的實現方式與建議,麻煩在下面評論反饋給我,先在此感謝。
.net core實踐系列之SSO-跨域實現