Как подключиться к удаленному URL-адресу, который требует проверки подлинности на основе форм Spring Security (Java)? - PullRequest
1 голос
/ 24 февраля 2011

Я искал и искал, но не могу найти ответ на то, что кажется простым сценарием аутентификации.

У нас есть веб-приложение на Java, которое использует авторизацию на основе форм, предоставляемую Spring. Мы пытаемся получить доступ к этому приложению через наш портал, не требуя от пользователя ввода его учетных данных (SSO).

Портал имеет хранилище учетных данных, и мы можем успешно получить доступ к секретам удаленного веб-приложения на стороне сервера. Мы используем утилиту Apache HTTP Components для отправки запроса на вход в систему j_spring_security_check и успешно проводим аутентификацию. Ответ на это сообщение отправляет перенаправление 302 на домашнюю страницу приложения и устанавливает файл cookie с идентификатором сеанса.

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

Итак, как мы аутентифицируем на стороне сервера и все еще можем загружать целевую страницу на стороне клиента?

Я относительно новичок в этом, поэтому я прошу прощения, если это глупый вопрос. Приветствуется любая помощь или совет относительно альтернативного подхода.

Примечания:


HttpComponent Код клиента:

DefaultHttpClient httpclient = new DefaultHttpClient();
    try {
        // try to get the home page
        HttpGet httpget = new HttpGet("http://<host>/<root>/home.action");
        HttpResponse httpClientResponse = httpclient.execute(httpget);
        HttpEntity entity = httpClientResponse.getEntity();

        // check status and close entity stream
        System.out.println("Login form get: " + httpClientResponse.getStatusLine());
        EntityUtils.consume(entity);

        // check cookies
        System.out.println("Initial set of cookies:");
        List<Cookie> cookies = httpclient.getCookieStore().getCookies();
        printCookies(cookies);

        /***  Login ***/
        HttpPost httppost = new HttpPost("http://<host>/<root>/j_spring_security_check");

        // Prepare post parameters
        List <NameValuePair> nvps = new ArrayList <NameValuePair>();
        nvps.add(new BasicNameValuePair("j_username", getUserFromVault()));
        nvps.add(new BasicNameValuePair("j_password", getPasswordFromVault()));
        httppost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));

        httpClientResponse = httpclient.execute(httppost);

        // copy response headers and determine redirect location
        Header[] allHeaders = httpClientResponse.getAllHeaders();
        System.out.println("Headers: ");
        String location = "";
        for (Header header : allHeaders) {
            System.out.println(header);
            if("location".equalsIgnoreCase(header.getName())) location = header.getValue();
            response.addHeader(header.getName(), header.getValue());
        }

        // check response body
        entity = httpClientResponse.getEntity();
        System.out.println("Response content: " + httpClientResponse.getStatusLine());
        System.out.println(EntityUtils.toString(entity)); // always empty
        EntityUtils.consume(entity);

        // check cookies
        System.out.println("Post logon cookies:");
        cookies = httpclient.getCookieStore().getCookies();
        printCookies(cookies);

        // populate redirect information in response
        System.out.println("Redirecting to: " + locationHeaderValue);
        response.setStatus(httpClientResponse.getStatusLine().getStatusCode()); // 302

        // test if server-side get works for home page at this point (it does)
        httpget = new HttpGet(location);
        httpClientResponse = httpclient.execute(httpget);
        entity = httpClientResponse.getEntity();

        // print response body (all home content is loaded)
        System.out.println("home get: " + httpClientResponse.getStatusLine());
        System.out.println("Response content: " + httpClientResponse.getStatusLine());
        System.out.println(EntityUtils.toString(entity));
        EntityUtils.consume(entity);

    } finally {
        httpclient.getConnectionManager().shutdown();
    }

Заголовки, возвращенные после успешного входа в систему на стороне сервера:

HTTP/1.1 302 Found
Date: Wed, 23 Feb 2011 22:09:03 GMT
Server: Apache/2.2.3 (CentOS)
Set-Cookie: JSESSIONID=6F98B0B9A65BA6AFA0472714A4C816E5; Path=<root>
Location: http://<host>/<root>/home.action
Content-Type: text/plain; charset=UTF-8
Content-Length: 0
Via: 1.1 PPWebFilter.<host>:80 (IronPort-WSA/7.0.0-825)
Connection: keep-alive

Заголовки из клиентского запроса и ответа:
Запрос:

GET /<root>/home.action HTTP/1.1  
Host: <host>  
Connection: keep-alive  
Referer: http://localhost:10039/SCMViewer/TestLoginServlet?launchScm=Launch+SCM+servlet  
Accept:application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5  
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.98 Safari/534.13  
Accept-Encoding: gzip,deflate,sdch  
Accept-Language: en-US,en;q=0.8  
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3  
Cookie: JSESSIONID=FC8E823AB1A1545BE8518DB4D097E665  

Ответ (перенаправить на логин):

