ASP.NET MVC 2 Авторизация со страницей шлюза - PullRequest
0 голосов
/ 23 августа 2011

У меня есть приложение MVC 2, которое не будет выполнять свою собственную аутентификацию, но будет извлекать идентификатор пользователя из заголовка HTTP-запроса, так как пользователи должны пройти через шлюз до достижения приложения.

Попав в приложение, мы должны сопоставить идентификатор пользователя с информацией в таблице «users», которая содержит некоторые сведения о безопасности, которые использует приложение.

Я знаком с настройкой настраиваемых поставщиков членства и ролей в ASP.NET, но это выглядит по-разному, поскольку пользователь никогда не должен видеть страницу входа в систему сразу после приложения шлюза.

Вопросы:

  1. Как мне сохранить идентификатор пользователя, если он вообще есть? Он начинается в заголовке запроса, но мне нужно поместить его в файл cookie? Как насчет SessionState?
  2. Где / когда я могу получить эту информацию? На главной странице отображается имя пользователя, поэтому оно должно быть доступно везде.

Я бы хотел использовать тег [Authorize(Roles="...")] в моем контроллере, если это возможно.

1 Ответ

2 голосов
/ 26 августа 2011

У нас очень похожая настройка, где я работаю.Как отметил @Mystere Man, с этой настройкой есть риски, но если вся инфраструктура настроена и работает правильно, мы обнаружили, что это безопасная установка (мы делаем заботимся о безопасности).Необходимо убедиться в том, что агент SiteMinder работает на узле IIS, который вы пытаетесь защитить, поскольку он проверяет зашифрованный ключ SMSESSION, также переданный в заголовках, что сделает запросы безопасными (это будет чрезвычайно сложноподделать значение заголовка SMSESSION).

Мы используем ASP.NET MVC3, который имеет фильтры глобальных действий, что мы и используем.Но с MVC2 вы можете создать обычный фильтр действий на уровне контроллера, который можно применить к базовому классу контроллера, чтобы обеспечить защиту всех ваших контроллеров / действий.

Мы создали специальный раздел конфигурации, который позволяетнам включить и отключить этот фильтр безопасности через web.config.Если он отключен, в нашем разделе конфигурации есть свойства, которые позволят вам «выдавать себя» за данного пользователя с заданными ролями для целей тестирования и отладки.Этот раздел конфигурации также позволяет нам хранить значения ключей заголовка, которые мы ищем, также в 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; }
    }
}

Итак, наша сеть.конфиг выглядит примерно так

<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.User и Thread.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;
        }
    }
}

Из того, что я понял из вашей ситуации, у вас есть информация вбаза данных, которую вам нужно искать, основываясь на информации в заголовках, чтобы получить все, что вам нужно, так что это будет не совсем то, что вам нужно, но, похоже, это должно, по крайней мере, заставить вас

Надеюсь, это поможет.

...