2011-08-23 94 views
0

我有一個MVC 2應用程序不會做自己的身份驗證,但會從HTTP請求標題中檢索用戶標識,因爲用戶必須先通過網關到達應用程序。ASP.NET MVC 2授權與網關頁面

一旦進入應用程序,我們需要將用戶ID與「用戶」表中的信息進行匹配,該表中包含應用程序使用的一些安全細節。

我很熟悉在ASP.NET中設置自定義成員資格和角色提供程序,但這種感覺非常不同,因爲用戶在通過網關應用程序後永遠不會看到登錄頁面。

問題:

  1. 我如何保存用戶ID,如果在所有?它從請求頭開始,但是我必須將它放入cookie中嗎? SessionState怎麼樣?
  2. 我何時/何時獲得此信息?母版頁顯示用戶的名字,所以它應該在任何地方都可用。

如果可能,我還想在控制器中使用[Authorize(Roles="...")]標記。

+0

這整個計劃似乎沒有安全感。如果你不關心安全,我想這很好。對公司內部的人來說,欺騙標題並不需要太多。儘管如此,授權​​只是查看IPrincipal,因此您可以執行自己的IPrincipal以從頭中獲取用戶名並將IsAuthenticate設置爲true。 –

+0

這是我們客戶的環境,而不是我們的,所以我假設他們設置正確,並且像Bob Yexley所說的那樣安全。客戶告訴我們,這就是我們的應用程序應該如何對用戶進行身份驗證,所以我只對MVC或ASP.NET的具體信息感興趣。 –

回答

2

我的工作環境非常類似。正如@Mystere Man提到的那樣,這種設置存在風險,但是如果整個基礎設施設置並正確運行,我們發現它是一個安全的設置(我們的關心安全)。需要確保的一件事是,SiteMinder代理正在您嘗試保護的IIS節點上運行,因爲它將驗證也傳遞到頭文件中的加密的SMSESSION密鑰,這將使請求安全(這將非常困難欺騙SMSESSION標頭的值)。

我們正在使用ASP.NET MVC3,它具有全局操作過濾器,這就是我們正在使用的。但是對於MVC2,您可以創建一個普通的控制器級別操作篩選器,該篩選器可以應用於基本控制器類,以便您的所有控制器/操作都將得到保護。

我們創建了一個自定義配置部分,允許我們通過web.config打開和關閉此安全過濾器。如果關閉,我們的配置部分具有的屬性將允許您「模擬」具有給定角色的給定用戶,以用於測試和調試目的。這個配置部分還允許我們在配置中存儲我們要查找的標題鍵值,以防供應商更改我們的標題鍵名稱。

public class SiteMinderConfiguration : ConfigurationSection 
{ 
    [ConfigurationProperty("enabled", IsRequired = true)] 
    public bool Enabled 
    { 
     get { return (bool)this["enabled"]; } 
     set { this["enabled"] = value; } 
    } 

    [ConfigurationProperty("redirectTo", IsRequired = true)] 
    public RedirectToElement RedirectTo 
    { 
     get { return (RedirectToElement)this["redirectTo"]; } 
     set { this["redirectTo"] = value; } 
    } 

    [ConfigurationProperty("sessionCookieName", IsRequired = true)] 
    public SiteMinderSessionCookieNameElement SessionCookieName 
    { 
     get { return (SiteMinderSessionCookieNameElement)this["sessionCookieName"]; } 
     set { this["sessionCookieName"] = value; } 
    } 

    [ConfigurationProperty("userKey", IsRequired = true)] 
    public UserKeyElement UserKey 
    { 
     get { return (UserKeyElement)this["userKey"]; } 
     set { this["userKey"] = value; } 
    } 

    [ConfigurationProperty("rolesKey", IsRequired = true)] 
    public RolesKeyElement RolesKey 
    { 
     get { return (RolesKeyElement)this["rolesKey"]; } 
     set { this["rolesKey"] = value; } 
    } 

    [ConfigurationProperty("firstNameKey", IsRequired = true)] 
    public FirstNameKeyElement FirstNameKey 
    { 
     get { return (FirstNameKeyElement)this["firstNameKey"]; } 
     set { this["firstNameKey"] = value; } 
    } 