HTTP/1.1 302 Found
Date: Wed, 23 Feb 2011 22:09:03 GMT
Server: Apache/2.2.3 (CentOS)
Location: http://<host>/<root>/security/login.action
Content-Type: text/plain; charset=UTF-8
Content-Length: 0
Via: 1.1 PPWebFilter.<host>:80 (IronPort-WSA/7.0.0-825)
Connection: keep-alive

В качестве теста мы написали небольшой взлом, который, кажется, работает, но слишком небезопасен, чтобы быть жизнеспособным:

  • Внедрил форму в jsp, которая будет отправлять учетные данные для входа непосредственно в j_spring_security_check удаленного сайта.
  • Написал метод сервлета для получения учетных данных из хранилища.
  • Заполнил учетные данные на стороне клиента в скрытых полях формы и отправил форму через javascript.

Ответы [ 2 ]

1 голос
/ 07 апреля 2011

В случае, если кому-то интересно, вот как все получилось.

Как только мы осознали проблему с установкой иностранных куки, мы решили, что у нас есть несколько вариантов:

  1. Прокси - туннель через портал к удаленному приложению, используя портал в качестве прокси.Этот вариант является наиболее простым с точки зрения логики, но он имеет сложности, как упомянуто выше (т. Е. Вы должны изменить каждый запрос и каждый ответ - добавив файлы cookie и разметку по мере необходимости).Этот метод оказался для нас болезненной точкой, не связанной с нашим использованием IBM WebSphere Portal 7.
  2. Решение SSO стороннего производителя - Используйте CAS или Tivoli или какое-либо другое корпоративное решение.Это наше идеальное окончательное решение, но оно все еще исследуется для определения совместимости с нашей средой.
  3. Cookie Monster - Наше временное решение для того, чтобы вывести портал IBM изпосредник должен был развернуть небольшое новое удаленное приложение на том же сервере, что и наше целевое приложение, которое просто принимает cookie в формате JSON и выдает его обратно в браузер в ответе на перенаправление 302.

Решение cookie monster работает следующим образом: когда пользователь щелкает ссылку на портале, наш портлет внутренне ищет учетные данные пользователя, аутентифицируется в удаленном приложении и возвращает cookie / токен аутентификации.Мы конвертируем это (а также целевой URL) в JSON и возвращаем его в браузер.Затем браузер отправляет этот JSON в удаленное приложение cookie в новом окне.Файл cookie восстанавливается и помещается в ответ вместе с 302 и целевым местоположением.Вуаля, страница перенаправляется на домашнюю страницу приложения, и пользователь вошел в систему. Yay!

Некоторые заметки для всех, кто использует IBM WebSphere Portal:

  • Мы выполняли аутентификацию через обслуживание ресурсовportlet.
  • Убедитесь, что ответ от портлета, обслуживающего ресурсы, не кэширован (мы немедленно устарели в кеше, поскольку не можем вернуть no-cache)
  • Убедитесь, что вы пропинговали портал довыполнение вызова ajax, так как сеанс может быть истек.

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

1 голос
/ 24 февраля 2011

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

Есть две вещи, за которыми нужно следить / иметь дело.

Ответы из приложения будут содержать SetCookie заголовки какого-либо рода.С куки нужно обращаться осторожно.В зависимости от используемой модели безопасности:

  • Они могут быть сохранены на портале и использованы для будущих запросов к приложению.
  • Они могут быть переданы в браузер пользователя.Портал также должен будет передавать файлы cookie в будущих запросах к приложению.(Этот подход требует осторожного подхода для решения возможных проблем с утечкой токена сеанса.)

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

Механизм входа в приложение явно пытается перенаправить вас (портал) наместо по умолчанию после входа в систему, и это неуместно.Для этого есть два простых исправления:

  • Пусть портал обнаружит окончательное перенаправление и будет рассматривать его как признак того, что вы успешно вошли в систему. Затем портал должен повторить запрос наСтраница, которую вы первоначально запрашивали из приложения, используя новый файл cookie (см. выше).

  • IIRC, есть дополнительный параметр, который вы можете добавить к запросу j_spring_security_check, который сообщает приложению, куда возвращатьсяуспешный вход в систему.Я не могу вспомнить детали ...


Я думал, что пересылка заголовка ответа setCookie из RA в ответ портала браузеру - это все, чтонеобходим для передачи идентификатора куки / сессии в новое окно браузера пользователя.Разве это не правильно?

Это заставит браузер установить cookie RA для контекста портала.Это не сработает, если RA и портал не входят в «область действия» куки-файла (для лучшего слова).

Вопрос в том, как мне отобразить это на / через портал?Должен ли я просто скопировать весь контент и соответствующим образом сопоставить все относительные ссылки?И, как вы заявляете, продолжаете проксировать все запросы к приложению через портал, каждый раз передавая cookie?Есть ли способ избежать копирования / изменения разметки?

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

Одним из инструментов, который можно использовать для работы с относительными ссылками, является элемент HTML <base> .На самом деле, с этим потенциально легче иметь дело, чем с абсолютными ссылками ... если вы сопоставляете все через портал.

Но имейте в виду, что в этом процессе могут возникнуть самые разные вещи.Например, вы должны остерегаться ограничения «одного и того же источника» и наличия javascript со встроенными URL для RA.

...