Аутентификация на основе форм с использованием HttpClient — j_security_check

#java #apache-httpclient-4.x #form-authentication #j-security-check

#java #apache-httpclient-4.x #форма-аутентификация #j-проверка безопасности

Вопрос:

Я пытаюсь аутентифицироваться на веб-сайте, который использует аутентификацию на основе форм (например, facebook.com ) с использованием Java-библиотеки Apache HttpClient.
Используя программу этого веб-сайта в качестве основного примера: http://www.elitejavacoder.com/2013/10/http-client-form-based-authentication.html , Я смог это сделать, но есть несколько вещей, которые я не понимаю в этой программе. Вот код:

 package com.elitejavacoder.http.client;

import java.util.ArrayList;
import java.util.List;

import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

public class HttpClientFormAuthentication {
    public static void main(String[] agrs) {
        String host = "yourhostname.com";
        int port = 8080;
        String protocol = "http";

        DefaultHttpClient client = new DefaultHttpClient();

        try {
            HttpHost httpHost = new HttpHost(host, port, protocol);
            client.getParams().setParameter(ClientPNames.DEFAULT_HOST, httpHost);

            HttpGet securedResource = new HttpGet("/secured/index.jsp");            
            HttpResponse httpResponse = client.execute(securedResource);
            HttpEntity responseEntity = httpResponse.getEntity();
            String strResponse = EntityUtils.toString(responseEntity);
            int statusCode = httpResponse.getStatusLine().getStatusCode();
            EntityUtils.consume(responseEntity);

            System.out.println("Http status code for Unauthenticated Request: "   statusCode);// Statue code should be 200
            System.out.println("Response for Unauthenticated Request: n"   strResponse); // Should be login page
            System.out.println("================================================================n");

            HttpPost authpost = new HttpPost("/j_security_check");
            List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
            nameValuePairs.add(new BasicNameValuePair("j_username", "yourusername"));
            nameValuePairs.add(new BasicNameValuePair("j_password", "yourpassword"));
            authpost.setEntity(new UrlEncodedFormEntity(nameValuePairs));

            httpResponse = client.execute(authpost);
            responseEntity = httpResponse.getEntity();
            strResponse = EntityUtils.toString(responseEntity);
            statusCode = httpResponse.getStatusLine().getStatusCode();
            EntityUtils.consume(responseEntity);

            System.out.println("Http status code for Authenticattion Request: "   statusCode);// Status code should be 302
            System.out.println("Response for Authenticattion Request: n"   strResponse); // Should be blank string
            System.out.println("================================================================n");

            httpResponse = client.execute(securedResource);
            responseEntity = httpResponse.getEntity();
            strResponse = EntityUtils.toString(responseEntity);
            statusCode = httpResponse.getStatusLine().getStatusCode();
            EntityUtils.consume(responseEntity);

            System.out.println("Http status code for Authenticated Request: "   statusCode);// Status code should be 200
            System.out.println("Response for Authenticated Request: n"   strResponse);// Should be actual page
            System.out.println("================================================================n");
        }
        catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}
 

У меня есть следующие вопросы (номера строк, на которые я собираюсь сослаться, указаны в контексте ссылки, которую я предоставил выше, поскольку StackOverflow не позволяет включать номера строк):

  • Что именно такое «/j_security_check» (строка 41)? И как автор узнал, что он должен использовать «j_security_check» вместо имени защищенного ресурса?
  • Как получилось, что строка «strResponse = EntityUtils.Строка toString(ResponseEntity);» (строка 49), которая находится через две строки после «HttpResponse = client.execute(authpost);» (строка 47), отличается от строки «strResponse = EntityUtils.toString(ResponseEntity);» (строка 59), что через две строки после «HttpResponse = client.execute(securedResource);» (строка 57)?
    В принципе, какие изменения происходят с «клиентом» между строками 47 и 57?

Спасибо

Ответ №1:

Это /j_security_check действие формы, чтобы контейнер знал, что этот запрос предназначен для аутентификации, и контейнер обрабатывает это. /j_security_check это адрес веб-страницы для отправки форм аутентификации, специфичный для корпоративных серверов приложений Java.

j_username и j_password являются именами параметров запроса для отправки как имени пользователя, так и пароля. Эти три должны быть названы таким образом (т.е. j_security_check j_username И j_password ), чтобы контейнер обрабатывал этот запрос как запрос аутентификации и мог извлекать требуемую информацию (т.Е. Имя пользователя и пароль) из отправленного запроса.

Автор знал, что его / ее нужно использовать /j_security_check , потому что он / она предполагает, что он проходит аутентификацию на сервере приложений J2EE. Это не очень хорошее предположение. Обратите внимание, что порт установлен на 8080? Это порт, обычно используемый серверами Java, такими как Tomcat, чтобы они не сталкивались с портом 80 на HTTP-сервере.

strResponse в строке 47 содержится содержимое самого запроса на вход (который ничего не представляет), а strResponse в строке 57 содержится содержимое защищенной страницы. Это разбивка:

Если вы делаете это в веб-браузере, произойдет следующее.