    [ConfigurationProperty("lastNameKey", IsRequired = true)] 
    public LastNameKeyElement LastNameKey 
    { 
     get { return (LastNameKeyElement)this["lastNameKey"]; } 
     set { this["lastNameKey"] = value; } 
    } 

    [ConfigurationProperty("impersonate", IsRequired = false)] 
    public ImpersonateElement Impersonate 
    { 
     get { return (ImpersonateElement)this["impersonate"]; } 
     set { this["impersonate"] = value; } 
    } 
} 

public class SiteMinderSessionCookieNameElement : ConfigurationElement 
{ 
    [ConfigurationProperty("value", IsRequired = true)] 
    public string Value 
    { 
     get { return (string)this["value"]; } 
     set { this["value"] = value; } 
    } 
} 

public class RedirectToElement : ConfigurationElement 
{ 
    [ConfigurationProperty("loginUrl", IsRequired = false)] 
    public string LoginUrl 
    { 
     get { return (string)this["loginUrl"]; } 
     set { this["loginUrl"] = value; } 
    } 
} 

public class UserKeyElement : ConfigurationElement 
{ 
    [ConfigurationProperty("value", IsRequired = true)] 
    public string Value 
    { 
     get { return (string)this["value"]; } 
     set { this["value"] = value; } 
    } 
} 

public class RolesKeyElement : ConfigurationElement 
{ 
    [ConfigurationProperty("value", IsRequired = true)] 
    public string Value 
    { 
     get { return (string)this["value"]; } 
     set { this["value"] = value; } 
    } 
} 

public class FirstNameKeyElement : ConfigurationElement 
{ 
    [ConfigurationProperty("value", IsRequired = true)] 
    public string Value 
    { 
     get { return (string)this["value"]; } 
     set { this["value"] = value; } 
    } 
} 

public class LastNameKeyElement : ConfigurationElement 
{ 
    [ConfigurationProperty("value", IsRequired = true)] 
    public string Value 
    { 
     get { return (string)this["value"]; } 
     set { this["value"] = value; } 
    } 
} 

public class ImpersonateElement : ConfigurationElement 
{ 
    [ConfigurationProperty("username", IsRequired = false)] 
    public UsernameElement Username 
    { 
     get { return (UsernameElement)this["username"]; } 
     set { this["username"] = value; } 
    } 

    [ConfigurationProperty("roles", IsRequired = false)] 
    public RolesElement Roles 
    { 
     get { return (RolesElement)this["roles"]; } 
     set { this["roles"] = value; } 
    } 
} 

public class UsernameElement : ConfigurationElement 
{ 
    [ConfigurationProperty("value", IsRequired = true)] 
    public string Value 
    { 
     get { return (string)this["value"]; } 
     set { this["value"] = value; } 
    } 
} 

public class RolesElement : ConfigurationElement 
{ 
    [ConfigurationProperty("value", IsRequired = true)] 
    public string Value 
    { 
     get { return (string)this["value"]; } 
     set { this["value"] = value; } 
    } 
} 

所以我們的web.config文件看起來像這樣

<configuration> 
    <configSections> 
    <section name="siteMinderSecurity" type="MyApp.Web.Security.SiteMinderConfiguration, MyApp.Web" /> 
    ... 
    </configSections> 
    ... 
    <siteMinderSecurity enabled="false"> 
    <redirectTo loginUrl="https://example.com/login/?ReturnURL={0}"/> 
    <sessionCookieName value="SMSESSION"/> 
    <userKey value="SM_USER"/> 
    <rolesKey value="SN-AD-GROUPS"/> 
    <firstNameKey value="SN-AD-FIRST-NAME"/> 
    <lastNameKey value="SN-AD-LAST-NAME"/> 
    <impersonate> 
     <username value="ImpersonateMe" /> 
     <roles value="Role1, Role2, Role3" /> 
    </impersonate> 
    </siteMinderSecurity> 
    ... 
</configuration> 

我們有一個自定義SiteMinderIdentity ...

