Panduan Pantas Menggunakan Cloud Foundry UAA

1. Gambaran keseluruhan

Akaun Pengguna dan Pengesahan Cloud Foundry (CF UAA) adalah perkhidmatan pengurusan dan kebenaran identiti. Lebih tepat lagi, ini adalah penyedia OAuth 2.0 yang membenarkan pengesahan dan mengeluarkan token untuk aplikasi Pelanggan.

Dalam tutorial ini, kita akan membahas asas penyediaan pelayan CF UAA. Kami kemudian akan melihat bagaimana menggunakannya untuk melindungi aplikasi Pelayan Sumber.

Tetapi sebelum ini, mari kita jelaskan peranan UAA dalam kerangka kebenaran OAuth 2.0.

2. Cloud Foundry UAA dan OAuth 2.0

Mari mulakan dengan memahami bagaimana UAA berkaitan dengan spesifikasi OAuth 2.0.

Spesifikasi OAuth 2.0 mendefinisikan empat peserta yang dapat berhubung antara satu sama lain: Pemilik Sumber, Pelayan Sumber, Pelanggan dan Pelayan Pengesahan.

Sebagai penyedia OAuth 2.0, UAA memainkan peranan sebagai pelayan kebenaran. Ini bermaksud tujuan utamanya adalah mengeluarkan token akses untuk aplikasi pelanggan dan mengesahkan token ini untuk pelayan sumber .

Untuk membolehkan interaksi peserta ini, pertama-tama kita perlu menyediakan pelayan UAA dan kemudian melaksanakan dua aplikasi lagi: satu sebagai pelanggan dan yang lain sebagai pelayan sumber.

Kami akan menggunakan authorization_code aliran geran dengan pelanggan. Dan kami akan menggunakan kebenaran token Bearer dengan pelayan sumber. Untuk berjabat tangan yang lebih selamat dan cekap, kami akan menggunakan JWT yang ditandatangani sebagai token akses kami.

3. Menyiapkan Pelayan UAA

Pertama, kami akan memasang UAA dan mengisi dengan beberapa data demo.

Setelah dipasang, kami akan mendaftarkan aplikasi pelanggan bernama webappclient. Kemudian, kami akan membuat pengguna bernama appuser dengan dua peranan, resource.read dan resource.write .

3.1. Pemasangan

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

Mari maju dan memuat turun perang UAA dan memasukkannya ke dalam penggunaan 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

Sebelum memulakannya, kita perlu mengkonfigurasi sumber datanya dan pasangan kunci JWS.

3.2. Konfigurasi yang diperlukan

Secara lalai, UAA membaca konfigurasi dari uaa.yml pada classpathnya. Tetapi, kerana kami baru sahaja memuat turun fail perang , lebih baik kami memberitahu UAA lokasi tersuai pada sistem fail kami.

Kita boleh melakukannya dengan menetapkan harta UAA_CONFIG_PATH :

export UAA_CONFIG_PATH=~/.uaa

Sebagai alternatif, kita boleh menetapkan CLOUD_FOUNDRY_CONFIG_PATH. Atau, kita boleh menentukan lokasi terpencil dengan UAA_CONFIG_URL.

Kemudian, kami dapat menyalin konfigurasi yang diperlukan UAA ke dalam jalan konfigurasi kami:

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

Perhatikan bahawa kami akan menghapus tiga baris terakhir kerana kami akan menggantikannya sebentar lagi.

3.3. Mengkonfigurasi Sumber Data

Oleh itu, mari kita konfigurasikan sumber data, di mana UAA akan menyimpan maklumat mengenai pelanggan.

Untuk tujuan tutorial ini, kita akan menggunakan HSQLDB:

export SPRING_PROFILES="default,hsqldb"

Sudah tentu, kerana ini adalah aplikasi Spring Boot, kami juga dapat menentukannya dalam uaa.yml sebagai properti spring.profiles .

3.4. Mengkonfigurasi Pasangan Kunci JWS

Oleh kerana kami menggunakan JWT, UAA perlu mempunyai kunci peribadi untuk menandatangani setiap JWT yang dikeluarkan oleh UAA.

OpenSSL menjadikannya mudah:

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

Pelayan kebenaran akan menandatangani JWT dengan kunci peribadi, dan pelanggan dan pelayan sumber kami akan mengesahkan tandatangan itu dengan kunci awam.

Kami akan mengeksportnya 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 dapat menentukannya dalam uaa.yml melalui sifat-sifat jwt.token.signing-key dan jwt.token.verification-key .

3.5. Memulakan UAA

Akhirnya, mari kita mulakan perkara:

$CATALINA_HOME/bin/catalina.sh run

Pada ketika ini, kita mesti mempunyai pelayan UAA yang berfungsi di // localhost: 8080 / uaa .

Sekiranya kita pergi ke // localhost: 8080 / uaa / info , maka kita akan melihat beberapa maklumat asas permulaan

3.6. Memasang Pelanggan Command-Line UAA

Pelanggan UA-Command-Line CF UAA adalah alat utama untuk mentadbir UAA , tetapi untuk menggunakannya, kita perlu memasang 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 memulakan dengan gambaran ringkas mengenai OAuth 2.0 kerana ia merupakan asas asas untuk UAA, Pelayan Pengesahan OAuth 2.0. Kemudian, kami mengkonfigurasinya untuk mengeluarkan token akses untuk pelanggan dan mendapatkan aplikasi pelayan sumber.

Kod sumber lengkap untuk contoh boleh didapati di Github.