1. 概要

Cloud Foundryユーザーアカウントと認証(CF UAA)は、ID管理と承認サービスです。より正確には、認証を許可し、クライアントアプリケーションにトークンを発行できるOAuth2.0プロバイダーです。

このチュートリアルでは、CFUAAサーバーのセットアップの基本について説明します。 次に、ResourceServerアプリケーションを保護するためにそれを使用する方法を見ていきます。

ただし、その前に、 OAuth2.0認証フレームワークでのUAAの役割を明確にしましょう。

2. CloudFoundryUAAおよびOAuth2.0

UAAがOAuth2.0仕様にどのように関連しているかを理解することから始めましょう。

OAuth 2.0仕様では、互いに接続できる 4つの参加者(リソース所有者、リソースサーバー、クライアント、および承認サーバー)が定義されています。

OAuth 2.0プロバイダーとして、UAAは承認サーバー。 これの意味はその主な目標は、クライアントアプリケーションのアクセストークンを発行し、リソースサーバーのこれらのトークンを検証することです。

これらの参加者の相互作用を可能にするには、最初にUAAサーバーをセットアップしてから、さらに2つのアプリケーションを実装する必要があります。1つはクライアントとして、もう1つはリソースサーバーとしてです。

クライアントでauthorization_codegrantフローを使用します。 また、リソースサーバーでベアラートークン認証を使用します。 より安全で効率的なハンドシェイクのために、署名されたJWTをアクセストークンとして使用します。

3. UAAサーバーのセットアップ

まず、 UAAをインストールし、デモデータを入力します。

インストールしたら、 webappclient。という名前のクライアントアプリケーションを登録します。次に、resource.readとresource.readの2つの役割を持つappuserという名前のユーザーを作成します。 X173X]resource.write。

3.1. インストール

UAAは、準拠したサーブレットコンテナで実行できるJavaWebアプリケーションです。 このチュートリアルでは、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から構成を読み取ります。ただし、 war ファイルをダウンロードしたばかりなので、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.3. データソースの構成

それでは、UAAがクライアントに関する情報を保存するデータソースを構成しましょう。

このチュートリアルでは、HSQLDBを使用します。

export SPRING_PROFILES="default,hsqldb"

もちろん、これはSpring Bootアプリケーションであるため、uaa.ymlspring.profilesプロパティとして指定することもできます。

3.4. JWSキーペアの構成

JWTを使用しているため、 UAAは、UAAが発行する各JWTに署名するための秘密鍵を持っている必要があります。

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)

ここでも、jwt.token.signing-keyおよびjwt.token.verification-keyプロパティを介してuaa.ymlでこれらを指定できます。

3.5. UAAの起動

最後に、物事を始めましょう:

$CATALINA_HOME/bin/catalina.sh run

この時点で、 http:// localhost:8080 /uaaでUAAサーバーが機能しているはずです。

http:// localhost:8080 / uaa / info にアクセスすると、基本的なスタートアップ情報が表示されます。

3.6. UAAコマンドラインクライアントのインストール

CFUAAコマンドラインクライアントはUAAを管理するためのメインツールですが、これを使用するには、最初にRubyをインストールする必要があります。

sudo apt install rubygems
gem install cf-uaac

次に、UAAの実行中のインスタンスを指すようにuaacを構成できます。

uaac target http://localhost:8080/uaa

コマンドラインクライアントを使用したくない場合は、もちろん、UAAのHTTPクライアントを使用できることに注意してください。

3.7. UAACを使用したクライアントとユーザーの設定

uaac がインストールされたので、UAAにデモデータを入力してみましょう。 少なくとも、次のものが必要です。クライアント、ユーザー、およびresource.readグループとresource.writeグループ。

したがって、管理を行うには、自分で認証する必要があります。 UAAに同梱されているデフォルトの管理者を選択します。この管理者には、他のクライアント、ユーザー、およびグループを作成する権限があります:

uaac token client get admin -s adminsecret

(もちろん、このアカウントを変更する必要があります oauth-clients.xml ファイルを介して–出荷する前に!)

基本的に、このコマンドは次のように読み取ることができます。 adminsecret“のecret。

すべてがうまくいけば、成功メッセージが表示されます。

Successfully fetched token via client credentials grant.

トークンはuaacの状態で保存されます。

これで、 admin として動作し、webappclientという名前のクライアントをclientadd:に登録できます。

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という名前のユーザーをuseradd:に登録することもできます。

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

次に、with group add:を使用して、resource.readresource.writeの2つのグループを追加します。

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

