Перейти к основному содержимому

Краткое руководство по использованию Cloud Foundry UAA

· 9 мин. чтения

1. Обзор

Cloud Foundry User Account and Authentication (CF UAA) — это служба управления идентификацией и авторизации. Точнее, это провайдер OAuth 2.0, позволяющий выполнять аутентификацию и выдавать токены клиентским приложениям.

В этом уроке мы рассмотрим основы настройки сервера CF UAA. Затем мы рассмотрим, как использовать его для защиты приложений Resource Server.

Но прежде давайте проясним роль UAA в структуре авторизации OAuth 2.0 .

2. Cloud Foundry UAA и OAuth 2.0

Начнем с понимания того, как UAA соотносится со спецификацией OAuth 2.0.

Спецификация OAuth 2.0 определяет четырех участников , которые могут подключаться друг к другу: владелец ресурса, сервер ресурсов, клиент и сервер авторизации.

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

Чтобы разрешить взаимодействие этих участников, нам нужно сначала настроить сервер UAA, а затем реализовать еще два приложения: одно в качестве клиента, а другое в качестве сервера ресурсов.

Мы будем использовать поток предоставления авторизации_кода с клиентом. И мы будем использовать авторизацию токена Bearer с сервером ресурсов. Для более безопасного и эффективного рукопожатия мы будем использовать подписанные JWT в качестве токенов доступа .

3. Настройка сервера UAA

Во- первых, мы установим UAA и заполним его демонстрационными данными.

После установки мы зарегистрируем клиентское приложение с именем webappclient. Затем мы создадим пользователя с именем appuser с двумя ролями, resource.read и resource.write .

3.1. Монтаж

UAA — это веб-приложение Java, которое можно запускать в любом совместимом контейнере сервлетов. В этом уроке мы будем использовать Tomcat .

Давайте продолжим и загрузим войну UAA и разместим ее в нашем развертывании Tomcat :

wget -O $CATALINA_HOME/webapps/uaa.war \
https://search.maven.org/remotecontent?filepath=org/cloudfoundry/identity/cloudfoundry-identity-uaa/4.27.0/cloudfoundry-identity-uaa-4.27.0.war

Однако, прежде чем мы запустим его, нам нужно настроить его источник данных и пару ключей JWS.

3.2. Требуемая конфигурация

По умолчанию UAA считывает конфигурацию из uaa.yml в своем пути к классам. Но, поскольку мы только что загрузили военный файл, нам будет лучше указать UAA пользовательское местоположение в нашей файловой системе.

Мы можем сделать это, установив свойство UAA_CONFIG_PATH :

export UAA_CONFIG_PATH=~/.uaa

В качестве альтернативы мы можем установить CLOUD_FOUNDRY_CONFIG_PATH. Или мы можем указать удаленное местоположение с помощью UAA_CONFIG_URL.

Затем мы можем скопировать требуемую конфигурацию UAA в наш путь конфигурации:

wget -qO- https://raw.githubusercontent.com/cloudfoundry/uaa/4.27.0/uaa/src/main/resources/required_configuration.yml \
> $UAA_CONFIG_PATH/uaa.yml

Обратите внимание, что мы удаляем последние три строки, потому что вскоре заменим их.

3.3. Настройка источника данных

Итак, настроим источник данных, в котором UAA будет хранить информацию о клиентах.

Для целей этого руководства мы будем использовать HSQLDB:

export SPRING_PROFILES="default,hsqldb"

Конечно, поскольку это приложение Spring Boot, мы также можем указать это в uaa.yml как свойство spring.profiles .

3.4. Настройка пары ключей JWS

Поскольку мы используем JWT, UAA должен иметь закрытый ключ для подписи каждого JWT, который выдает UAA.

OpenSSL делает это просто:

openssl genrsa -out signingkey.pem 2048
openssl rsa -in signingkey.pem -pubout -out verificationkey.pem

Сервер авторизации подпишет JWT с помощью закрытого ключа, а наш клиент и сервер ресурсов проверят эту подпись с помощью открытого ключа.

Мы экспортируем их в JWT_TOKEN_SIGNING_KEY и JWT_TOKEN_VERIFICATION_KEY :

export JWT_TOKEN_SIGNING_KEY=$(cat signingkey.pem)
export JWT_TOKEN_VERIFICATION_KEY=$(cat verificationkey.pem)

Опять же, мы могли бы указать их в uaa.yml через свойства jwt.token.signing-key и jwt.token.verification-key .

3.5. Запуск УАА

Наконец, давайте начнем:

$CATALINA_HOME/bin/catalina.sh run

