Eine Kurzanleitung zur Verwendung von Cloud Foundry UAA

1. Übersicht

Das Cloud Foundry-Benutzerkonto und die Authentifizierung (CF UAA) sind ein Identitätsverwaltungs- und Autorisierungsdienst. Genauer gesagt handelt es sich um einen OAuth 2.0-Anbieter, der die Authentifizierung und Ausgabe von Token an Clientanwendungen ermöglicht.

In diesem Tutorial werden die Grundlagen zum Einrichten eines CF UAA-Servers behandelt. Wir werden uns dann ansehen, wie es zum Schutz von Resource Server-Anwendungen verwendet wird.

Lassen Sie uns jedoch zuvor die Rolle der UAA im OAuth 2.0-Autorisierungsframework klären.

2. Cloud Foundry UAA und OAuth 2.0

Beginnen wir mit dem Verständnis der Beziehung zwischen UAA und der OAuth 2.0-Spezifikation.

Die OAuth 2.0-Spezifikation definiert vier Teilnehmer, die eine Verbindung miteinander herstellen können: einen Ressourcenbesitzer, einen Ressourcenserver, einen Client und einen Autorisierungsserver.

Als OAuth 2.0-Anbieter spielt UAA die Rolle des Autorisierungsservers. Dies bedeutet, dass das Hauptziel darin besteht, Zugriffstoken für Clientanwendungen auszugeben und diese Token für Ressourcenserver zu validieren .

Um die Interaktion dieser Teilnehmer zu ermöglichen, müssen wir zuerst einen UAA-Server einrichten und dann zwei weitere Anwendungen implementieren: eine als Client und die andere als Ressourcenserver.

Wir werden den Authorization_code Grant Flow mit dem Client verwenden. Und wir werden die Bearer-Token-Autorisierung mit dem Ressourcenserver verwenden. Für einen sichereren und effizienteren Handshake verwenden wir signierte JWTs als Zugriffstoken.

3. Einrichten eines UAA-Servers

Zuerst installieren wir UAA und füllen es mit einigen Demo-Daten.

Nach der Installation registrieren wir eine Clientanwendung mit dem Namen webappclient. Anschließend erstellen wir einen Benutzer mit dem Namen appuser mit zwei Rollen, resource.read und resource.write .

3.1. Installation

UAA ist eine Java-Webanwendung, die in jedem kompatiblen Servlet-Container ausgeführt werden kann. In diesem Tutorial verwenden wir Tomcat.

Fahren wir fort und laden Sie die UAA Krieg und deponieren es in unseren Tomcat Einsatz:

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

Bevor wir es starten, müssen wir jedoch die Datenquelle und das JWS-Schlüsselpaar konfigurieren.

3.2. Erforderliche Konfiguration

Standardmäßig liest UAA die Konfiguration aus uaa.yml in seinem Klassenpfad. Da wir die Kriegsdatei gerade heruntergeladen haben , ist es für uns besser, der UAA einen benutzerdefinierten Speicherort in unserem Dateisystem mitzuteilen.

Wir können dies tun, indem wir die Eigenschaft UAA_CONFIG_PATH festlegen :

export UAA_CONFIG_PATH=~/.uaa

Alternativ können wir CLOUD_FOUNDRY_CONFIG_PATH setzen. Oder wir können mit UAA_CONFIG_URL einen Remote-Standort angeben.

Anschließend können wir die erforderliche Konfiguration von UAA in unseren Konfigurationspfad kopieren:

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

Beachten Sie, dass wir die letzten drei Zeilen löschen, da wir sie gleich ersetzen werden.

3.3. Konfigurieren der Datenquelle

Konfigurieren wir also die Datenquelle, in der UAA Informationen zu Clients speichern wird.

Für dieses Tutorial verwenden wir HSQLDB:

export SPRING_PROFILES="default,hsqldb"

Da dies eine Spring Boot-Anwendung ist, können wir dies natürlich auch in uaa.yml als spring.profiles- Eigenschaft angeben .

3.4. Konfigurieren des JWS-Schlüsselpaars

Da wir JWT verwenden, muss UAA über einen privaten Schlüssel verfügen, um jede von UAA ausgegebene JWT zu signieren.

OpenSSL macht dies einfach:

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

Der Autorisierungsserver signiert die JWT mit dem privaten Schlüssel, und unser Client und Ressourcenserver überprüfen diese Signatur mit dem öffentlichen Schlüssel.

Wir exportieren sie nach JWT_TOKEN_SIGNING_KEY und JWT_TOKEN_VERIFICATION_KEY :

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

Auch hier können wir diese in uaa.yml über die Eigenschaften jwt.token.signing-key und jwt.token.verification-key angeben .

3.5. UAA starten

Zum Schluss fangen wir an:

$CATALINA_HOME/bin/catalina.sh run

Zu diesem Zeitpunkt sollte ein funktionierender UAA-Server unter // localhost: 8080 / uaa verfügbar sein .

Wenn wir zu // localhost: 8080 / uaa / info gehen , sehen wir einige grundlegende Startinformationen

3.6. Installieren des UAA-Befehlszeilenclients

Der CF UAA-Befehlszeilenclient ist das Hauptwerkzeug für die Verwaltung von UAA. Um ihn jedoch verwenden zu können, müssen Sie zuerst Ruby installieren:

