使用Gateway页面进行ASP.NET MVC 2授权

时间:2011-08-23 17:54:33

标签: asp.net-mvc-2 asp.net-membership

我有一个MVC 2应用程序,它不会进行自己的身份验证,但会从HTTP请求标头中检索用户ID,因为用户必须在到达应用程序之前通过网关。

进入应用程序后,我们需要将用户ID与“用户”表中的信息进行匹配,其中包含应用程序使用的一些安全性详细信息。

我熟悉在ASP.NET中设置自定义成员资格和角色提供程序,但这种感觉非常不同,因为用户永远不会在网关应用程序之后看到登录页面。

问题:

  1. 如果有的话,如何保留用户ID?它从请求标头开始,但是我必须将它放在cookie中吗? SessionState怎么样?
  2. 我/何时获得此信息?母版页显示用户的名称,因此应该随处可用。
  3. 如果可能,我还想在控制器中使用[Authorize(Roles="...")]标记。

1 个答案:

答案 0 :(得分: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;
        }
    }
}

从我收集的情况来看,您在数据库中有信息需要根据标题中的信息进行查找以获得您需要的所有信息,因此这不是您所需要的,但它似乎至少应该让你开始。

希望这有帮助。