Richie_W Ответов: 1

Как сохранить/передать сеанс httpwebrequest, инициированный на основе файлов cookie, в веб-браузер


Я написал контроллер MVC для гораздо более крупного веб-приложения, которое облегчает интеграцию SSO с бизнес-партнером с помощью SAML 2.0.
Пользователи веб-приложения должны сначала пройти проверку подлинности в Active Directory, прежде чем получить доступ.

Приложение просто извлекает дополнительные данные из объекта AD для пользователя, который в данный момент вошел в систему, затем использует эти данные для создания утверждения SAML, а затем отправляет его на Url-адрес поставщика SSO, используя следующий код:

/* Build & Post Assertion */
string _ssoPostData = _sso.GenerateRequest();

/* Send Data to Provider */
_request = (HttpWebRequest)WebRequest.Create(_sso.Recipient);
_request.CookieContainer = _cookieContainer;
_request.AllowAutoRedirect = true;
_request.UserAgent = "Identity Provider Client";
_request.Method = "POST";
_request.ContentType = "application/x-www-form-urlencoded";
_request.ContentLength = _ssoPostData.Length;

Stream _requestStream = _request.GetRequestStream();
_requestStream.Write(System.Text.Encoding.UTF8.GetBytes(_ssoPostData), 0, _ssoPostData.Length);

_response = (HttpWebResponse)_request.GetResponse();


Проблема, которую я пытаюсь решить, заключается в следующем: когда WebRequest выполнен и установлено соединение с сервером, сервер назначает JSESSIONID для этого соединения. Полученный ответ содержит целевой URL-адрес, на который пользователь должен быть перенаправлен, а также 2 файла cookie, один со значением JSESSIONID, а другой со значением USERID, которые должны быть возвращены при каждом последующем вызове сервера.
Поэтому мой вопрос заключается в том, есть ли способ передать сеанс/файлы cookie, созданные при установке веб-запроса/сеанса на сервер единого входа, и передать этот сеанс обратно в браузер, из которого было запущено веб-приложение?

Любая помощь очень ценится.

Спасибо,
Ричард

Что я уже пробовал:

Я пробовал использовать
Response.Redirect()
с Url-адресом, возвращенным вызовом службы SSO, но он все равно делает сеанс недействительным и возвращает меня на страницу входа в систему служб.

F-ES Sitecore

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

Richie_W

Спасибо F-ES Sitecore.

Я думаю, что, возможно, не очень ясно объяснил, что я пытаюсь сделать. На самом деле это не что иное, как инициированный ВПЛ сеанс единого входа.

Пользователи моей компании будут клиентами, а наш рекламный сервер будет предоставлять услуги IdP.

Предполагаемый рабочий процесс будет выглядеть следующим образом:

1. Один из наших пользователей первым делом приходит в офис и входит в свою рабочую станцию со своими пользовательскими кредитами.

2. Затем они открывают свой браузер, который по умолчанию находится на странице интрасети компании, и на этой странице есть кнопка с надписью "подключиться к порталу делового партнера"

3. пользователь нажимает кнопку, которая вызывает мою веб-страницу, которая запрашивает объявление и извлекает дополнительную информацию для генерации утверждения SAML.

4. веб-страница создает экземпляр нового объекта HttpWebRequest и использует его для публикации запроса SAML, на который затем отвечает служба потребления SSO нашего делового партнера. Прежде чем будет сгенерирован ответ, сервис проверяет запрос и, если он успешно проверен, отправляет обратно сеансовые файлы cookie, упомянутые в моем первоначальном вопросе, а также целевой URL-адрес места, куда пользователь попадает после успешного входа в свою систему.

Именно эта последняя часть, с которой у меня возникли проблемы, берет данные, возвращаемые HttpWebRequest, и каким-то образом передает этот сеанс браузеру, который пользователь изначально запустил страницу интрасети с помощью кнопки, запустившей этот процесс.

Я не уверен, что это имеет какое-то значение, но я подумал, что буду немного более многословным.

