1. 概要

前回のSpringCloudの記事では、アプリケーションにZipkinのサポートを追加しました。 この記事では、フロントエンドアプリケーションをスタックに追加します。

これまで、私たちはクラウドアプリケーションを構築するために完全にバックエンドに取り組んできました。 しかし、UIがない場合、Webアプリはどのようなメリットがありますか? この記事では、シングルページアプリケーションをプロジェクトに統合することで、この問題を解決します。

このアプリは、AngularBootstrapを使用して作成します。 Angular 4コードのスタイルは、Spring開発者にとって自然なクロスオーバーであるSpringアプリのコーディングによく似ています。 フロントエンドコードはAngularを使用しますが、この記事の内容は最小限の労力で任意のフロントエンドフレームワークに簡単に拡張できます。

この記事では、Angular 4アプリを作成し、それをクラウドサービスに接続します。 SPAとSpringSecurityの間でログインを統合する方法を示します。 また、AngularのHTTP通信のサポートを使用してアプリケーションのデータにアクセスする方法も示します。

2. ゲートウェイの変更

フロントエンドが整ったら、フォームベースのログインに切り替えて、UIの一部を特権ユーザーに保護します。 これには、ゲートウェイのセキュリティ構成を変更する必要があります。

2.1. HttpSecurityを更新します

まず、ゲートウェイSecurityConfig。javaクラスのconfigure(HttpSecurity http)メソッドを更新しましょう。

@Override
protected void configure(HttpSecurity http) {
    http
      .formLogin()
      .defaultSuccessUrl("/home/index.html", true)
      .and()
    .authorizeRequests()
      .antMatchers("/book-service/**", "/rating-service/**", "/login*", "/")
      .permitAll()
      .antMatchers("/eureka/**").hasRole("ADMIN")
      .anyRequest().authenticated()
      .and()
    .logout()
      .and()
    .csrf().disable();
}

まず、 /home/index.htmlを指すデフォルトの成功URLを追加します。これは、Angularアプリが存在する場所になるためです。 次に、 Eureka リソースを除いて、ゲートウェイを介したすべての要求を許可するようにantマッチャーを構成します。 これにより、すべてのセキュリティチェックがバックエンドサービスに委任されます。

次に、ログインページへのデフォルトのリダイレクトが正常に機能するため、ログアウト成功URLを削除しました。

2.2. プリンシパルエンドポイントを追加する

次に、認証されたユーザーを返すエンドポイントを追加しましょう。 これは、Angularアプリでログインし、ユーザーの役割を識別するために使用されます。 これは、彼らが私たちのサイトで実行できるアクションを制御するのに役立ちます。

ゲートウェイプロジェクトで、AuthenticationControllerクラスを追加します。

@RestController
public class AuthenticationController {
 
    @GetMapping("/me")
    public Principal getMyUser(Principal principal) {
        return principal;
    }
}

コントローラは、現在ログインしているユーザーオブジェクトを呼び出し元に返します。 これにより、Angularアプリを制御するために必要なすべての情報が得られます。

2.3. ランディングページを追加する

非常に単純なランディングページを追加して、ユーザーがアプリケーションのルートにアクセスしたときに何かが表示されるようにします。

src / main / resources / static、に、ログインページへのリンクを含むindex.htmlファイルを追加しましょう。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Book Rater Landing</title>
</head>
<body>
    <h1>Book Rater</h1>
    <p>So many great things about the books</p>
    <a href="/login">Login</a>
</body>
</html>

3. AngularCLIとスタータープロジェクト

新しいAngularプロジェクトを開始する前に、必ず最新バージョンのNode.jsとnpmをインストールしてください。

3.1. AngularCLIをインストールします

まず、 npm を使用して、Angularコマンドラインインターフェイスをダウンロードしてインストールする必要があります。 ターミナルを開いて実行します。

npm install -g @angular/cli

これにより、CLIがグローバルにダウンロードおよびインストールされます。

3.2. 新しいプロジェクトをインストールする

ターミナルにいる間に、ゲートウェイプロジェクトに移動し、gateway / src/mainフォルダーに移動します。 「angular」というディレクトリを作成し、そこに移動します。 ここから実行:

ng new ui

我慢して; CLIが新しいプロジェクトをセットアップし、npmを使用してすべてのJavaScript依存関係をダウンロードします。 このプロセスに何分もかかることは珍しくありません。