На данный момент у нас должен быть работающий сервер UAA, доступный по адресу http://localhost:8080/uaa .

Если мы перейдем по адресу http://localhost:8080/uaa/info , то увидим базовую информацию о запуске.

3.6. Установка клиента командной строки UAA

Клиент командной строки CF UAA является основным инструментом для администрирования UAA , но для его использования нам нужно сначала установить Ruby :

sudo apt install rubygems
gem install cf-uaac

Затем мы можем настроить uaac так , чтобы он указывал на наш работающий экземпляр UAA:

uaac target http://localhost:8080/uaa

Обратите внимание: если мы не хотим использовать клиент командной строки, мы, конечно, можем использовать HTTP-клиент UAA.

3.7. Заполнение клиентов и пользователей с помощью UAAC

Теперь, когда у нас установлен uaac , давайте заполним UAA некоторыми демонстрационными данными. Как минимум, нам понадобятся: клиент , пользователь и группы resource.read и resource.write .

Таким образом, для любого администрирования нам потребуется пройти аутентификацию самостоятельно. Мы выберем администратора по умолчанию, который поставляется с UAA и имеет разрешения на создание других клиентов, пользователей и групп:

uaac token client get admin -s adminsecret

(Конечно, нам обязательно нужно изменить эту учетную запись — через файл oauth-clients.xml — перед отправкой!)

По сути, мы можем прочитать эту команду как: «Дайте мне токен, используя учетные данные клиента с client_id admin и секретом adminsecret ».

Если все пойдет хорошо, мы увидим сообщение об успешном завершении:

Successfully fetched token via client credentials grant.

Токен хранится в состоянии uaac .

Теперь, работая от имени администратора , мы можем зарегистрировать клиента с именем webappclient с добавлением клиента:

uaac client add webappclient -s webappclientsecret \ 
--name WebAppClient \
--scope resource.read,resource.write,openid,profile,email,address,phone \
--authorized_grant_types authorization_code,refresh_token,client_credentials,password \
--authorities uaa.resource \
--redirect_uri http://localhost:8081/login/oauth2/code/uaa

А также мы можем зарегистрировать пользователя с именем appuser с добавлением пользователя:

uaac user add appuser -p appusersecret --emails appuser@acme.com

Далее мы добавим две группы — resource.read и resource.write — используя group add:

uaac group add resource.read
uaac group add resource.write

И, наконец, мы назначим эти группы для appuser с добавлением участников:

uaac member add resource.read appuser
uaac member add resource.write appuser

Фу! Итак, что мы сделали до сих пор:

  • Установил и настроил УАА
  • Установлен UAAC
  • Добавлен демонстрационный клиент, пользователи и группы

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

4. Клиент OAuth 2.0

В этом разделе мы будем использовать Spring Boot для создания клиентского приложения OAuth 2.0 .

4.1. Настройка приложения

Начнем с доступа к Spring Initializr и создания веб-приложения Spring Boot. Мы выбираем только компоненты Web и OAuth2 Client :

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>

В этом примере мы использовали Spring Boot версии 2.1.3 .

Далее нам нужно зарегистрировать наш клиент webapp client .

Проще говоря, нам нужно предоставить приложению client-id, client-secret и UAA issuer-uri . Мы также укажем области действия OAuth 2.0, которые этот клиент хочет предоставить пользователю:

#registration
spring.security.oauth2.client.registration.uaa.client-id=webappclient
spring.security.oauth2.client.registration.uaa.client-secret=webappclientsecret
spring.security.oauth2.client.registration.uaa.scope=resource.read,resource.write,openid,profile

#provider
spring.security.oauth2.client.provider.uaa.issuer-uri=http://localhost:8080/uaa/oauth/token

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

И поскольку мы уже используем порт 8080 для UAA, давайте запустим это на 8081:

server.port=8081

4.2. Авторизоваться

Теперь, если мы получим доступ к пути /login , у нас должен быть список всех зарегистрированных клиентов. В нашем случае у нас есть только один зарегистрированный клиент:

./8f7512bff20b0bb75368864f69b49253.png

Клик по ссылке перенаправит нас на страницу входа в UAA:

./f22c01c0a1e948926e3dd3fd16f8c811.png

Здесь давайте войдем с помощью appuser/appusersecret .

Отправка формы должна перенаправить нас на форму утверждения, где пользователь может разрешить или запретить доступ к нашему клиенту:

./5d827817f7420835e4354114793dfcbb.png

Затем пользователь может предоставить те привилегии, которые он хочет. Для наших целей мы выберем все , кроме resource:write.

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

