歡迎光臨
每天分享高質量文章

.net core實踐系列之SSO-跨域實現

作者:陳珙

鏈接: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",
                asyncfalse,
                successfunction (d{
                    token = d.token;
                }
            });
            return token;
        }
    });
script
>





requestIdleCallback(doWork, 2000)

 

業務系統Token儲存與註銷

 

public class TokenController : Controller
    {
        public static TokenCookieOptions CookieOptions { getset; }

        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 = "[email protected]#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、資料庫、運維等

    赞(0)

    分享創造快樂