我遇到的情境是這樣的:某歷史悠久的網站由新舊程式組成,早期開發是用 WebForm,後期擴充功能時另開了 MVC 專案,所以站台上有兩個 Web Application,一個是 Web Site,一個是 MVC。

架構潔癖者看到這裡可能就怒了,把系統搞成這樣是要怎麼維護? 是不會繼續用 WebForm 寫或乾脆全翻成 MVC 嗎?

唉,繼續用 WebForm 寫新功能會傷心,用 MVC 全部翻新系統會傷肝,還可能危害心血管跟飯碗(想想最近新聞上花一百億砍掉重練的故事...),當痛苦指數仍在可承受範圍內,海納百川是上策呀~

近期接到需求,系統要從 Windows 整合式驗證改為 Form 驗證,憑著第一時間直覺,我捲起袖子自幹了一套簡易版 Single Sign On 身分同步機制,搞到滿頭大汗,但也成功讓 MVC 用 WebForm 的 Form 驗證登入身分。

早上跑步,跑著跑著忽然想到一件事:同一個站台多個 Web Application 的 Cookie 原本就可共享, 而 Form 驗證靠的是 .ASPXAUTH Cookie 配合 Machine Key 加密。因此只要確保 WebForm 與 MVC 的 Form 驗證 Cookie 能互通,就可以做到 Web Form 登入寫入 .ASPXAUTH Cookie, 連上 MVC 時,MVC 解密 .ASPXAUTH Cookie 取出登入身分,即可實現 Web Form 與 MVC 共用登入,根本不需要自己搞 SSO。

跑完步迫不及查資料外加寫程式測試,嘿嘿嘿,還真的讓我做出來了!

先整理一些背景知識:

測試環境如下,IIS 站台有兩個網站專案,FormAuthWebForm 及 FormAuthMvc 分別為 Web Site Project 及 ASP.NET MVC。我將登入頁面放在 WebForm Login.aspx:

Login.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Login.aspx.cs" Inherits="Login" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <h3>WebForm</h3>
    <form id="form1" runat="server">
        <div>
            Account: 
            <asp:TextBox runat="server" id="txtUserId" text="Jeffrey"></asp:TextBox>
        </div>
        <div>
            Password:
            <asp:TextBox runat="server" ID="txtPasswd" TextMode="Password"></asp:TextBox>
        </div>
        <asp:Button runat="server" id="btnLogin" OnClick="btnLogin_OnClick" text="Login"/>
    </form>
</body>
</html>

Login.aspx.cs

using System;
using System.Web.Security;

public partial class Login : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }

    protected void btnLogin_OnClick(object sender, EventArgs e)
    {
        var userId = txtUserId.Text;
        var passwd = txtPasswd.Text;
        //展示用途,省略密碼驗證,直接登入
        FormsAuthentication.RedirectFromLoginPage(userId, false);
    }
}

AutoFormWebForm/web.config 要設成 Form 驗證。但有幾個注意事項:

  1. MVC 的 httpRuntime 使用 4.5.2,實測 WebForm 也要配合設成 <httpRuntime targetFramework="4.5.2" /> 才會通。 參考
  2. MVC 與 WebForm 必須使用相同 Machine Key,.ASPXAUTH Cookie 才能同時被兩邊解密驗證,故 Machine Key 不能自動產生,要在 web.config 加上 <machineKey ...> 指定。IIS 管理員有產生 Machine Key 的工具。
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.web>
    <compilation debug="false" targetFramework="4.5.2" />
    <httpRuntime targetFramework="4.5.2" />
    <authentication mode="Forms">
      <forms loginUrl="Login.aspx" />
    </authentication>
    <machineKey decryption="AES" decryptionKey="D1DD...省略..." validation="SHA1" validationKey="4134...省略..." />
    <authorization>
      <deny users="?" />
    </authorization>
    <pages controlRenderingCompatibilityVersion="4.0" />
  </system.web>
</configuration>

MVC 端也要修改 web.config。首先 machineKey 要跟 WebForm 網站一致,forms loginUrl 指向 /FormAuthWebForm/Login.aspx,defaultUrl 則指向自己的 Home/Index。

  <system.web>
    <compilation debug="true" targetFramework="4.5.2"/>
    <httpRuntime targetFramework="4.5.2"/>
    <authentication mode="Forms">
      <forms loginUrl="/FormAuthWebForm/Login.aspx" defaultUrl="~/Home/Index"></forms>
    </authentication>
    <<machineKey decryption="AES" decryptionKey="D1DD...省略..." validation="SHA1" validationKey="4134...省略..." />
    <authorization>
      <deny users="?"/>
    </authorization>
  </system.web>

在 Index.cshtml 加入一段程式驗證 MVC 端是否能抓到 WebForm 登入使用者名稱:


@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
<body>
<div>
    <h3>MVC</h3>
    Login User = @User.Identity.Name
</div>
</body>
</html>

實測結果:

掌握這個原則,意味著只要我們能確保 Cookie 可被讀取外加 Machine Key 一致,就能在多個 ASP.NET Web Application 間(不管是 MVC 還是 WebForm,即使在不同站台也可能,只要 Cookie 能互通)共享 Form 驗證登入身分。酷~

【資安提醒】採自行指定 Machine Key 時,decryptionKey 與 validationKey 需視為最高機密嚴密防護,一旦洩漏,惡意人士將可跳過 ASP.NET Form 驗證冒充任何登入帳號。延伸閱讀:MachineKey 外流有多可怕? 淺談 ASP.NET Form 驗證之破解與防護

Tutorial of how to setup to make ASP.NET WebFrom web site and MVC.


Comments

# by pico.chang

設定和圖片的validationKey不一致?

# by Jeffrey

to pico.chang,擷圖與web.config設定是測完模擬場景補拍的,卻不慎穿幫(羞)。已修改,感謝指正。

Post a comment


73 - 4 =