public class SiteMinderIdentity : GenericIdentity, IIdentity 
{ 
    public SiteMinderIdentity(string name, string type) : base(name, type) { } 
    public IList<string> Roles { get; set; } 
} 

和一個自定義SiteMinderPrincipal ...

public class SiteMinderPrincipal : GenericPrincipal, IPrincipal 
{ 
    public SiteMinderPrincipal(IIdentity identity) : base(identity, null) { } 
    public SiteMinderPrincipal(IIdentity identity, string[] roles) : base(identity, roles) { } 
} 

而我們填入HttpContext.Current.UserThread.CurrentPrincipal,它們的實例是SiteMinderPrincipal,我們根據我們從操作篩選器中的請求標頭獲取的信息構建了該實例。

public class SiteMinderSecurity : ActionFilterAttribute 
{ 
    public override void OnActionExecuting(ActionExecutingContext filterContext) 
    { 
     base.OnActionExecuting(filterContext); 

     var request = filterContext.HttpContext.Request; 
     var response = filterContext.HttpContext.Response; 

     if (MyApp.SiteMinderConfig.Enabled) 
     { 
      string[] userRoles = null; // default to null 
      userRoles = Array.ConvertAll(request.Headers[MyApp.SiteMinderConfig.RolesKey.Value].Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries), r => r.Trim()); 

      var identity = new SiteMinderIdentity(request.Headers[MyApp.SiteMinderConfig.UserKey.Value];, "SiteMinder"); 
      if (userRoles != null) 
       identity.Roles = userRoles.ToList(); 
      var principal = new SiteMinderPrincipal(identity, userRoles); 

      HttpContext.Current.User = principal; 
      Thread.CurrentPrincipal = principal; 
     } 
     else 
     { 
      var roles = Array.ConvertAll(MyApp.SiteMinderConfig.Impersonate.Roles.Value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries), r => r.Trim()); 
      var identity = new SiteMinderIdentity(MyApp.SiteMinderConfig.Impersonate.Username.Value, "SiteMinder") { Roles = roles.ToList() }; 
      var principal = new SiteMinderPrincipal(identity, roles); 

      HttpContext.Current.User = principal; 
      Thread.CurrentPrincipal = principal; 
     } 
    } 
} 

MyApp是被那個緩存的配置信息,所以我們不會在每次請求從web.config中讀取它的應用程序啓動時初始化靜態類...

public static class MyApp 
{ 
    private static bool _isInitialized; 
    private static object _lock; 

    static MyApp() 
    { 
     _lock = new object(); 
    } 

    private static void Initialize() 
    { 
     if (!_isInitialized) 
     { 
      lock (_lock) 
      { 
       if (!_isInitialized) 
       { 
        // Initialize application version number 
        _version = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).FileVersion; 
        _siteMinderConfig = (SiteMinderConfiguration)ConfigurationManager.GetSection("siteMinderSecurity"); 

        _isInitialized = true; 
       } 
      } 
     } 
    } 

    private static string _version; 
    public static string Version 
    { 
     get 
     { 
      Initialize(); 
      return _version; 
     } 
    } 

    private static SiteMinderConfiguration _siteMinderConfig; 
    public static SiteMinderConfiguration SiteMinderConfig 
    { 
     get 
     { 
      Initialize(); 
      return _siteMinderConfig; 
     } 
    } 
} 

從我收集你的情況,你有一個數據庫中的信息,你需要根據標題中的信息來查找,以得到你需要的一切,所以這不會是你需要的,但它至少應該至少應該讓你開始。

希望這會有所幫助。

+0

非常感謝徹底的答案!我特別喜歡web.config中的模擬選項,以及我必須查找的Multithreaded Singleton模式(http://msdn.microsoft.com/zh-cn/library/ff650316.aspx)。這種解決方案非常適合我們的情況。 –

+0

目前,我有一個HTTP模塊在OnAuthorization事件中做類似的事情。在OnActionExecuting中使用Action Filter還是使用Action Filter而不是HTTP Module有什麼好處? –

+0

@bob,我很想知道在過去的一年中你是否改變了方法,還有你在IIS配置和web.config中必須做些什麼才能使其發揮作用。換句話說,這個策略仍然有效嗎? – MikeC