遇到特殊 SSO 整合需求,在 Windows 上執行的第三方網站服務,登入部分允許客製但不支援整合式 Windows 驗證,如何借用 IIS 的整合式 Windows 簡單實現 SSO 整合呢?

我做了一個超精簡版本,只需一個資料夾三個檔案(sso.aspx、validate.aspx、web.config)搞定。

運作流程如下:

  1. 使用者以 Windows/AD 帳號登入 sso.aspx
  2. sso.aspx 取得使用者帳號,將其存入 Cache 並隨機產生對映 GUID
  3. sso.aspx 將瀏覽器重新導向第三方網站網址並加上「?t=對映GUID」參數
  4. 第三方網站取出對映 GUID 呼叫 validate.aspx 進行驗證
  5. validate.aspx 依對映 GUID 從 Cache 取出使用者帳號回傳給第三方網站,並將資料自 Cache 清除防止重複使用
  6. 第三方網站由 validate.aspx 取得使用者帳號資訊,切換登入身分

這裡有個眉角,sso.aspx 需啟用整合式 Windows 驗證以取得使用者帳號,而 validate.aspx 屬 WebAPI 性質需開放匿名存取,故 IIS 必須「同時」開放匿名驗證及Windows驗證(參考:在 Windows 驗證網站設定部分匿名存取),並從 web.config 設定:

<configuration>
    <appSettings>
        <add key="RedirectUrl" value="http://localhost/AspNet/TinySso/test.aspx" />
    </appSettings>
	<location path="sso.aspx">
		<system.web>
			<authorization>
				<deny users="?" />
			</authorization>
		</system.web>
	</location>
	<location path="validate.aspx">
		<system.web>
			<authorization>
				<allow users="*" />
			</authorization>
		</system.web>
	</location>
</configuration>

以下是 sso.aspx 程式。邏輯很單純,由 Request.LogonUserIdentity.Name 取得登入者帳號,產生 GUID 為 Key 存入 Cache,導向 appSetting 所指定的 URL 並附上 GUID 參數。

<%@ Page Language="C#"%>
<script runat="server">
void Page_Load(object sender, EventArgs e)
{
	var ssoRedirect = System.Configuration.ConfigurationManager.AppSettings["RedirectUrl"];
	var token = Guid.NewGuid().ToString();
	Cache.Add(token, Request.LogonUserIdentity.Name, null, DateTime.Now.AddSeconds(30), 
		Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.High, null);
	Response.Redirect(ssoRedirect + "?t=" + token);
}
</script>

validate.aspx 程式也很簡單,依據 GUID 從 Cache 取出帳號資訊回傳,帳號資訊讀取後立即從 Cache 清除,避免 GUID 被拿來多次登入。另外,它限定本機存取及 POST 方法,減少被誤用風險。

<%@ Page Language="C#"%>
<script runat="server">
void Page_Load(object sender, EventArgs e)
{
	var identity = "";
	if (Request.HttpMethod == "POST") 
	{
		var token = Request["t"];
		if (!string.IsNullOrEmpty(token) && Request.IsLocal) 
		{
			identity = (string)Cache[token];
			if (identity != null) Cache.Remove(token);
		}
	}
	Response.Write(identity);
}
</script>

至於第三方網站要怎麼做,我用一支 test.aspx 示範:

<%@ Page Language="C#"%>
<script runat="server">
void Page_Load(object sender, EventArgs e)
{
	var tinySsoCheck = "http://localhost/aspnet/tinysso/validate.aspx";
	var token = Request["t"];
	var identity = new System.Net.WebClient().UploadString(tinySsoCheck + "?t=" + token, "");
	if (!string.IsNullOrEmpty(identity)) 
		Response.Write("Welcome, " + identity.Split('\\').Last());
	else 
		Response.Write("Unkown User");
}
</script>

測試成功。

不過,test.aspx 自己就能做整合式 Windows 驗證了,測起來像脫褲子放屁。

我想到前陣子練習的 Python Django,改一下 views.py:

from django.shortcuts import render
import requests

# Create your views here.
def index(request):
    resp = requests.post('http://localhost/aspnet/tinysso/validate.aspx?t=' + request.GET.get('t', ''))
    if resp.status_code == requests.codes.ok:
        u = resp.text
    context = {
        'name': u
    }
    return render(request, 'index.html', context=context)

這樣子 Python 網站也能用整合式 Windows 驗證登入了,酷吧!

第三方網站可以裝在另一台機器甚至非 Windows 上嗎?可以,但 validate.aspx 需稍加改寫。習慣上我會限定呼叫 validate.aspx 的來源 IP,本機時可用 Request.IsLocal 檢查,若第三方網站不在同一台,可在 web.config 加入 <add key="ClientIps" value="192.168.1.1,192.168.1.2" /> 列舉會呼叫 validate.aspx 的來源,validate.aspx 則改為:

<%@ Page Language="C#"%>
<script runat="server">
void Page_Load(object sender, EventArgs e)
{
	var identity = "";
	var clientIps = System.Configuration.ConfigurationManager.AppSettings["ClientIps"].Split(',');
	if (Request.HttpMethod == "POST") 
	{
		var token = Request["t"];
		if (!string.IsNullOrEmpty(token) && clientIps.Contains(Request.UserHostAddress)) 
		{
			identity = (string)Cache[token];
			if (identity != null) Cache.Remove(token);
		}
	}
	Response.Write(identity);
}
</script>

不用建專案,免安裝,不依賴程式庫,就能實現 IIS 整合式 Windows 驗證 SSO,符合我偏愛的極簡風,提供大家參考。

A extremely simple implementation of SSO to make 3rd party web support integrated Windows authenticaion.


Comments

# by Ted

黑大您好 我想要請教如果今天情境是有超過2個以上第三方網站 如果第三方A網站驗證成功後有功能需求去第三方B網站 但因為Cache已被清空.....是否就需要再驗證一次

# by Jeffrey

to Ted, 如果不想驗證兩次,你可以改寫程式,sso.aspx 一次產生兩組 Guid 轉交 A 網站,A 網站接收後一組自用,一組交給 B 網站。另一種解法是讓 sso.aspx 有轉 A 網站與 B 網站兩種版本,連去 B 網站時一樣從 sso.aspx 轉過去,因為之前連 A 網站時已 Windows 整合式驗證過,不需要再 Key 帳密就可導到 B 網站。

# by Ted

To 黑大 謝謝您的答覆,受益良多

Post a comment