Чтобы доказать это, мы можем скопировать токен, указанный в пути индекса, http://localhost:8081 , и декодировать его с помощью отладчика JWT . Мы должны увидеть области, которые мы проверили, на странице утверждения:

{
"jti": "f228d8d7486942089ff7b892c796d3ac",
"sub": "0e6101d8-d14b-49c5-8c33-fc12d8d1cc7d",
"scope": [
"resource.read",
"openid",
"profile"
],
"client_id": "webappclient"
// more claims
}

Как только наше клиентское приложение получит этот токен, оно сможет аутентифицировать пользователя, и он получит доступ к приложению.

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

Готовый сервер ресурсов будет иметь два защищенных API: для одного требуется область resource.read , а для другого требуется resource.write.

Мы увидим, что клиент, используя предоставленные нами области действия, сможет вызывать API для чтения , но не для записи.

5. Сервер ресурсов

На сервере ресурсов размещаются защищенные ресурсы пользователя.

Он аутентифицирует клиентов через заголовок Authorization и консультируется с сервером авторизации — в нашем случае это UAA.

5.1. Настройка приложения

Чтобы создать наш сервер ресурсов, мы снова будем использовать Spring Initializr для создания веб-приложения Spring Boot. На этот раз мы выберем компоненты Web и OAuth2 Resource Server :

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

Как и в случае с клиентским приложением, мы используем версию 2.1.3 Spring Boot.

Следующим шагом будет указание расположения работающего CF UAA в файле application.properties :

spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8080/uaa/oauth/token

Конечно, давайте выберем и здесь новый порт. 8082 будет работать нормально:

server.port=8082

Вот и все! У нас должен быть работающий сервер ресурсов, и по умолчанию для всех запросов потребуется действительный токен доступа в заголовке авторизации .

5.2. Защита API-интерфейсов Resource Server

Затем давайте добавим некоторые конечные точки, которые стоит защитить.

Мы добавим RestController с двумя конечными точками, одна авторизована для пользователей с областью resource.read , а другая — для пользователей с областью resource.write:

@GetMapping("/read")
public String read(Principal principal) {
return "Hello write: " + principal.getName();
}

@GetMapping("/write")
public String write(Principal principal) {
return "Hello write: " + principal.getName();
}

Далее мы переопределим конфигурацию Spring Boot по умолчанию, чтобы защитить два ресурса:

@EnableWebSecurity
public class OAuth2ResourceServerSecurityConfiguration extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/read/**").hasAuthority("SCOPE_resource.read")
.antMatchers("/write/**").hasAuthority("SCOPE_resource.write")
.anyRequest().authenticated()
.and()
.oauth2ResourceServer().jwt();
}
}

Обратите внимание, что области, предоставленные в токене доступа, имеют префикс SCOPE_ , когда они транслируются в Spring Security GrantedAuthority .

5.3. Запрос защищенного ресурса от клиента

Из клиентского приложения мы будем вызывать два защищенных ресурса с помощью RestTemplate. Перед выполнением запроса мы извлекаем токен доступа из контекста и добавляем его в заголовок Authorization :

private String callResourceServer(OAuth2AuthenticationToken authenticationToken, String url) {
OAuth2AuthorizedClient oAuth2AuthorizedClient = this.authorizedClientService.
loadAuthorizedClient(authenticationToken.getAuthorizedClientRegistrationId(),
authenticationToken.getName());
OAuth2AccessToken oAuth2AccessToken = oAuth2AuthorizedClient.getAccessToken();

HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + oAuth2AccessToken.getTokenValue());

// call resource endpoint

return response;
}

Однако обратите внимание, что мы можем удалить этот шаблон, если будем использовать WebClient вместо RestTemplate .

Затем мы добавим два вызова к конечным точкам сервера ресурсов:

@GetMapping("/read")
public String read(OAuth2AuthenticationToken authenticationToken) {
String url = remoteResourceServer + "/read";
return callResourceServer(authenticationToken, url);
}

@GetMapping("/write")
public String write(OAuth2AuthenticationToken authenticationToken) {
String url = remoteResourceServer + "/write";
return callResourceServer(authenticationToken, url);
}

Как и ожидалось, вызов API / read будет успешным, но не вызов / write . Статус HTTP 403 говорит нам, что пользователь не авторизован.

6. Заключение

В этой статье мы начали с краткого обзора OAuth 2.0, поскольку он является базовой основой для UAA, сервера авторизации OAuth 2.0. Затем мы настроили его для выдачи маркеров доступа для клиента и защиты приложения сервера ресурсов.

Полный исходный код примеров доступен на Github .