Содержание


Использование клиентов OAuth 2.0 в Java-программировании

Часть 2. Передача реквизитов клиента

Comments

Серия контента:

Этот контент является частью # из серии # статей: Использование клиентов OAuth 2.0 в Java-программировании

Следите за выходом новых статей этой серии.

Этот контент является частью серии:Использование клиентов OAuth 2.0 в Java-программировании

Следите за выходом новых статей этой серии.

Обзор

OAuth – это открытый стандарт авторизации, позволяющий клиентам получать доступ к защищенным ресурсам сервера от имени владельца ресурса. Владельцем ресурса может быть другой клиент или конечный пользователь. OAuth также помогает конечным пользователям разрешать третьим лицам доступ к своим ресурсам на сервере без необходимости передавать собственные регистрационные данные, такие как имя пользователя и пароль. Эта серия статей посвящена системе авторизации OAuth 2.0, описанной в документе RFC RFC6749. Полная система авторизации OAuth 2.0, описанная в RFC 6749, находится на веб-сайте Internet Engineering Task Force (см. раздел Ресурсы).

Грант авторизации

Грант авторизации – это реквизит доступа, служащий разрешением владельца ресурса на авторизацию, который можно использовать для доступа к защищенному ресурсу. Этот реквизит используется клиентом для получения ключа доступа, а ключ доступа в конечном итоге передается вместе с запросом доступа к защищенному ресурсу. В OAuth 2.0 определены гранты четырех типов:

  1. Код авторизации
  2. Неявный
  3. Реквизиты доступа владельца ресурса
  4. Реквизиты клиента

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

Грант реквизитов клиента

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

Схема, приведенная на рисунке 1, состоит из следующих действий.

(A) Клиент OAuth 2.0 проходит проверку подлинности на сервере авторизации, используя свои реквизиты доступа, и запрашивает ключ доступа из соответствующей конечной точки.

(B) Сервер авторизации проверяет подлинность клиента OAuth 2.0 и подтверждает реквизиты клиента. Если они действительны, сервер авторизации выдает ключ доступа.

Рисунок 1. Схема передачи реквизитов клиента
Схема передачи реквизитов клиента
Схема передачи реквизитов клиента

Запрос ключа доступа

Запрос ключа доступа соответствует шагу А на рисунке 1.

Клиент делает запрос к конечной точке выдачи ключа (серверу авторизации), передавая следующие параметры в формате application/x-www-form-urlencoded.

  • grant_type: ОБЯЗАТЕЛЬНЫЙ. Должно быть присвоено значение client_credentials.
  • client_id: ОБЯЗАТЕЛЬНЫЙ. Идентификатор клиента.
  • client_secret: ОБЯЗАТЕЛЬНЫЙ. Секретный ключ и пароль клиента.
  • scope: ФАКУЛЬТАТИВНЫЙ. Область запроса доступа.

Поскольку в качестве гранта авторизации используется проверка подлинности клиента, никакой дополнительной авторизации не требуется. Например, клиент делает следующий HTTP-запрос с использованием средств безопасности транспортного уровня.

Листинг 1. Запрос HTTP-клиента
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&client_id=myApp&client_secret=ab32vr

Ответ на запрос ключа доступа

Ответ на запрос ключа доступа соответствует шагу В на рисунке 1. Если запрос ключа доступа действителен и авторизован, то сервер авторизации возвращает ключ доступа. Успешный ответ показан в листинге 2.

Листинг 2. Ответ на запрос ключа доступа
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
  "access_token":"2YotnFZFEjr1zCsicMWpAA",
  "token_type":"example",
  "expires_in":3600,
  "example_parameter":"example_value"
}

Если запрос недопустимый или несанкционированный, то сервер авторизации возвращает код с соответствующим сообщением об ошибке.

Установка