  • Вы должны ввести адрес защищенной страницы и нажать Enter.
  • Поскольку вы не прошли проверку подлинности, сервер ответит страницей формы входа.
  • Вы должны ввести свое имя пользователя и пароль и нажать «Отправить».
  • Вы получите свою защищенную страницу. Сервер вернет код перенаправления 302 с адресом, который вы изначально запросили, вместе с файлом cookie аутентификации, который сохранит ваш браузер. Ваш браузер повторно обращается к этой странице, но теперь ваш браузер также отправляет cookie, поэтому вместо формы входа вы получаете страницу, к которой пытались получить доступ.

Строка 31 — это начальный доступ к странице без аутентификации. Строки 38-39 отображают форму входа в систему, строки 41-45 эквивалентны вводу вашего имени пользователя и пароля в форму.
Строка 47 похожа на нажатие кнопки Отправки.
Строка 49 показывает, что сервер отправил в ответ. Обратите внимание, что в строке 54 комментарий «Должен быть пустой строкой». Когда вы отправляете имя пользователя и пароль, то, что вас больше всего беспокоит в ответе, — это статус HTTP. Комментарий в строке, которая выводит код состояния, гласит: «Код состояния должен быть 302». 302 — это HTTP-статус, который указывает браузеру перенаправить. Заголовки ответа будут содержать адрес, на который будет перенаправляться ваш браузер. Заголовки ответов также содержат файл cookie для проверки подлинности. Было бы неплохо, если бы это тоже было распечатано, это помогло бы понять, как все это работает. Код вручную выполняет перенаправление в строке 57, но предполагается, что он будет перенаправлен на защищенную страницу, к которой он пытался получить доступ в строке 31, а не извлекает этот адрес из заголовков HTTP-ответа.

Самое большое изменение client заключается в том, что в строке 57 client есть файл cookie аутентификации, аналогичный операции браузера. DefaultHttpClient обрабатывает все это для вас под капотом.

Файл cookie для аутентификации поступает с сервера в виде HTTP-заголовка Set-Cookie. Это указывает client на сохранение файла cookie. Затем, делая запрос, клиент отправляет HTTP-заголовок Cookie вместе с данными cookie.

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

Комментарии:

1. Вы знаете, что ваш ответ — почти идеальная копия этого: quora.com /…

Ответ №2:

«/ j_security_check» — это конечная точка, используемая Spring Framework для проверки учетных данных пользователя (вы можете найти эту информацию здесь: http://docs.spring.io/spring-security/site/docs/3.2.4.RELEASE/reference/htmlsingle/#form-login-filter , конечной точкой по умолчанию является «/j_spring_security_check»).

Автор знал, что он / она должен был использовать этот URL-адрес, потому что он / она установил URL-адрес в конфигурации Spring Framework (он определен в элементе, для установки URL-адреса необходимо изменить атрибут «login-processing-url»).

Между строками 47 и 57 клиент был перенаправлен на другой URL и получил этот URL. URL-адрес, на который должен быть перенаправлен клиент, определяется атрибутом «default-target-url». Обратите внимание, что Spring Framework также может перенаправлять пользователя на URL, который он / она запросил до отображения страницы входа в систему.

Комментарии:

1. Спасибо. Но какие именно физические изменения происходят со структурой client между строками 47 и 57? Потому что, если бы я создал новый DefaultHttpClient (скажем client 2 ) объект прямо перед строкой 57 (со всем правильным хостом, портом и т.д.) и вызвал httpResponse = client2.execute(securedResource); using client2 , результирующая сущность строки 58 и результирующая строка строки 59 были бы другими, чем если бы я просто вызвал все эти методы с client помощью . client После выполнения строки 47 что-то происходит, и это приводит к тому, что вы можете получить защищенный ресурс в строке 57.