Я также включил код для своей страницы, которая вызывается из кнопки интрасети:

   public partial class sso : System.Web.UI.Page
    {
        [DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool InternetGetCookieEx(
           string url,
           string cookieName,
           StringBuilder cookieData,
           ref int size,
           Int32 dwFlags,
           IntPtr lpReserved);

        [DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool InternetSetCookie(
            string lpszUrlName,
            string lpszCookieName,
            string lpszCookieData);

        private const Int32 InternetCookieHttponly = 0x2000;

        protected void Page_Load(object sender, EventArgs e)
        {
            SSO _sso;
            UserPrincipal _user;

            HttpWebRequest _request = null;
            HttpWebResponse _response = null;
            CookieContainer _cookieContainer = new CookieContainer();

            string _userName = Environment.UserName;
            string _domain = Environment.UserDomainName;

            string _destUrl = String.Empty;

            /* Pull local credentials for logged in user and display it*/
            using (PrincipalContext _principalContext = new PrincipalContext(ContextType.Domain, "MYCOMPANY"))
            {
                _user = UserPrincipal.FindByIdentity(_principalContext, _userName);

                if (_user != null)
                {
                    _sso = new SSO();
                    _sso.Recipient = "https://www.business-partner.com/customer/login/sso";
                    _sso.RecipientNameQualifier = "tga";
                    _sso.Issuer = "https://idp.mycompany.com";
                    _sso.Domain = "mycompany.com";
                    _sso.Subject = _user.EmailAddress;
                    _sso.CertificateStoreLocation = null;
                    _sso.CertificateStoreName = null;
                    _sso.CertificateFindType = null;
                    _sso.CertificateFindValue = null;
                    _sso.CertificateFile = System.Configuration.ConfigurationManager.AppSettings["SSOCERT"];
                    _sso.CertificatePassword = "SuperSecurePassword123";
                    _sso.Attributes.Add("uid", _user.DisplayName);
                    _sso.Attributes.Add("mail", _user.EmailAddress);

Richie_W

                    _sso.SignatureType = SigningHelper.SignatureType.Response;
                    _sso.RelayState = "";

                    /* Build & Post Assertion */
                    string _ssoPostData = _sso.GenerateRequest();

                    /* Send Data to Business Partner */

                    _request = (HttpWebRequest)WebRequest.Create(_sso.Recipient);
                    _request.CookieContainer = _cookieContainer;
                    _request.AllowAutoRedirect = true;
                    _request.UserAgent = "My Company's Identity Provider Client";
                    _request.Method = "POST";
                    _request.ContentType = "application/x-www-form-urlencoded";
                    _request.ContentLength = _ssoPostData.Length;

                    Stream _requestStream = _request.GetRequestStream();
                    _requestStream.Write(System.Text.Encoding.UTF8.GetBytes(_ssoPostData), 0, _ssoPostData.Length);

                    _response = (HttpWebResponse)_request.GetResponse();
                    Stream _responseStream = _response.GetResponseStream();

                    Response.Cookies.Clear();

                    CookieCollection _cookieCollection = GetCookies(_cookieContainer);
                    foreach (Cookie _netCookie in _cookieCollection)
                    {
                        InternetSetCookie(_response.ResponseUri.ToString(), null, _netCookie.ToString());

                        /* Convert from NetCookie to HttpCookie */
                        HttpCookie _httpCookie = new HttpCookie(_netCookie.Name);
                        foreach (string value in _netCookie.Value.Split('&'))
                        {
                            string[] val = value.Split('=');
                            if (val.Count() == 1)
                            {
                                _httpCookie.Values.Add(_netCookie.Name, val[0]);
                            }
                            else if(val.Count() == 2)
                            {
                                _httpCookie.Values.Add(val[0], val[1]);
                            }
                        }

                        /* Copy NetCookie properties to HttpCookie */
                        _httpCookie.Domain = _netCookie.Domain;
                        _httpCookie.Expires = _netCookie.Expires;
                        _httpCookie.HttpOnly = _netCookie.HttpOnly;
                        _httpCookie.Path = _netCookie.Path;
                        _httpCookie.Secure = _netCookie.Secure;

                        /* Append cookie to Response object */
                        Response.Cookies.Add(_httpCookie);  
                    }

                    Response.Redirect(_response.ResponseUri.ToString());
                }
            }
        }
        private CookieCollection GetCookies(CookieContainer container)
        {
            var allCookies = new CookieCollection();
            var domainTableField = container.GetType().GetRuntimeFields().FirstOrDefault(x => x.Name == "m_domainTable");
            var domains = (IDictionary)domainTableField.GetValue(container);

            foreach (var val in domains.Values)
            {
                var type = val.GetType().GetRuntimeFields().First(x => x.Name == "m_list");
                var values = (IDictionary)type.GetValue(val);
                foreach (CookieCollection cookies in values.Values)
                {
                    allCookies.Add(cookies);
                }
            }
            return allCookies;
        }
        public static CookieContainer GetUriCookieContainer(Uri uri)
        {
            CookieContainer cookies = null;
            // Determine the size of the cookie
            int datasize = 8192 * 16;
            StringBuilder cookieData = new StringBuilder(datasize);
            if (!InternetGetCookieEx(uri.ToString

Richie_W

, null, cookieData, ref datasize, InternetCookieHttponly, IntPtr.Zero))
            {
                if (datasize < 0)
                    return null;
                // Allocate stringbuilder large enough to hold the cookie
                cookieData = new StringBuilder(datasize);
                if (!InternetGetCookieEx(
                    uri.ToString(),
                    null, cookieData,
                    ref datasize,
                    InternetCookieHttponly,
                    IntPtr.Zero))
                    return null;
            }
            if (cookieData.Length > 0)
            {
                cookies = new CookieContainer();
                cookies.SetCookies(uri, cookieData.ToString().Replace(';', ','));
            }
            return cookies;
        }


    }



Любое дополнительное понимание или направление приветствуется.

Спасибо,
Ричард

F-ES Sitecore

Вы должны помнить, что файлы cookie привязаны к домену. Если ваши пользователи получают доступ к вашей интрасети, которая затем выполняет аутентификацию с другим веб-сайтом и возвращает файлы cookie, то если вы возвращаете эти файлы cookie в свой клиентский браузер, то эти файлы cookie прикрепляются к вашему домену интрасети. Когда этот пользователь затем связывается с любым другим сайтом, который использует эти файлы cookie для аутентификации, браузер *не* отправляет их, поскольку эти файлы cookie прикреплены к вашей интрасети, а не к сайту, который пользователь сейчас просматривает. Вы не можете установить файлы cookie на клиенте для использования доменами, отличными от того, который возвращает файлы cookie.

Richie_W

Спасибо, Ф-Эс. Во-первых, позвольте мне поблагодарить вас за то, что вы нашли время помочь мне. Я не получил никакой обратной связи от своих постов на других сайтах, поэтому очень ценю ваше время.

Поэтому, прежде чем начать, я собрал POC с тем же подходом, но с использованием элемента управления WebBrowser. Это действительно дало результаты, которые я надеялся получить от вышеприведенного подхода. Возможно, я упускаю из виду то, что не совсем понимаю, но надеюсь, что вы поможете мне понять.

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

Но в любом случае, вот код:

публичный частичный класс FormMain : Form
{
public FormMain()
{
метод InitializeComponent();
}

частная FormMain_Load недействительным(объект отправителя, EventArgs в электронной)
{
объект WebBrowser.Url = новый Uri("http://www.google.com");
}

частная btnLogin_Click недействительным(объект отправителя, EventArgs в электронной)
{
ССО _sso;
Метод userprincipal _user;

HttpWebRequest _request = null;
Класс httpwebresponse _response = нуль;
CookieContainer _cookieContainer = новый CookieContainer();

строка _userName = Environment.имя пользователя;
строка _domain = окружающая среда.UserDomainName;

string _destUrl = строка.Пустой;

/* Извлеките локальные учетные данные для вошедшего в систему пользователя и отобразите их*/
using (PrincipalContext _principalContext = new PrincipalContext(ContextType.Domain, "MYCOMPANY"))
{
_user = метод userprincipal.FindByIdentity(_principalContext, _userName);

если (_user != нуль)
{
_sso = новый SSO();
_sso.Recipient = "https://www.business-partner.com/customer/login/sso";
_sso.RecipientNameQualifier = "ТГА";
_sso.Эмитент = "https://idp.mycompany.com";
_sso.Domain = "mycompany.com";
_sso.Subject = _user.Адрес электронной почты;
_sso.CertificateStoreLocation = нуль;
_sso.CertificateStoreName = нуль;
_sso.CertificateFindType = нуль;
_sso.CertificateFindValue = нуль;
_sso.CertificateFile = система.Конфигурация.Конфигурационный менеджер.AppSettings["SSOCERT"];
_sso.CertificatePassword = "SuperSecurePassword123";
_sso.атрибуты.Добавить("uid", _user.значение DisplayName);
_sso.атрибуты.Добавить("почта", _user.Адрес электронной почты);
_sso.SignatureType = SigningHelper.SignatureType.Ответ;
_sso.RelayState = "";

/* Создайте &ампер; после утверждения */
строка _ssoPostData = _sso.GenerateRequest();

/* Отправка данных деловому партнеру */
_request = (HttpWebRequest)WebRequest.Создать(_sso.Recipient);
_запрос.CookieContainer = _cookieContainer;
_запрос.AllowAutoRedirect = true;
_запрос.UserAgent = "клиент поставщика удостоверений личности моей компании";
_запрос.Метод = "сообщение";
_запрос.ContentType = "application/x-www-form-urlencoded";
_запрос.ContentLength = _ssoPostData.Длина;

_requestStream трансляция = _request.Метод getrequeststream();
_requestStream.Писать(Системы.Текст.Кодирование.Кодировке utf8.GetBytes(_ssoPostData), 0, _ssoPostData.Длина);

_response = (класс httpwebresponse)_request.Творческий();
_responseStream трансляция = _response.GetResponseStream();

CookieCollection _cookieCollection = GetCookies(_cookieContainer);

Richie_W

по каждому элементу (_netCookie печенье в _cookieCollection)
{
Программа.InternetSetCookie(_response.Ответьте мне.ToString(), null, _netCookie.Метод toString());
}

объект WebBrowser.ScriptErrorsSuppressed = true;
объект WebBrowser.Навигация(_response.ResponseUri);
}
}
}

private CookieCollection GetCookies(контейнер CookieContainer)
{
var allCookies = новая коллекция CookieCollection();
var domainTableField = контейнер.GetType().GetRuntimeFields().FirstOrDefault(x => x.Name == "m_domainTable");
var domains = (IDictionary)domainTableField.GetValue(контейнер);

foreach (var val в доменах.Ценности)
{
тип var = val.GetType().GetRuntimeFields().First(x => x.Name == "m_list");
var values = (IDictionary)type.GetValue(val);
foreach (CookieCollection cookies в значениях.Ценности)
{
allCookies.Добавить(cookies);
}
}
верните allCookies;
}

частная webBrowser_Navigated недействительным(объект отправителя, WebBrowserNavigatedEventArgs е)
{
if(txtAddressBar.Текст != е.URL-адрес.Метод toString())
{
txtAddressBar.Текст = е.URL-адрес.Метод toString();
}
}

частная btnBrowserGo_Click недействительным(объект отправителя, EventArgs в электронной)
{
если(!строка.IsNullOrEmpty(txtAddressBar.Text))
{
объект WebBrowser.Навигация(txtAddressBar.Text);
}
}
}

Еще раз спасибо за вашу помощь.

Ричард

F-ES Sitecore

С помощью этого кода Вы вводите файлы cookie в элемент управления браузером для стороннего домена, а затем получаете этот элемент управления для перехода по url-адресу, и он будет использовать эти файлы cookie. Вы можете сделать это, потому что вы используете элемент управления браузера в настольном приложении, вы не связаны интернет-безопасностью.

Когда вы пишете один и тот же код через веб-сайт, любые файлы cookie, которые вы пишете клиенту, отправляются в домен запрашиваемой страницы, вот в чем разница. Вы отправляете куки обратно, говоря: "этот куки исходит из этого домена", поэтому, когда браузер получает доступ businss-partner.com у него нет никаких файлов cookie для отправки, но в вашем настольном приложении вы говорите webcontrol: "эти файлы cookie от business-partner.com так что в следующий раз, когда вы зайдете на этот сайт, отправьте их". Вы можете сделать это в настольном приложении, которым управляете, но вы не можете сделать это через интернет, поскольку это просто запрещено, браузеры написаны таким образом, что они ведут себя и подчиняются правилам.

Richie_W

Спасибо Ф-Эс! В этом есть смысл.

И спасибо за понимание поведения браузера. Сегодня я узнал кое-что новое. :)

1 Ответов

Рейтинг:
0

Richie_W

Спасибо Ф-Эс! В этом есть смысл.

И спасибо за понимание поведения браузера. Сегодня я узнал кое-что новое. :)