ngコマンドはAngularCLIのショートカットであり、 new パラメーターはCLIに新しいプロジェクトを作成するように指示し、uiコマンドはプロジェクトに名前。

3.3. プロジェクトを実行する

newコマンドが完了したら。 作成して実行したuiフォルダーに移動します。

ng serve

プロジェクトがビルドされたら、http:// localhost:4200に移動します。 これはブラウザに表示されます。

おめでとう! Angularアプリを作成しました!

3.4. ブートストラップをインストールします

npmを使用してブートストラップをインストールしましょう。 uiディレクトリから次のコマンドを実行します。

npm install [email protected] --save

これにより、ブートストラップがnode_modulesフォルダーにダウンロードされます。

ui ディレクトリで、.angular-cli.jsonファイルを開きます。 これは、プロジェクトに関するいくつかのプロパティを構成するファイルです。 apps> styles プロパティを見つけて、BootstrapCSSクラスのファイルの場所を追加します。

"styles": [
    "styles.css",
    "../node_modules/bootstrap/dist/css/bootstrap.min.css"
],

これにより、プロジェクトでビルドされたコンパイル済みCSSファイルにBootstrapを含めるようにAngularに指示されます。

3.5. ビルド出力ディレクトリを設定します

次に、Spring Bootアプリがビルドファイルを提供できるように、ビルドファイルを配置する場所をAngularに指示する必要があります。 Spring Bootは、resourcesフォルダー内の2つの場所からファイルを提供できます。

  • src / main / resources / static
  • src / main / resource / public

すでに静的フォルダーを使用してEurekaのリソースを提供しており、Angularはビルドが実行されるたびにこのフォルダーを削除するため、Angularアプリをパブリックフォルダーにビルドしましょう。

.angular-cli.json ファイルを再度開き、 apps>outDirプロパティを見つけます。 その文字列を更新します:

"outDir": "../../resources/static/home",

Angularプロジェクトがsrc/main / angular / uiにある場合、プロジェクトはsrc / main / resources/publicフォルダーにビルドされます。 アプリが別のフォルダーにある場合は、場所を正しく設定するためにこの文字列を変更する必要があります。

3.6. Mavenでビルドを自動化する

最後に、コードをコンパイルするときに実行する自動ビルドを設定します。 このantタスクは、「mvncompile」が実行されるたびにAngularCLIビルドタスクを実行します。 このステップをゲートウェイのPOM.xmlに追加して、コンパイルするたびに最新のUI変更を確実に取得できるようにします。

<plugin>
    <artifactId>maven-antrun-plugin</artifactId>
    <executions>
        <execution>
            <phase>generate-resources</phase>
            <configuration>
                <tasks>
                    <exec executable="cmd" osfamily="windows"
                      dir="${project.basedir}/src/main/angular/ui">
                        <arg value="/c"/>
                        <arg value="ng"/>
                        <arg value="build"/>
                    </exec>
                    <exec executable="/bin/sh" osfamily="mac"
                      dir="${project.basedir}/src/main/angular/ui">
                        <arg value="-c"/>
                        <arg value="ng build"/>
                    </exec>
                </tasks>
            </configuration>
            <goals>
                <goal>run</goal>
            </goals>
        </execution>
    </executions>
</plugin>

この設定では、クラスパスでAngularCLIが使用可能である必要があることに注意してください。 このスクリプトをその依存関係のない環境にプッシュすると、ビルドが失敗します。

それでは、Angularアプリケーションの作成を始めましょう。

4. Angular

チュートリアルのこのセクションでは、ページに認証メカニズムを構築します。 基本認証を使用し、簡単なフローに従って機能させます。

ユーザーには、ユーザー名とパスワードを入力できるログインフォームがあります。

次に、それらの資格情報を使用してbase64認証トークンを作成し、“ / me”エンドポイントを要求します。 エンドポイントは、このユーザーの役割を含むPrincipalオブジェクトを返します。

最後に、後続のリクエストで使用するために、クレデンシャルとプリンシパルをクライアントに保存します。

これがどのように行われるか見てみましょう!

4.1. テンプレート

ゲートウェイプロジェクトで、 src / main / angle / ui / src / app に移動し、app.component.htmlファイルを開きます。 これはAngularがロードする最初のテンプレートであり、ユーザーがログインした後に着陸する場所になります。

ここでは、ログインフォームでナビゲーションバーを表示するためのコードを追加します。

