[ASP.NET]多個 Web Application 使用同一個 Forms Authentication

有多個 Web Application 可以共同一個 Forms Authentication 來達到 single sign on哦!

問題

我們有一個 Web Application (WebMainLogin) 及一個 Web API Application(MyWebAPI1),而 Web API 只給那個 Web Application 去使用。

沒有登入那個 Web Application 就不可以去 Access 它。 一般會透過 Web Application 在 Server 端去 Access 那個 Web API 。

但是目前的狀況我們是在 Web Application 裡透過 JS 去 Access 它的! 但目前不想再透過 Web Application 的 Server 端去 Access 。

環境

Web Application : WebMainLogin  (.NET 3.5)

Web API : MyWebAPI1 (.NET 4.0)

 

研究

要達到「Web API 只給那個 Web Application 去使用」有想到以下2個方式。

1.將那個 Web API 整進 Web Application 之中。

2.在 Web API 那裡使用 Forms Authentication 。

評估起來,使用第2點似乎比較容易些。

 

實作

因為原本的 Web Application 是使用 Forms Authentication ,所以再來就是讓 Web API 也使用 Forms Authentication 。這樣 2 個 Web Application 就都使用相同的驗證。

所以就將 Web Application 的 web.confg 將 authentication 部份,蓋掉原本在 Web API 的 web.config 的 authentication mode="None"部份,如下,

<authorization>
	<deny users="?"/>
</authorization>
<authentication mode="Forms">
	<forms name="ShareAuth" loginUrl="Login.aspx" protection="All" path="/"/>
</authentication>

Web API 的設定要跟 Web Application  一樣哦! forms 的 name 也要相同哦!

然後直接開啟 Web API 應該就會驗證不過,而被導到 Login.aspx ,如下,

image

 

這樣就達成我們設定 Web API 使用 Forms Authentication 的需求。如果沒有登入就不能使用 Web API。

再來就是要讓當使用者登入 Web Application 後可以去 Access Web API 。

所以我們要在2個 web.config 中設定一樣的 machineKey 。

要建立 machineKey 最快的方式就是透過 IIS 來建立(開啟 IIS 管理員 在 ASP.NET 裡的「電腦金鑰」),如下,

SNAGHTML74055a

image

取消勾選「在執行階段自動產生」,然後按下右邊的「產生金鑰」,就會幫我們產生電腦金鑰了,如下,

image

然後把分別把「驗證金鑰」及「解密金鑰」Copy 到 machineKey 裡的 validationKey 及 decryptionKey 之中,如下,

image

 

所以2個 web.config 中的驗證設定如下,

<machineKey
  validationKey="IIS做出來的驗證金鑰"
  decryptionKey="IIS做出來的解密金鑰"
  validation="SHA1"
  decryption="AES"
/>
<authorization>
  <deny users="?"/>
</authorization>
<authentication mode="Forms">
  <forms name="ShareAuth" loginUrl="Login.aspx"protection="All"  path="/"   />
</authentication>

 

再來修改 ValuesController.cs 裡的 Get Method,來回傳登入者的資訊,如下,

// GET api/values/5
public string Get(int id)
{
	if (User.Identity.IsAuthenticated)
	{
		return string.Format("{0}, getId:{1}", User.Identity.Name, id);
	}
	else
	{
		return string.Format("{0}, getId:{1}", "沒有登入", id);
	}
}

然後我們先從 Web Application 登入,再開另一個 Tab 連到 Web API 看看是否能取得到 登入者的資訊,如下,

image

image

image

如果沒有先登入 Web Application 的話,直接去 Access Web API 跟前面一樣,被導到 Login.aspx 哦!

image

 

有時 .NET 版本不同(例如 .NET 4 跟 .NET 4.5),這時可以在 Application_BeginRequest 裡去取出 驗證的 cookie 來試看看能不能解開,如下,

protected void Application_BeginRequest(object sender, EventArgs e)
{
	var authCookieName = FormsAuthentication.FormsCookieName;
	HttpCookie authCookie = HttpContext.Current.Request.Cookies[authCookieName];
	if (authCookie == null)
	{
		throw new Exception("取不到驗證Cookie值");
	}
	//如果 MachineKey 不對,或是.NET Framework 有差異,可能會發生 0x80004005 的錯誤
	FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(authCookie.Value);
	//解開後,可以取得登入者的名稱
	string userName = ticket.Name;
}

解不開的錯誤如下,

[HttpException (0x80004005): 無法驗證資料。]
   System.Web.Configuration.MachineKeySection.EncryptOrDecryptData(Boolean fEncrypt, Byte[] buf, ....) +1358
   System.Web.Security.FormsAuthentication.Decrypt

 

當然,如果您有多個 Web Application ,也可以透過這種方式,共用一個 Forms Authentication ,如果驗證不過,就導到那個主要的登入 Web Application。

 

參考資源

How To: Configure MachineKey in ASP.NET 2.0

MachineKeySection 類別

ASP.NET MachineKey自動產生原理剖析

Forms Authentication Across Applications

Hi, 

亂馬客Blog已移到了 「亂馬客​ : Re:從零開始的軟體開發生活

請大家繼續支持 ^_^