有多個 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 ,如下,
這樣就達成我們設定 Web API 使用 Forms Authentication 的需求。如果沒有登入就不能使用 Web API。
再來就是要讓當使用者登入 Web Application 後可以去 Access Web API 。
所以我們要在2個 web.config 中設定一樣的 machineKey 。
要建立 machineKey 最快的方式就是透過 IIS 來建立(開啟 IIS 管理員 在 ASP.NET 裡的「電腦金鑰」),如下,
取消勾選「在執行階段自動產生」,然後按下右邊的「產生金鑰」,就會幫我們產生電腦金鑰了,如下,
然後把分別把「驗證金鑰」及「解密金鑰」Copy 到 machineKey 裡的 validationKey 及 decryptionKey 之中,如下,
所以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 看看是否能取得到 登入者的資訊,如下,
如果沒有先登入 Web Application 的話,直接去 Access Web API 跟前面一樣,被導到 Login.aspx 哦!
有時 .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。
參考資源
Hi,
亂馬客Blog已移到了 「亂馬客 : Re:從零開始的軟體開發生活」
請大家繼續支持 ^_^