<nav class="navbar navbar-toggleable-md navbar-inverse fixed-top bg-inverse">
    <button class="navbar-toggler navbar-toggler-right" type="button" 
      data-toggle="collapse" data-target="#navbarCollapse" 
      aria-controls="navbarCollapse" aria-expanded="false" 
      aria-label="Toggle navigation">
    <span class="navbar-toggler-icon"></span>
    </button>
    <a class="navbar-brand" href="#">Book Rater 
        <span *ngIf="principal.isAdmin()">Admin</span></a>
    <div class="collapse navbar-collapse" id="navbarCollapse">
    <ul class="navbar-nav mr-auto">
    </ul>
    <button *ngIf="principal.authenticated" type="button" 
      class="btn btn-link" (click)="onLogout()">Logout</button>
    </div>
</nav>

<div class="jumbotron">
    <div class="container">
        <h1>Book Rater App</h1>
        <p *ngIf="!principal.authenticated" class="lead">
        Anyone can view the books.
        </p>
        <p *ngIf="principal.authenticated && !principal.isAdmin()" class="lead">
        Users can view and create ratings</p>
        <p *ngIf="principal.isAdmin()"  class="lead">Admins can do anything!</p>
    </div>
</div>

このコードは、Bootstrapクラスを使用してナビゲーションバーを設定します。 バーにはインラインログインフォームが埋め込まれています。 Angularはこのマークアップを使用してJavaScriptと動的に対話し、ページのさまざまな部分をレンダリングし、フォームの送信などを制御します。

(ngSubmit)=” onLogin(f)” のようなステートメントは、フォームが送信されたときにメソッド“ onLogin(f)” を呼び出し、フォームをその関数に渡すことを示しています。 ジャンボトロンdiv内には、主要オブジェクトの状態に応じて動的に表示される段落タグがあります。

次に、このテンプレートをサポートするTypescriptファイルをコーディングしましょう。

4.2. Typescript

同じディレクトリからapp.component.tsファイルを開きます。 このファイルでは、テンプレート関数を作成するために必要なすべてのtypescriptプロパティとメソッドを追加します。

import {Component} from "@angular/core";
import {Principal} from "./principal";
import {Response} from "@angular/http";
import {Book} from "./book";
import {HttpService} from "./http.service";

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent {
    selectedBook: Book = null;
    principal: Principal = new Principal(false, []);
    loginFailed: boolean = false;

    constructor(private httpService: HttpService){}

    ngOnInit(): void {
        this.httpService.me()
          .subscribe((response: Response) => {
              let principalJson = response.json();
              this.principal = new Principal(principalJson.authenticated,
              principalJson.authorities);
          }, (error) => {
              console.log(error);
        });
    }

    onLogout() {
        this.httpService.logout()
          .subscribe((response: Response) => {
              if (response.status === 200) {
                  this.loginFailed = false;
                  this.principal = new Principal(false, []);
                  window.location.replace(response.url);
              }
           }, (error) => {
               console.log(error);
       });
    }
}

このクラスは、Angularライフサイクルメソッド ngOnInit()にフックします。 このメソッドでは、 / me エンドポイントを呼び出して、ユーザーの現在の役割と状態を取得します。 これにより、ユーザーがメインページに表示する内容が決まります。 このメソッドは、このコンポーネントが作成されるたびに起動されます。これは、アプリの権限についてユーザーのプロパティを確認する絶好の機会です。

また、 onLogout()メソッドを使用して、ユーザーをログアウトし、このページの状態を元の設定に復元します。

ただし、ここではいくつかの魔法が起こっています。 コンストラクターで宣言されているhttpServiceプロパティ。 Angularは、実行時にこのプロパティをクラスに注入しています。 Angularは、サービスクラスのシングルトンインスタンスを管理し、Springと同じように、コンストラクターインジェクションを使用してそれらをインジェクトします。

次に、HttpServiceクラスを定義する必要があります。

4.3. HttpService

同じディレクトリに、“ http.service.ts”という名前のファイルを作成します。 このファイルに次のコードを追加して、ログインとログアウトの方法をサポートします。

import {Injectable} from "@angular/core";
import {Observable} from "rxjs";
import {Response, Http, Headers, RequestOptions} from "@angular/http";
import {Book} from "./book";
import {Rating} from "./rating";

@Injectable()
export class HttpService {

