雖然 Windows 11 Edge IE 模式無法用 AD 帳號登入網站的疑慮已消除,IE Only 古蹟不致立即崩塌,但現在已是時侯該安排都更計劃,將老舊危樓逐步改建翻新,別拖到重要的事變成重要又緊急,搞出一場盛大「肝臟爆破秀」... Orz

今天遇到的問題與跨網站 AJAX 呼叫有關。我們都知道 IE 將網站區分成謂網際網路、近端內部網路、信任的網站、限制的網站等區域,而在「信任的網站」保護傘下,程式享有許多特權,可以啟用 ActiveX、無視跨站台存取限制等 (延伸閱讀:IE CORS跨網域存取的特殊規則),當轉到 Chrome 或 Edge Chromium,一切回歸 HTML 標準,問題便如雨後春筍般冒出頭。

CORS 也不是什麼難解問題,透過加入 Access-Control-Allow-Origin 及相關 Header,並設好 IIS 接收 OPTIONS Preflight,理論上就 OK 了。

用以下網頁示範:

ℎttp://127.0.0.1/aspnet/cors/client.html

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>CORS Test</title>
</head>
<body>
	<button>Test</button>
	<script src="jquery-1.12.4.min.js"></script>
	<script>
		$('button').click(function() {
			$.post('http://192.168.50.7/aspnet/cors/cross-site.aspx').done(function(res) { 
				alert('OK-' + res);
			}); 
		});
	</script>
</body>
</html>

ℎttp://192.168.50.7/aspnet/cors/cross-site.aspx

<%@Page Language="C#"%>
<script runat="server">
void Page_Load(object sender, EventArgs e)
{
	//Response.AddHeader("Access-Control-Allow-Origin", "http://127.0.0.1");
	//Response.AddHeader("Access-Control-Allow-Methods", "POST,GET");
	Response.ContentType = "text/plain";
	Response.Write(DateTime.Now.ToString("mmssfff"));
}
</script>

由於 client.html 與 cross-site.aspx 網站 IP 不同屬於不同來源,不意外地會收到錯誤:Access to XMLHttpRequest at 'http://192.168.50.7/aspnet/cors/cross-site.aspx' from origin 'http://127.0.0.1' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

在 cross-site.aspx 啟用 esponse.AddHeader("Access-Control-Allow-Origin", "http://127.0.0.1"); 及 Response.AddHeader("Access-Control-Allow-Methods", "POST,GET");,即可打通跨站台存取。

到這裡都算簡單,如果我停用 cross-site.aspx 的匿名存取,改為 Windows 整合驗證,麻煩就來了。

Access to XMLHttpRequest at 'http://192.168.50.7/aspnet/cors/cross-site.aspx' from origin 'http://127.0.0.1' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.再次出現,理由是 IIS 回傳 401 要求進行身分認證,而這個回應未包含 Access-Control-Allow-Origin,直接被瀏覽器封鎖。

傳回 401 這段仍在 IIS 處理階段,還沒輪到 cross-site.apsx 執行,所以我們在 web.config 加入 Access-Control-Allow-Origin 能解決問題嗎?

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <httpProtocol>
            <customHeaders>
                <add name="Access-Control-Allow-Origin" value="http://127.0.0.1" />
                <add name="Access-Control-Allow-Methods" value="GET,POST" />
            </customHeaders>
        </httpProtocol>
    </system.webServer>
</configuration>

加入後,上述錯誤訊息消失,但瀏覽器接收 401 錯誤卻不會帶出帳號密碼輸入視窗,以存取被拒收場。

CORS 請求涉及第三方站台,基於安全及隱私考慮通常會以匿名方式發送,不夾帶 Cookie 等身分識別。研究許久,終於查到關鍵 - XHR 有個 withCredentials 屬性,設定後 XHR 會傳送 Cookie 也能完成 Windows 身分驗證。jQuery.ajax 有個 xhrFields 可指定 withCredentials,所以 client.html 要修改如下:

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>CORS Test</title>
</head>
<body>
	<button>Test</button>
	<script src="jquery-1.12.4.min.js"></script>
	<script>
		$('button').click(function() {
			$.ajax({
				url: 'http://192.168.50.7/aspnet/cors/cross-site.aspx',
				method: 'POST',
				xhrFields: {
					withCredentials: true
				}
			}).done(function(res) { 
				alert('OK-' + res);
			}); 
		});
	</script>
</body>
</html>

然後,IIS 端還要加上 Access-Control-Allow-Credentials: true Header。

終於,掛著第三站台 IP 的帳號密碼輸入視窗出現了,我感動到熱淚盈眶...

Tips of how to send CORS requests to Window authentication ASP.NET pages.


Comments

# by Huang

IE逝去的美好,只能留給回憶

Post a comment