sudo apt install rubygems gem install cf-uaac

Then, we can configure uaac to point to our running instance of UAA:

uaac target //localhost:8080/uaa

Note that if we don't want to use command-line client, we can, of course, use UAA's HTTP client.

3.7. Populating Clients and Users Using UAAC

Now that we have uaac installed, let's populate UAA with some demo data. At a minimum, we'll need: A client, a user, and resource.read and resource.write groups.

So, to do any administration, we'll need to authentication ourselves. We'll pick the default admin that ships with UAA, which has permissions to create other clients, users, and groups:

uaac token client get admin -s adminsecret

(Of course, we definitely need to change this account – via the oauth-clients.xml file – before shipping!)

Basically, we can read this command as: “Give me a token, using client credentials with the client_id of admin and a secret of adminsecret“.

If all goes well, we'll see a success message:

Successfully fetched token via client credentials grant.

The token is stored in uaac‘s state.

Now, operating as admin, we can register a client named webappclient with 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 //localhost:8081/login/oauth2/code/uaa

And also, we can register a user named appuser with user add:

uaac user add appuser -p appusersecret --emails [email protected]

Next, we'll add two groups – resource.read and resource.write – using with group add:

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

And finally, we'll assign these groups to appuser with member add:

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

Phew! So, what we've done so far is:

  • Installed and configured UAA
  • Installed uaac
  • Added a demo client, users, and groups

So, let's keep in mind these pieces of information and jump to the next step.

4. OAuth 2.0 Client

In this section, we'll use Spring Boot to create an OAuth 2.0 Client application.

4.1. Application Setup

Let's start by accessing Spring Initializr and generating a Spring Boot web application. We choose only the Web and OAuth2 Client components:

 org.springframework.boot spring-boot-starter-web   org.springframework.boot spring-boot-starter-oauth2-client 

In this example, we've used version 2.1.3 of Spring Boot.

Next, we need to register our client, webappclient.

Quite simply, we'll need to give the app the client-id, client-secret, and UAA's issuer-uri. We'll also specify the OAuth 2.0 scopes that this client wants the user to grant to it:

#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=//localhost:8080/uaa/oauth/token

For more information about these properties, we can have a look at the Java docs for the registration and provider beans.

And since we're already using port 8080 for UAA, let's have this run on 8081:

server.port=8081

4.2. Login

Now if we access the /login path, we should have a list of all registered clients. In our case, we have only one registered client:

Clicking on the link will redirect us to the UAA login page:

Here, let's login with appuser/appusersecret.

Submitting the form should redirect us to an approval form where the user can authorize or deny access to our client:

The user can then grant which privileges she wants. For our purposes, we'll select everything except resource:write.

Whatever the user checks will be the scopes in the resulting access token.

To prove this, we can copy the token shown at the index path, //localhost:8081, and decode it using the JWT 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 }

Once our client application receives this token, it can authenticate the user and they'll have access to the app.

Now, an app that doesn't show any data isn't very useful, so our next step will be to stand up a resource server – which has the user's data – and connect the client to it.

The completed resource server will have two protected APIs: one that requires the resource.read scope and another that requires resource.write.

What we'll see is that the client, using the scopes we granted, will be able to call the read API but not write.

5. Resource Server

The resource server hosts the user's protected resources.

It authenticates clients via the Authorization header and in consultation with an authorization server – in our case, that's UAA.

5.1. Application Set Up

To create our resource server, we'll use Spring Initializr again to generate a Spring Boot web application. This time, we'll choose the Web and OAuth2 Resource Server components:

 org.springframework.boot spring-boot-starter-oauth2-resource-server   org.springframework.boot spring-boot-starter-web 

As with the Client application, we're using the version 2.1.3 of Spring Boot.

The next step is to indicate the location of the running CF UAA in the application.properties file:

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

Of course, let's pick a new port here, too. 8082 will work fine:

server.port=8082

And that's it! We should have a working resource server and by default, all requests will require a valid access token in the Authorization header.

5.2. Protecting Resource Server APIs

Next, let's add some endpoints worth protecting, though.

We'll add a RestController with two endpoints, one authorized for users having the resource.read scope and the other for users having the resource.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(); }

Next, we'll override the default Spring Boot configuration to protect the two resources:

@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. Requesting a Protected Resource From a Client

From the Client application, we'll call the two protected resources using RestTemplate. Before making the request, we retrieve the access token from the context and add it to the Authorization header:

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; }

Note, though, that we can remove this boilerplate if we use WebClient instead of RestTemplate.

Then, we'll add two calls to the resource server endpoints:

@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); }

As expected, the call of the /read API will succeed, but not the /write one. The HTTP status 403 tells us that the user is not authorized.

6. Conclusion

In diesem Artikel haben wir mit einem kurzen Überblick über OAuth 2.0 begonnen, da es die Basis für UAA ist, einen OAuth 2.0-Autorisierungsserver. Anschließend haben wir es für die Ausgabe von Zugriffstoken für einen Client und die Sicherung einer Ressourcenserveranwendung konfiguriert.

Der vollständige Quellcode für die Beispiele ist auf Github verfügbar.