Um guia rápido para usar o Cloud Foundry UAA
1. Visão geral
Cloud Foundry User Account and Authentication (CF UAA) is an identity management and authorization service. Mais precisamente, é um provedor OAuth 2.0 que permite autenticação e emissão de tokens para aplicativos clientes.
Neste tutorial, vamos cobrir os fundamentos da configuração de um servidor CF UAA. Em seguida, veremos como usá-lo para proteger os aplicativos do Resource Server.
Mas antes, vamos esclarecer o papel do UAA na estrutura de autorizaçãoOAuth 2.0.
2. Cloud Foundry UAA e OAuth 2.0
Vamos começar entendendo como o UAA se relaciona com a especificação OAuth 2.0.
A especificação OAuth 2.0 definefour participants que podem se conectar: um proprietário de recurso, um servidor de recurso, um cliente e um servidor de autorização.
Como um provedor OAuth 2.0, o UAA desempenha o papel deauthorization server.. Isso significaits primary goal is issuing access tokens for client applications and validating these tokens for resource servers.
Para permitir a interação desses participantes, precisamos primeiro configurar um servidor UAA e depois implementar mais dois aplicativos: um como cliente e outro como servidor de recursos.
Usaremos o fluxoauthorization_code grant com o cliente. E usaremos a autorização de token do portador com o servidor de recursos. Para um handshake mais seguro e eficiente, usaremos JWTs assinados como nossoaccess tokens.
3. Configurando um Servidor UAA
Primeiro,we’ll install UAA and populate it with some demo data.
Uma vez instalado, vamos registrar um aplicativo cliente chamadowebappclient. Em seguida, vamos criar um usuário chamadoappuser com duas funções,resource.readeresource.write.
3.1. Instalação
O UAA é um aplicativo da Web Java que pode ser executado em qualquer contêiner de servlet compatível. Neste tutorial, vamosuse Tomcat.
Vamos em frente e implantação dedownload the UAA war and deposit it into our 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
Antes de iniciá-lo, no entanto, precisaremos configurar sua fonte de dados e par de chaves JWS.
3.2. Configuração necessária
By default, UAA reads configuration from uaa.yml on its classpath. Mas, como acabamos de baixar o arquivowar, será melhor informar ao UAA um local personalizado em nosso sistema de arquivos.
Podemos fazer isso porsetting the UAA_CONFIG_PATH property:
export UAA_CONFIG_PATH=~/.uaa
Alternativamente, podemos definirCLOUD_FOUNDRY_CONFIG_PATH. Ou, podemos especificar um local remoto comUAA_CONFIG_URL.
Então, podemos copiarUAA’s required configuration em nosso caminho de configuração:
wget -qO- https://raw.githubusercontent.com/cloudfoundry/uaa/4.27.0/uaa/src/main/resources/required_configuration.yml \
> $UAA_CONFIG_PATH/uaa.yml
Observe que estamos excluindo as três últimas linhas porque as substituiremos em um momento.
3.3. Configurando a fonte de dados
Então, vamos configurar a fonte de dados,where UAA is going to store information about clients.
Para o propósito deste tutorial, vamos usar HSQLDB:
export SPRING_PROFILES="default,hsqldb"
Obviamente, como este é um aplicativo Spring Boot, também podemos especificar isso emuaa.yml as a propriedadespring.profiles.
3.4. Configurando o par de chaves JWS
Como estamos usando JWT,UAA needs to have a private key to sign each JWT that UAA issues.
O OpenSSL simplifica isso:
openssl genrsa -out signingkey.pem 2048
openssl rsa -in signingkey.pem -pubout -out verificationkey.pem
O servidor de autorizaçãosign o JWT com a chave privada, e nosso cliente e servidor de recursosverify essa assinatura com a chave pública.
Vamos exportá-los paraJWT_TOKEN_SIGNING_KEY eJWT_TOKEN_VERIFICATION_KEY:
export JWT_TOKEN_SIGNING_KEY=$(cat signingkey.pem)
export JWT_TOKEN_VERIFICATION_KEY=$(cat verificationkey.pem)
Novamente, poderíamos especificá-los emuaa.yml por meio das propriedadesjwt.token.signing-keyejwt.token.verification-key.
3.5. Iniciando o UAA
Finalmente, vamos começar:
$CATALINA_HOME/bin/catalina.sh run
Neste ponto, devemos ter um servidor UAA funcionando disponível emhttp://localhost:8080/uaa.
Se formos parahttp://localhost:8080/uaa/info, veremos algumas informações básicas de inicialização
3.6. Instalando o UAA Command-Line Client
The CF UAA Command-Line Client is the main tool for administering UAA, mas para usá-lo, precisamosinstall Ruby first:
sudo apt install rubygems
gem install cf-uaac
Então, podemos configuraruaac para apontar para nossa instância em execução do UAA:
uaac target http://localhost:8080/uaa
Observe que se não quisermos usar o cliente de linha de comando, podemos, é claro, usar o cliente HTTP do UAA.
3.7. Preenchendo clientes e usuários usando UAAC
Agora que temosuaac instalado, vamos preencher o UAA com alguns dados de demonstração. At a minimum, we’ll need: A client, a user, and resource.read and resource.write groups.
Então, para fazer qualquer administração, precisamos nos autenticar. Escolheremos o administrador padrão que vem com o UAA,which has permissions to create other clients, users, and groups:
uaac token client get admin -s adminsecret
(Claro,we definitely need to change this account - por meio do arquivooauth-clients.xml - antes do envio!)
Basicamente, podemos ler este comando como: “Dê-me umtoken, usando credenciaisclient com o client_id deadmine um segredosdeadminsecret“.
Se tudo correr bem, veremos uma mensagem de sucesso:
Successfully fetched token via client credentials grant.
O token é armazenado no estado deuaac.
Agora, operando comoadmin, podemos registrar um cliente chamadowebappclient com client add:
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
E também, podemos registrar um usuário chamadoappuser comuser add:
uaac user add appuser -p appusersecret --emails [email protected]
A seguir, vamos adicionar dois grupos -resource.read eresource.write - usando com group add:
uaac group add resource.read
uaac group add resource.write
E finalmente, vamos atribuir esses grupos aappuser com member add:
uaac member add resource.read appuser
uaac member add resource.write appuser
Ufa! Então, o que fizemos até agora é:
-
UAA instalado e configurado
-
Instaladouaac
-
Adicionado um cliente demo, usuários e grupos
Então, vamos manter em mente essas informações e pular para a próxima etapa.
4. Cliente OAuth 2.0
Nesta seção,we’ll use Spring Boot to create an OAuth 2.0 Client application.
4.1. Configuração do aplicativo
Vamos começar acessandoSpring Initializre gerando um aplicativo da web Spring Boot. Escolhemos apenas os componentesWebeOAuth2 Client:
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-oauth2-client
Neste exemplo, usamosversion 2.1.3 de Spring Boot.
Em seguida,we need to register our client, webapp*client*.
Muito simplesmente, precisaremos fornecer ao aplicativoclient-id,client-secret,e UAA'sissuer-uri. Também especificaremos os escopos OAuth 2.0 que este cliente deseja que o usuário conceda a ele:
#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
Para obter mais informações sobre essas propriedades, podemos dar uma olhada na documentação Java dos beansregistrationeprovider.
E como já estamos usando a porta 8080 para UAA, vamos executá-la na 8081:
server.port=8081
4.2. Conecte-se
Agora, se acessarmos o caminho/login, devemos ter uma lista de todos os clientes cadastrados. No nosso caso, temos apenas um cliente registrado:
Aqui, vamos fazer o login comappuser/appusersecret.
O envio do formulário deve nos redirecionar para um formulário de aprovação onde o usuário pode autorizar ou negar acesso ao nosso cliente:
O usuário pode então conceder quais privilégios ele deseja. Para nossos propósitos,we’ll select everything except resource:write.
O que quer que o usuário verifique será o escopo no token de acesso resultante.
Para provar isso, podemos copiar o token mostrado no caminho do índice,http://localhost:8081, e decodificá-lo usandoJWT debugger. We should see the scopes we checked on the approval page:
{
"jti": "f228d8d7486942089ff7b892c796d3ac",
"sub": "0e6101d8-d14b-49c5-8c33-fc12d8d1cc7d",
"scope": [
"resource.read",
"openid",
"profile"
],
"client_id": "webappclient"
// more claims
}
Depois que nosso aplicativo cliente recebe esse token, ele pode autenticar o usuário e ele terá acesso ao aplicativo.
Agora,an app that doesn’t show any data isn’t very useful, so our next step will be to stand up a resource server - que contém os dados do usuário - e conecte o cliente a ele.
O servidor de recursos concluído terá duas APIs protegidas: uma que requer o escoporesource.read e outra que requerresource.write.
O que veremos é quethe client, using the scopes we granted, will be able to call the read API but not write.
5. Servidor de Recursos
O servidor de recursos hospeda os recursos protegidos do usuário.
Ele autentica clientes por meio do cabeçalhoAuthorization e em consulta com um servidor de autorização - em nosso caso, é o UAA.
5.1. Configuração do aplicativo
Para criar nosso servidor de recursos, usaremosSpring Initializr novamente para gerar um aplicativo da web Spring Boot. Desta vez, vamos escolher os componentesWebeOAuth2 Resource Server:
org.springframework.boot
spring-boot-starter-oauth2-resource-server
org.springframework.boot
spring-boot-starter-web
Tal como acontece com o aplicativo cliente, estamos usandothe version 2.1.3 de Spring Boot.
A próxima etapa é indicar a localização do CF UAA em execução no arquivoapplication.properties:
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8080/uaa/oauth/token
Claro, vamos escolher uma nova porta aqui também. 8082 funcionará bem:
server.port=8082
E é isso! Devemos ter um servidor de recursos funcionando e, por padrão, todas as solicitações exigirão um token de acesso válido noAuthorization header.
5.2. Protegendo APIs de servidor de recursos
A seguir, vamos adicionar alguns pontos de extremidade que vale a pena proteger.
Adicionaremos umRestController com dois endpoints, um autorizado para usuários com escoporesource.read e outro para usuários com escoporesource.write scope:
@GetMapping("/read")
public String read(Principal principal) {
return "Hello write: " + principal.getName();
}
@GetMapping("/write")
public String write(Principal principal) {
return "Hello write: " + principal.getName();
}
A seguir,we’ll override the default Spring Boot configuration para proteger os dois recursos:
@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();
}
}
Note that the scopes supplied in the access token are prefixed with SCOPE_ when they are translated to a Spring Security GrantedAuthority.
5.3. Solicitando um recurso protegido de um cliente
Do aplicativo cliente, chamaremos os dois recursos protegidos usandoRestTemplate. Antes de fazer a solicitação, recuperamos o token de acesso do contexto e o adicionamos ao cabeçalhoAuthorization:
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;
}
Observe, porém, que podemos remover esse clichê seuse WebClient instead of RestTemplate.
Em seguida, adicionaremos duas chamadas aos endpoints do servidor de recursos:
@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);
}
Como esperado,the call of the /read API will succeed, but not the /write one. O status HTTP 403 nos diz que o usuário não está autorizado.
6. Conclusão
Neste artigo, começamos com uma breve visão geral do OAuth 2.0, pois ele é a base do UAA, um servidor de autorização OAuth 2.0. Em seguida, o configuramos para emitir tokens de acesso para um cliente e proteger um aplicativo de servidor de recursos.
O código-fonte completo dos exemplos está disponívelover on Github.