最後に、これらのグループを appuser に割り当て、メンバーを追加します:

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

ふぅ! したがって、これまでに行ったことは次のとおりです。

  • インストールおよび構成されたUAA
  • uaacをインストールしました
  • デモクライアント、ユーザー、およびグループを追加しました

したがって、これらの情報を念頭に置いて、次のステップに進みましょう。

4. OAuth2.0クライアント

このセクションでは、Spring Bootを使用してOAuth2.0クライアントアプリケーションを作成します。

4.1. アプリケーションのセットアップ

Spring Initializr にアクセスし、Spring BootWebアプリケーションを生成することから始めましょう。 WebおよびOAuth2クライアントコンポーネントのみを選択します。

<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を指定する必要があります。 また、このクライアントがユーザーに付与するOAuth2.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

これらのプロパティの詳細については、登録およびプロバイダーBeanのJavaドキュメントを参照してください。

また、UAAにはすでにポート8080を使用しているので、8081でこれを実行してみましょう。

server.port=8081

4.2. ログイン

これで、 / login パスにアクセスすると、登録されているすべてのクライアントのリストが表示されます。 この場合、登録されているクライアントは1つだけです。


リンクをクリックすると、UAAログインページにリダイレクトされます。

ここで、 appuser /appusersecretでログインしましょう。

フォームを送信すると、ユーザーがクライアントへのアクセスを承認または拒否できる承認フォームにリダイレクトされます。

その後、ユーザーは必要な特権を付与できます。 ここでは、 resource:writeを除くすべてを選択します。

ユーザーがチェックするものはすべて、結果のアクセストークンのスコープになります。

これを証明するために、インデックスパス http:// localhost:8081 に表示されているトークンをコピーし、JWTデバッガーを使用してデコードできます。 承認ページで確認したスコープが表示されます:

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

クライアントアプリケーションがこのトークンを受信すると、ユーザーを認証できるようになり、ユーザーはアプリにアクセスできるようになります。

さて、データを表示しないアプリはあまり役に立たないので、次のステップは、ユーザーのデータを持っているリソースサーバーを立ち上げ、クライアントをそれに接続することです。

完成したリソースサーバーには、2つの保護されたAPIがあります。1つは resource.read スコープを必要とし、もう1つはresource.write。を必要とします。

クライアントは、付与したスコープを使用して、読み取りAPIを呼び出すことはできますが、書き込みはできません。

5. リソースサーバー

リソースサーバーは、ユーザーの保護されたリソースをホストします。

Authorization ヘッダーを介して、認証サーバー(この場合はUAA)と連携してクライアントを認証します。

5.1. アプリケーションの設定

リソースサーバーを作成するには、 SpringInitializrを再度使用してSpring BootWebアプリケーションを生成します。 今回は、WebおよびOAuth2リソースサーバーコンポーネントを選択します。

<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を使用しています。

次のステップは、application.propertiesファイルで実行中のCFUAAの場所を示すことです。

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

もちろん、ここでも新しいポートを選びましょう。 8082は正常に動作します:

server.port=8082

以上です! リソースサーバーが機能している必要があります。デフォルトでは、すべてのリクエストで承認ヘッダーに有効なアクセストークンが必要です。

5.2. リソースサーバーAPIの保護

次に、保護する価値のあるエンドポイントをいくつか追加しましょう。

2つのエンドポイントを持つRestControllerを追加します。1つはresource.read スコープを持つユーザー用に承認され、もう1つは 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構成をオーバーライドして、2つのリソースを保護します。

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

アクセストークンで提供されるスコープは、Spring Security GrantedAuthority に変換されるときに、接頭辞SCOPE_が付けられることに注意してください。

5.3. クライアントからの保護されたリソースの要求

クライアントアプリケーションから、を使用して2つの保護されたリソースを呼び出します RestTemplate。 リクエストを行う前に、コンテキストからアクセストークンを取得し、 承認ヘッダ

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

ただし、RestTemplateの代わりにWebClientを使用すると、この定型文を削除できることに注意してください。

次に、リソースサーバーエンドポイントに2つの呼び出しを追加します。

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

予想どおり、 / read APIの呼び出しは成功しますが、/ write APIの呼び出しは成功しません。HTTPステータス403は、ユーザーが許可されていないことを示しています。

6. 結論

この記事では、OAuth 2.0認証サーバーであるUAAの基本基盤である、OAuth2.0の概要から始めました。 次に、クライアントにアクセストークンを発行し、リソースサーバーアプリケーションを保護するように構成しました。

例の完全なソースコードは、Githubから入手できます。