.Net Core外部登入中的一個坑:Correlation failed

在開發youzack。com背單詞模組的“外部登入”的時候,又遇到另外一個Bug,頭疼了好久。今天我透過這篇文章把解決的過程分享出來。

解決完《

解決反向代理後的。Net Core網站進行第三方登入的Bug

》這篇文章提到的Bug之後,又遇到另一個問題,那麼就是在微信中開啟登入頁面、拉起QQ登入,登入完成後,跳轉回微信,報錯“Correlation failed”,沒有具體的異常堆疊資訊,不知道是哪裡丟擲的異常。幸好。Net Core是開源的,所以到。Net Core的Microsoft。AspNetCore。Authentication。OAuth原始碼中去搜尋,終於在OAuthHandler類中找到了“Correlation failed”,如下:

protected override async Task HandleRemoteAuthenticateAsync()

{

var query = Request。Query;

var state = query[“state”];

var properties = Options。StateDataFormat。Unprotect(state);

if (properties == null)

{

return HandleRequestResult。Fail(“The oauth state was missing or invalid。”);

}

if (!ValidateCorrelationId(properties))

{

return HandleRequestResult。Fail(“Correlation failed。”, properties);

}

//。。。

}

經過分析程式碼,得知OAuth跳轉到第三方頁面之前會在Cookie中寫入類似於“AspNetCore。Correlation。QQ。23423424234242423=N”的值,從第三方頁面跳轉回來以後,第三方網站會把這個值以state引數的方式傳遞回來,我們的網站需要把本地cookie中的值和state值進行校驗比對。這樣就可以防範CSRF攻擊。

用上一篇文章介紹的方法,把從第三方頁面跳轉回來的Http請求的報文列印到日誌中,比對在普通瀏覽器中拉起QQ登入和在微信中拉起QQ登入有什麼不懂。我發現在微信中拉起QQ登入完成後返回的頁面回撥的Http請求中,是沒有這個Cookie值的,但是在跳轉之前的Http響應報文中,則是有這個Cookie值的。這證明了一點:在跳轉到第三方登入頁面之前Set-Cookie寫入給瀏覽器的Cookie無效或者被丟失了。

我之前是模模糊糊記得,在有一些瀏覽器中,在帶重定向的Http響應中設定Cookie的Set-Cookie響應頭,會被瀏覽器忽略。經過搜尋,發現有人說有一些瀏覽器中,重定向到其他域名的Http響應中的Set-Cookie會被忽略。但是經過測試,我的微信中(不是所有人的微信都是這樣,不清楚具體受影響的是哪些微信),即使是重定向到本域名下頁面的Http響應中的Set-Cookie也會被忽略。

解決方法有兩種,第一種簡單就是直接禁用ValidateCorrelationId檢查。編寫一個MyQQAuthenticationHandler類繼承自QQAuthenticationHandler,然後重寫ValidateCorrelationId方法,方法裡直接返回true。

然後在Starup裡AddQQ的程式碼改為:

。AddOAuth(QQAuthenticationDefaults。AuthenticationScheme

, QQAuthenticationDefaults。DisplayName,。。。);

這樣做的缺點就是禁用了CSRF檢查,系統的安全性會降低。

第二種解決方法就是在本網站編寫一個頁面,這個頁面中負責把Cookie寫入瀏覽器,然後再跳轉到QQ等第三方登入頁面。修改生成重定向頁面的程式碼,讓重定向先跳轉到這個我們頁面。

先編寫一個Action方法:

public IActionResult RedirectToChallengeUrl(string url,string cookieName, string authenticationSchemeName)

{

StringBuilder sb = new StringBuilder();

sb。AppendLine(“”);

sb。AppendLine($“Continue”);

CookieOptions cookieOption = new CookieOptions();

cookieOption。Expires = DateTimeOffset。UtcNow。AddMinutes(15);

cookieOption。Path = “/”;

Response。Cookies。Append(cookieName, “N”, cookieOption);

return Content(sb。ToString(),“text/html”);

}

可以看到這裡首先構造了一個頁面的html,html中透過javascript程式碼進行了頁面的跳轉,同時為了防止萬一某些瀏覽器不支援js跳轉,還提供了一個讓使用者手動重定向的【Continue】連結。然後再寫入Cookie的值。

這裡沒有直接用Response。Redirect()而是構造一個透過js程式碼進行重定向的原因就是我的微信中“重定向到本域名下頁面的Http響應中的Set-Cookie也會被忽略”。

然後同樣編寫一個MyQQAuthenticationHandler,這次是重寫BuildChallengeUrl,這是一個用來生成跳轉到第三方登入頁面的地址的方法,我們把它的url修改為先跳轉到我們自己的RedirectToChallengeUrl地址:

protected override string BuildChallengeUrl(AuthenticationProperties properties, string redirectUri)

{

string url = base。BuildChallengeUrl(properties, redirectUri);

string redirectActionName = “RedirectToChallengeUrl”;

string xsrf = properties。Items[“。xsrf”];

var cookieName = Options。CorrelationCookie。Name + Scheme。Name + “。” + xsrf;

string redirectUrl = $“/Account/Home/{redirectActionName}?url=”+Uri。EscapeDataString(url)

+ “&cookieName=” + Uri。EscapeDataString(cookieName) + “&AuthenticationSchemeName=” + Uri。EscapeDataString(this。Scheme。Name);

return redirectUrl;

}

然後用和第一種解決方法一樣的方式註冊MyQQAuthenticationHandler。這種方式的優點是保證了安全性。不過,曾經有使用者反饋過,它的蘋果手機上裝了firefox瀏覽器,在firefox瀏覽器中訪問我們的網站,點選QQ登入,拉起了QQ,但是QQ登入完成後,竟然在safari瀏覽器中打開了回撥頁面,跨兩個瀏覽器當然就不可能訪問設定的Cookie了,這是這種方案的唯一缺點。

請根據自己的情況選用合適的方案吧。