Panduan Cepat Menggunakan Cloud Foundry UAA

1. Ikhtisar

Cloud Foundry User Account and Authentication (CF UAA) adalah layanan manajemen dan otorisasi identitas. Lebih tepatnya, ini adalah penyedia OAuth 2.0 yang memungkinkan autentikasi dan mengeluarkan token ke aplikasi Klien.

Dalam tutorial ini, kita akan membahas dasar-dasar pengaturan server CF UAA. Kami kemudian akan melihat cara menggunakannya untuk melindungi aplikasi Resource Server.

Namun sebelumnya, mari kita perjelas peran UAA dalam framework otorisasi OAuth 2.0.

2. Cloud Foundry UAA dan OAuth 2.0

Mari kita mulai dengan memahami bagaimana UAA terkait dengan spesifikasi OAuth 2.0.

Spesifikasi OAuth 2.0 menentukan empat peserta yang dapat terhubung satu sama lain: Pemilik Sumber Daya, Server Sumber Daya, Klien, dan Server Otorisasi.

Sebagai penyedia OAuth 2.0, UAA berperan sebagai server otorisasi. Ini berarti tujuan utamanya adalah mengeluarkan token akses untuk aplikasi klien dan memvalidasi token ini untuk server sumber daya .

Untuk mengizinkan interaksi peserta ini, pertama-tama kita perlu menyiapkan server UAA, lalu mengimplementasikan dua aplikasi lagi: satu sebagai klien dan yang lainnya sebagai server sumber daya.

Kami akan menggunakan aliran pemberian authorization_code dengan klien. Dan kami akan menggunakan otorisasi token Bearer dengan server sumber daya. Untuk jabat tangan yang lebih aman dan efisien, kami akan menggunakan JWT bertanda tangan sebagai token akses kami.

3. Menyiapkan Server UAA

Pertama, kami akan menginstal UAA dan mengisinya dengan beberapa data demo.

Setelah terinstal, kami akan mendaftarkan aplikasi klien bernama webappclient. Kemudian, kita akan membuat pengguna bernama appuser dengan dua peran, resource.read dan resource.write .

3.1. Instalasi

UAA adalah aplikasi web Java yang dapat dijalankan di wadah servlet yang sesuai. Dalam tutorial ini, kami akan menggunakan Tomcat.

Mari lanjutkan dan unduh perang UAA dan simpan ke dalam penyebaran Tomcat kami :

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

Namun, sebelum memulainya, kami perlu mengonfigurasi sumber datanya dan pasangan kunci JWS.

3.2. Konfigurasi yang Diperlukan

Secara default, UAA membaca konfigurasi dari uaa.yml pada jalur kelasnya. Tapi, karena kita baru saja mengunduh file perang , akan lebih baik bagi kita untuk memberi tahu UAA lokasi khusus di sistem file kita.

Kita dapat melakukan ini dengan menyetel properti UAA_CONFIG_PATH :

export UAA_CONFIG_PATH=~/.uaa

Alternatifnya, kita bisa menyetel CLOUD_FOUNDRY_CONFIG_PATH. Atau, kita dapat menentukan lokasi jauh dengan UAA_CONFIG_URL.

Kemudian, kita dapat menyalin konfigurasi yang diperlukan UAA ke dalam jalur konfigurasi kita:

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

Perhatikan bahwa kami menghapus tiga baris terakhir karena kami akan menggantinya sebentar lagi.

3.3. Mengonfigurasi Sumber Data

Jadi, mari konfigurasikan sumber data, tempat UAA akan menyimpan informasi tentang klien.

Untuk keperluan tutorial ini, kami akan menggunakan HSQLDB:

export SPRING_PROFILES="default,hsqldb"

Tentu saja, karena ini adalah aplikasi Spring Boot, kita juga dapat menetapkannya di uaa.yml sebagai properti spring.profiles .

3.4. Mengonfigurasi Pasangan Kunci JWS

Karena kami menggunakan JWT, UAA harus memiliki kunci pribadi untuk menandatangani setiap JWT yang mengeluarkan UAA.

OpenSSL membuatnya sederhana:

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

Server otorisasi akan menandatangani JWT dengan kunci pribadi, dan klien serta server sumber daya kami akan memverifikasi tanda tangan itu dengan kunci publik.

Kami akan mengekspornya ke JWT_TOKEN_SIGNING_KEY dan JWT_TOKEN_VERIFICATION_KEY :

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

Sekali lagi, kita bisa menentukan ini di uaa.yml melalui properti jwt.token.signing-key dan jwt.token.verification-key .

3.5. Memulai UAA

Akhirnya, mari kita mulai:

$CATALINA_HOME/bin/catalina.sh run

Pada titik ini, kita harus memiliki server UAA yang berfungsi tersedia di // localhost: 8080 / uaa .

Jika kita pergi ke // localhost: 8080 / uaa / info , maka kita akan melihat beberapa info startup dasar

3.6. Menginstal Klien Baris Perintah UAA

Klien Baris Perintah CF UAA adalah alat utama untuk mengelola UAA , tetapi untuk menggunakannya, kita perlu menginstal Ruby terlebih dahulu:

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

Dalam artikel ini, kami memulai dengan ikhtisar singkat tentang OAuth 2.0 sebagai fondasi dasar untuk UAA, Server Otorisasi OAuth 2.0. Kemudian, kami mengonfigurasinya untuk menerbitkan token akses untuk klien dan mengamankan aplikasi server sumber daya.

Kode sumber lengkap untuk contoh tersedia di Github.