    constructor(private http: Http) { }

    me(): Observable<Response> {
        return this.http.get("/me", this.makeOptions())
    }

    logout(): Observable<Response> {
        return this.http.post("/logout", '', this.makeOptions())
    }

    private makeOptions(): RequestOptions {
        let headers = new Headers({'Content-Type': 'application/json'});
        return new RequestOptions({headers: headers});
    }
}

このクラスでは、AngularのDI構造を使用して別の依存関係を注入しています。 今回はHttpクラスです。 このクラスはすべてのHTTP通信を処理し、フレームワークによって提供されます。

これらのメソッドはそれぞれ、angularのHTTPライブラリを使用してHTTPリクエストを実行します。 各リクエストは、ヘッダーのコンテンツタイプも指定します。

次に、依存性注入システムに HttpService を登録するために、もう1つ行う必要があります。 app.module.ts ファイルを開き、プロバイダーのプロパティを見つけます。 そのアレイにHttpServiceを追加します。 結果は次のようになります。

providers: [HttpService],

4.4. プリンシパルを追加

次に、TypescriptコードにプリンシパルDTOオブジェクトを追加しましょう。 同じディレクトリに「principal.ts」というファイルを追加し、次のコードを追加します。

export class Principal {
    public authenticated: boolean;
    public authorities: Authority[] = [];
    public credentials: any;

    constructor(authenticated: boolean, authorities: any[], credentials: any) {
        this.authenticated = authenticated;
        authorities.map(
          auth => this.authorities.push(new Authority(auth.authority)))
        this.credentials = credentials;
  }

    isAdmin() {
        return this.authorities.some(
          (auth: Authority) => auth.authority.indexOf('ADMIN') > -1)
    }
}

export class Authority {
    public authority: String;

    constructor(authority: String) {
        this.authority = authority;
    }
}

PrincipalクラスとAuthorityクラスを追加しました。 これらは2つのDTOクラスであり、SpringアプリのPOJOによく似ています。 そのため、これらのクラスを角度でDIシステムに登録する必要はありません。

次に、不明なリクエストをアプリケーションのルートにリダイレクトするようにリダイレクトルールを設定しましょう。

4.5. 404取り扱い

ゲートウェイサービスのJavaコードに戻りましょう。 GatewayApplication クラスが存在する場所に、ErrorPageConfigという新しいクラスを追加します。

@Component
public class ErrorPageConfig implements ErrorPageRegistrar {
 
    @Override
    public void registerErrorPages(ErrorPageRegistry registry) {
        registry.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND,
          "/home/index.html"));
    }

}

このクラスは404応答を識別し、ユーザーを“ /home/index.html”にリダイレクトします。 シングルページアプリでは、クライアントがすべてのナビゲート可能なルートを処理する必要があるため、これは専用リソースに送信されないすべてのトラフィックを処理する方法です。

これで、このアプリを起動して、作成したものを確認する準備が整いました。

4.6. ビルドして表示

次に、ゲートウェイフォルダーから「mvncompile」を実行します。 これにより、Javaソースがコンパイルされ、Angularアプリがパブリックフォルダーにビルドされます。 他のクラウドアプリケーションを始めましょう: config Discovery 、およびzipkin。 次に、ゲートウェイプロジェクトを実行します。 サービスが開始したら、 http:// localhost:8080に移動してアプリを表示します。 次のようなものが表示されます。

次に、ログインページへのリンクをたどってみましょう。

ユーザー/パスワードのクレデンシャルを使用してログインします。 「ログイン」をクリックすると、シングルページアプリが読み込まれる/home/index.htmlにリダイレクトされます。

ジャンボトロンは、ユーザーとしてログインしていることを示しているようです。 次に、右上隅のリンクをクリックしてログアウトし、今回は admin /admin資格情報を使用してログインします。

いいね! これで、管理者としてログインしました。

5. 結論

この記事では、シングルページアプリをクラウドシステムに統合するのがいかに簡単かを見てきました。 最新のフレームワークを採用し、機能するセキュリティ構成をアプリケーションに統合しました。

これらの例を使用して、book-serviceまたはrating-serviceを呼び出すコードを記述してみてください。 HTTP呼び出しを行い、データをテンプレートに接続する例があるので、これは比較的簡単です。

サイトの残りの部分がいつものように構築されていることを確認したい場合は、Githubでソースコードを見つけることができます。