2011-08-23 98 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