Пример клиента Outh2.0 содержится в файле OAuth2.0_client_credentials.zip (см. раздел Загрузки. Код организован как Java-проект, который можно импортировать в среду Eclipse.

Предварительные замечания

Чтобы настроить среду разработки и импортировать прилагаемый проект, нужно загрузить IDE Eclipse для разработчиков Java EE. Загрузите Eclipse со страницы загрузок Eclipse.

В проекте используются следующие JAR-файлы:

  1. commons-codec-1.6.jar
  2. commons-logging-1.1.1.jar
  3. httpclient-4.2.5.jar
  4. httpclient-cache-4.2.5.jar
  5. httpcore-4.2.4.jar
  6. httpmime-4.2.5.jar
  7. json-simple-1.1.1.jar

JAR-файлы, упомянутые в пп. 1-6, находятся в JAR-файле HttpComponents. Его можно загрузить из проекта Apache HTTP Component. Файл json-simple-1.1.1.jar можно загрузить со страницы проекта Simple JSON. Скопируйте эти JAR-файлы в папку lib проекта Java.

Код клиента OAuth 2.0

Обсуждаемый здесь клиент OAuth 2.0 реализует грант реквизитов доступа клиента. В последующих частях этой серии руководств обсуждаются остальные типы грантов и код клиента подвергается дальнейшему редактированию.

Входные параметры

Входные параметры для клиента предоставляются посредством файла параметров Oauth2Client.config, который находится в папке ресурсов проекта.

  • scope: это необязательный параметр. Он определяет область запроса доступа. Ключ доступа, возвращенный сервером, обеспечивает доступ только к тем службам, которые входят в эту область.
  • grant_type: должен иметь значение client_credentials, соответствующее гранту реквизитов доступа клиента.
  • client_id: идентификатор клиента или потребителя, предоставляемый сервером ресурсов при регистрации приложения.
  • client_secret: секретный ключ клиента или потребителя, предоставляемый сервером ресурсов при регистрации приложения.
  • access_token: ключ доступа, возвращенный сервером авторизации в ответ на действительный и авторизованный запрос ключа доступа. В рамках этого запроса реквизиты доступа клиента обмениваются на ключ доступа.
  • authentication_server_url: конечная точка ключа. Все запросы на предоставление и возобновление ключей доступа необходимо направлять на этот URL-адрес.
  • resource_server_url: URL-адрес сервера ресурсов, по которому нужно обращаться, чтобы получить доступ к защищенному ресурсу, передавая ему ключ доступа в заголовке авторизации.

Код клиента приведен в листинге 2.

Листинг 3. Исходный код клиента
//Загрузка файла параметров
Properties config = OauthUtils.getClientConfigProps(OauthConstants.CONFIG_FILE_PATH);

//Создание компонента OAuthDetails из конфигурационного файла параметров
Oauth2Details oauthDetails = OauthUtils.createOauthDetails(config);

//Проверка входных данных
if(!OauthUtils.isValidInput(oauthDetails)){
 System.out.println("Please provide valid config properties to continue.");
 System.exit(0);
}

//Определение операции
if(oauthDetails.isAccessTokenRequest()){
  //Создание нового ключа доступа
  String accessToken = OauthUtils.getAccessToken(oauthDetails);
   if(OauthUtils.isValid(accessToken)){
      System.out.println("Successfully generated Access token for client_credentials grant_type: "+accessToken);
   }
   else{
    System.out.println("Could not generate Access token for client_credentials grant_type");
   }
}

else {
 //Доступ к защищенному ресурсу с сервера с помощью OAuth2.0
 //Ответ от сервера ресурсов должен быть в формате Json, URL-коде или xml
   System.out.println("Resource endpoint url: " + oauthDetails.getResourceServerUrl());
   System.out.println("Attempting to retrieve protected resource");
   OauthUtils.getProtectedResource(oauthDetails);
}

Код клиента, приведенный в листинге 3, считывает входные параметры, указанные в файле Oauth2Client.config. Он проверяет значения client_id, client_secret и authentication_server_url. Если URL сервера ресурсов в файле конфигурации действителен, то клиент пытается получить защищенный ресурс, доступный по этому URL. В противном случае клиент только делает запрос ключа доступа к серверу авторизации и извлекает ключ доступа. В следующем разделе объясняется, как работает код, отвечающий за извлечение защищенного ресурса и ключа доступа.

Доступ к защищенному ресурсу

Код, приведенный в листинге 4, демонстрирует, как получить доступ к защищенному ресурсу, используя ключ доступа.

Листинг 4. Доступ к защищенному ресурсу
String resourceURL = oauthDetails.getResourceServerUrl();

HttpGet get = new HttpGet(resourceURL);
get.addHeader(OAuthConstants.AUTHORIZATION,
 getAuthorizationHeaderForAccessToken(oauthDetails
  .getAccessToken()));
DefaultHttpClient client = new DefaultHttpClient();
HttpResponse response = null;
int code = -1;
	try {
	response = client.execute(get);
	code = response.getStatusLine().getStatusCode();
	if (code == 401) {
	 // Ключ доступа недействителен или просрочен. 
	 // Восстановление ключа доступа
	 System.out
	 .println("Access token is invalid or expired. Regenerating access 	  token....");
	 String accessToken = getAccessToken(oauthDetails);
	 if (isValid(accessToken)) {
	  // Обновление ключа доступа
	  // System.out.println("New access token: " + accessToken);
	   oauthDetails.setAccessToken(accessToken);
	   get.removeHeaders(OAuthConstants.AUTHORIZATION);
	   get.addHeader(OAuthConstants.AUTHORIZATION,
	   getAuthorizationHeaderForAccessToken(oauthDetails
	   .getAccessToken()));
	   get.releaseConnection();
	   response = client.execute(get);
	   code = response.getStatusLine().getStatusCode();
		if (code == 401) {
			throw new RuntimeException(
			"Could not access protected resource. Server returned http 			code: " + code);

		}

	  }
		else {
		 throw new RuntimeException(
		 Could not regenerate access token");
	       }

       }

		handleResponse(response);

Примечания.

  • Этот метод заполняет компонент OauthDetails значениями из файла конфигурации.
  • Как подсказывает название, он пытается получить защищенный ресурс с сервера ресурсов. Таким образом, мы создаем простой метод HttpGet.
  • Для аутентификации на сервере ресурсов нужно передать в заголовке авторизации ключ доступа.

    Пример: Authorization: Bearer accessTokenValue

  • Создайте компонент DefaultHttpClient, чтобы сделать запрос get к серверу ресурсов.
  • Если код ответа сервера ресурсов 401, то ключ доступа, используемый для аутентификации, недействителен или просрочен.
  • На следующем шаге выполняется регенерация ключа доступа. (См. Листинг 5).
  • После успешной регенерации обновите значение ключа доступа в компоненте OauthDetails. Замените существующий заголовок авторизации в методе get новым значением ключа доступа.
  • Теперь сделайте другой запрос доступа к защищенному ресурсу.
  • Если ключ доступа действителен и URL-адрес сервера ресурсов правильный, вы увидите в окне консоли содержимое ответа.

Возобновление просроченного ключа доступа

Код, приведенный в листинге 5, управляет возобновлением просроченных ключей доступа.

Листинг 5. Возобновление просроченного ключа доступа
 HttpPost post = new HttpPost(
 oauthDetails.getAuthenticationServerUrl());
 String clientId = oauthDetails.getClientId();
 String clientSecret = oauthDetails.getClientSecret();
 String scope = oauthDetails.getScope();

 List<BasicNameValuePair> parametersBody =
 new ArrayList<BasicNameValuePair>();
 parametersBody.add(new BasicNameValuePair(OAuthConstants.GRANT_TYPE,
 oauthDetails.getGrantType()));
 parametersBody.add(new BasicNameValuePair(OAuthConstants.Client_ID,
 clientId));
 parametersBody.add(new BasicNameValuePair(OAuthConstants.Client_Secret,
 clientSecret));

 
 if (isValid(scope)) {
	parametersBody.add(new BasicNameValuePair
      (OAuthConstants.SCOPE,scope));
  }

 DefaultHttpClient client = new DefaultHttpClient();
 HttpResponse response = null;
 String accessToken = null;
   try {
	post.setEntity(new UrlEncodedFormEntity(parametersBody,
       HTTP.UTF_8));
	
	response = client.execute(post);
	int code = response.getStatusLine().getStatusCode();
	if (code == 401) {
	System.out.println("Authorization
       server expects Basic authentication");
	// Add Basic Authorization header
	post.addHeader(
	OAuthConstants.AUTHORIZATION,
	getBasicAuthorizationHeader(oauthDetails.clientId,
	clientSecret));
	System.out.println("Retry with client credentials");
	post.releaseConnection();
	response = client.execute(post);
	code = response.getStatusLine().getStatusCode();
	
	if (code == 401) {
	throw new RuntimeException(
	"Could not retrieve access token for client: "
	clientId);
	   }
        }
      }
	Map<String, String> map = handleResponse(response);
	accessToken = map.get(OAuthConstants.ACCESS_TOKEN);
	} catch (ClientProtocolException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
	} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
	}

	return accessToken;

Примечания.

  • Этот метод генерирует запрос на HttpPost и направляет его по URL-адресу сервера аутентификации.
  • В запросе client_id передаются параметры client_secret, password и, при необходимости, scope в URL-коде.
  • Согласно системе авторизации OAuth 2.0, для проверки подлинности при запросе ключа доступа клиент должен установить заголовок авторизации со своими реквизитами доступа или другими учетными данными, предоставленными сервером. Но это – предмет реализации сервера авторизации. Клиентский код делает первоначальный запрос без добавления основного заголовка аутентификации. Если сервер возвращает несанкционированный ответ, то клиент впоследствии пытается пройти аутентификацию со своими реквизитами доступа.
  • OAuth 2.0 требует, чтобы ответ на запрос ключа доступа отправлялся в формате JSON. Но я добавил для гибкости дополнительные методы обработки ответа от сервера в формате xml или URL-кода.

Тестирование клиента OAuth 2.0

В этом разделе мы обсудим способы настройки OAuth 2.0-совместимой конечной точки и тестирования клиента через нее.

Тестирование клиента через конечные точки IBM

Этот клиент успешно протестирован с OAuth 2.0-совместимыми конечными точками IBM, а именно, IBM® WebSphere® Application Server и IBM Datapower™.

Инструкции по настройке конечной точки OAuth 2.0 на сервере приложений Websphere приведены в документе Подключение поставщика услуг OAuth к WebSphere Application Server.

Запуск клиента

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

  • Импортируйте Java-проект, прилагаемый к этому руководству, в рабочее пространство Eclipse.
  • Загрузите и скопируйте JAR-файлы зависимостей в папку lib проекта.
  • Перейдите к файлу resources/com/ibm/oauth/Oauth2Client.config и введите значения client_idclient_secret и authorization_server URL.
  • Откройте файл Oauth2Client.java и запустите его в Eclipse.

Получение ключа доступа

Вы должны увидеть в окне консоли результат, показанный в листинге 6.

Листинг 6. Получение ключа доступа
Resource server URL is null. Will assume request is for generating Access token
Validated Input

********** Response Received **********
  expires_in = 3600
  token_type = bearer
  scope =
  access_token = mc20Tn3Br8raUvCrBEap3VYMbErGXshjiXYFAwEB
Successfully generated Access token for client_credentials grant_type: mc20Tn3Br8raUvCrBEap3VYMbErGXshjiXYFAwEB

Получение от сервера сведений о пользователе

Имея ключ доступа, можно обратиться к веб-приложению на сервере приложений WebSphere с запросом аутентификации OAuth 2.0.

  • Отредактируйте файл Oauth2Client.confg, указав ключ доступа и URL-адрес сервера ресурсов, который вы хотите протестировать.
  • Снова выполните Oauth2Client.java.

Вы должны увидеть в окне консоли результат, показанный в листинге 7.

Листинг 7. Информация о пользователе
Resource endpoint url: https://localhost/protectedResource
Attempting to retrieve protected resource

********** Response Received **********
{
 "Author": "Varun Ojha",
 "Authenticatation": "Oauth2.0",
 "Result": "Success"
}

Как видите, вы успешно получили сведения о пользователе методом аутентификации OAuth 2.0. Когда указанный в файле конфигурации срок действия ключа доступа истечет, клиент автоматически обновит его и будет использовать для извлечения защищенного ресурса, доступного по URL-адресу сервера ресурсов.

Заключение

Это руководство знакомит читателя с основами реквизитов клиента OAuth. Оно демонстрирует, как написать на языке Java универсальный клиент OAuth 2.0, который устанавливает соединение с несколькими OAuth 2.0-совместимыми конечными точками и получает от них защищенные ресурсы. К статье прилагается пример клиента в виде проекта Java, чтобы читатель мог легко импортировать его в рабочее пространство Eclipse и приступить к тестированию. В последующих частях этой серии статей мы опишем оставшиеся два типа грантов, имеющихся в системе авторизации OAuth 2.0.


Ресурсы для скачивания


Похожие темы


Комментарии

Войдите или зарегистрируйтесь для того чтобы оставлять комментарии или подписаться на них.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Технология Java
ArticleID=1012025
ArticleTitle=Использование клиентов OAuth 2.0 в Java-программировании: Часть 2. Передача реквизитов клиента
publish-date=07282015