33

IIS 整合式 Windows 驗證之極簡風 SSO 整合

 2 years ago
source link: https://blog.darkthread.net/blog/tiny-sso/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
IIS 整合式 Windows 驗證之極簡風 SSO 整合-黑暗執行緒

遇到特殊 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,符合我偏愛的極簡風,提供大家參考。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK