作者:陳珙
連結:https://www.cnblogs.com/skychen1218/p/9805995.html
前言
接著上篇的《.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認證,我唯一能找到的解決方案就是跳轉頁面。
具體步驟:
1、認證中心登入成功後,請求登入中心介面獲得token
2、攜帶token逐個跳轉到業務系統的中轉頁面。
3、跳轉完成後,傳回到認證中心登入頁面進行引導。
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>
…
requestIdleCallback(doWork, 2000)
業務系統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與文章寫的比較倉促,如果朋友們有更好的實現方式與建議,麻煩在下麵評論反饋給我,先在此感謝。
●編號171,輸入編號直達本文
●輸入m獲取文章目錄
Web開發
更多推薦《18個技術類公眾微信》
涵蓋:程式人生、演演算法與資料結構、駭客技術與網路安全、大資料技術、前端開發、Java、Python、Web開發、安卓開發、iOS開發、C/C++、.NET、Linux、資料庫、運維等