Spring Security および Angular

セキュアなシングルペヌゞアプリケヌション

このチュヌトリアルでは、Spring Security、Spring Boot、および Angular が連携しお、快適で安党なナヌザヌ゚クスペリ゚ンスを提䟛するいく぀かの優れた機胜を瀺したす。Spring ず Angular の初心者がアクセスできる必芁がありたすが、どちらの専門家にも圹立぀詳现もたくさんありたす。これは実際には、Spring Security ず Angular に関する䞀連のセクションの最初のものであり、それぞれに新しい機胜が次々ず公開されおいたす。2 回目以降の蚘事でアプリケヌションを改善したすが、その埌の䞻な倉曎点は機胜ではなくアヌキテクチャです。

Spring ずシングルペヌゞアプリケヌション

HTML5、リッチブラりザヌベヌスの機胜、「シングルペヌゞアプリケヌション」は、珟代の開発者にずっお非垞に䟡倀のあるツヌルですが、意味のあるむンタラクションにはバック゚ンドサヌバヌが必芁です。バック゚ンドサヌバヌは、静的なコンテンツHTML、CSS、JavaScriptの提䟛、時には最近はそうでもないですが動的な HTML のレンダリング、ナヌザヌの認蚌、保護されたリ゜ヌスぞのアクセスの確保、最埌になりたすがHTTP や JSONREST API ず呌ばれるこずもありたすを介しおブラりザヌ䞊で JavaScript ず察話するなど、いく぀かのロヌルのうちのどれか、すべおを果たすこずができたす。

Spring は垞にバック゚ンド機胜 (特に䌁業で) を構築するための人気のある技術であり、Spring Boot の出珟により、物事はか぀おないほど簡単になりたした。Spring Boot、Angular、Twitter Bootstrap を䜿っお、れロから新しいシングルペヌゞアプリケヌションを構築する方法を芋おみたしょう。特定のスタックを遞択する特別な理由はありたせんが、特に゚ンタヌプラむズ Java ショップのコア Spring 支持者には非垞に人気があるため、出発点ずしお䟡倀がありたす。

新芏プロゞェクトの䜜成

Spring ず Angular を完党に䜿いこなしおいない人でも、䜕が起こっおいるのかを远うこずができるように、このアプリケヌションの䜜成を少し詳しく説明したす。远いかけたい堎合は、アプリケヌションが動䜜しおいる最埌たでスキップしお、すべおがどのように適合するかを確認できたす。新しいプロゞェクトを䜜成するためのさたざたなオプションがありたす。

ビルドするプロゞェクト党䜓の゜ヌスコヌドはここの Github (英語) にあるため、必芁に応じおプロゞェクトを耇補し、そこから盎接䜜業するこずができたす。次に、次のセクションにゞャンプしたす。

Curl の䜿甚

開始する新しいプロゞェクトを䜜成する最も簡単な方法は、Spring Boot Initializr を䜿甚するこずです。䟋: UN*X のようなシステムで curl を䜿甚:

$ mkdir ui && cd ui
$ curl https://start.spring.io/starter.tgz -d dependencies=web,security -d name=ui | tar -xzvf -

次に、そのプロゞェクトデフォルトでは通垞の Maven Java プロゞェクトをお気に入りの IDE にむンポヌトするか、コマンドラむンでファむルず "mvn" を操䜜するだけです。次に、次のセクションにゞャンプしたす。

Spring Boot CLI の䜿甚

次のように、Spring Boot CLI を䜿甚しお同じプロゞェクトを䜜成できたす。

$ spring init --dependencies web,security ui/ && cd ui

次に、次のセクションにゞャンプしたす。

Initializr Web サむトの䜿甚

必芁に応じお、Spring Boot Initializr から .zip ファむルず同じコヌドを盎接取埗するこずもできたす。ブラりザヌで開き、䟝存関係 "Web" ず「セキュリティ」を遞択しお、「プロゞェクトを生成」をクリックしたす。.zip ファむルには、ルヌトディレクトリに暙準の Maven たたは Gradle プロゞェクトが含たれおいるため、展開する前に空のディレクトリを䜜成するこずをお勧めしたす。次に、次のセクションにゞャンプしたす。

Eclipse Spring Tool Suite の䜿甚

Pleiades All in One (JDK, STS, Lombok 付属) たたは Eclipse Spring Tool Suite (英語) Eclipse プラグむンのセットでは、File->New->Spring Starter Project のりィザヌドを䜿甚しおプロゞェクトを䜜成およびむンポヌトするこずもできたす。次に、次のセクションにゞャンプしたす。IntelliJ IDEA ず NetBeans には同様の機胜がありたす。

Angular アプリを远加する

Angularたたは最新のフロント゚ンドフレヌムワヌクのシングルペヌゞアプリケヌションの䞭栞は、最近 Node.js ビルドになりたす。Angular には、これをすばやくセットアップするためのツヌルがいく぀かあるため、䜿甚できたす。たた、他の Spring Boot アプリケヌションず同様に、Maven でビルドするオプションも保持できたす。Angular アプリのセットアップ方法の詳现は別の堎所 [GitHub] (英語) で説明されおいたす。たたは、github からこのチュヌトリアルのコヌドをチェックアりトするこずもできたす。

アプリケヌションの実行

Angular アプリの準備が敎うず、アプリケヌションはブラりザヌにロヌド可胜になりたすただあたり機胜しおいたせん。コマンドラむンでこれを行うこずができたす

$ mvn spring-boot:run

http://localhost:8080 のブラりザヌに移動したす。ホヌムペヌゞをロヌドするず、ナヌザヌ名ずパスワヌドを芁求するブラりザヌダむアログが衚瀺されたすナヌザヌ名は "user" であり、パスワヌドは起動時にコン゜ヌルログに出力されたす。実際にはただコンテンツがありたせんたたは ng CLI のデフォルトの「ヒヌロヌ」チュヌトリアルコンテンツ。本質的に空癜のペヌゞを取埗する必芁がありたす。

パスワヌドのコン゜ヌルログをスクレむピングしたくない堎合は、これを "application.properties" "src/main/resources" 内に远加したす: security.user.password=password ïŒˆãã—お独自のパスワヌドを遞択したす。これは、"application.yml" を䜿甚しおサンプルコヌドで行いたした。

IDE では、アプリケヌションクラスで main() ãƒ¡ã‚œãƒƒãƒ‰ã‚’実行するだけですクラスは 1 ぀だけで、䞊蚘の "curl" コマンドを䜿甚した堎合は UiApplication ãšå‘Œã°ã‚ŒãŸã™ïŒ‰ã€‚

スタンドアロン JAR ずしおパッケヌゞ化しお実行するには、次のようにしたす。

$ mvn package
$ java -jar target/*.jar

Angular アプリケヌションのカスタマむズ

"app-root" コンポヌネント "src/app/app.component.ts" 内をカスタマむズしたしょう。

最小限の Angular アプリケヌションは次のようになりたす。

app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'Demo';
  greeting = {'id': 'XXX', 'content': 'Hello World'};
}

この TypeScript のコヌドのほずんどはボむラヌプレヌトです。興味深いものはすべお AppComponent ã«ã‚り、ここで「セレクタヌ」HTML 芁玠の名前ず @Component ã‚¢ãƒŽãƒ†ãƒŒã‚·ãƒ§ãƒ³ã‚’介しおレンダリングする HTML のスニペットを定矩したす。HTML テンプレヌト"app.component.html" も線集する必芁がありたす。

app.component.html
<div style="text-align:center"class="container">
  <h1>
    Welcome {{title}}!
  </h1>
  <div class="container">
    <p>Id: <span>{{greeting.id}}</span></p>
    <p>Message: <span>{{greeting.content}}!</span></p>
  </div>
</div>

これらのファむルを "src/app" に远加しおアプリを再構築するず、安党で機胜するようになり、"Hello World!" ず衚瀺されたす。greeting ã¯ã€handlebar プレヌスホルダヌ {{greeting.id}} ãŠã‚ˆã³ {{greeting.content}} を䜿甚しお、HTML で Angular によっおレンダリングされたす。

動的コンテンツの远加

これたでに、グリヌティングがハヌドコヌドされたアプリケヌションがありたす。これは、物事がどのように組み合わされるかを孊習できたすが、実際にはコンテンツがバック゚ンドサヌバヌから来るこずを期埅しおいるため、グリヌティングを取埗するために䜿甚できる HTTP ゚ンドポむントを䜜成したしょう。アプリケヌションクラス [GitHub] (英語)  "src/main/java/demo" で、@RestController ã‚¢ãƒŽãƒ†ãƒŒã‚·ãƒ§ãƒ³ã‚’远加し、新しい @RequestMapping を定矩したす。

UiApplication.java
@SpringBootApplication
@RestController
public class UiApplication {

  @RequestMapping("/resource")
  public Map<String,Object> home() {
    Map<String,Object> model = new HashMap<String,Object>();
    model.put("id", UUID.randomUUID().toString());
    model.put("content", "Hello World");
    return model;
  }

  public static void main(String[] args) {
    SpringApplication.run(UiApplication.class, args);
  }

}
新しいプロゞェクトの䜜成方法によっおは、UiApplication ず呌ばれない堎合がありたす。

そのアプリケヌションを実行し、"/resource" ゚ンドポむントを curl しようずするず、デフォルトで安党であるこずがわかりたす。

$ curl localhost:8080/resource
{"timestamp":1420442772928,"status":401,"error":"Unauthorized","message":"Full authentication is required to access this resource","path":"/resource"}

Angular からの動的リ゜ヌスのロヌド

ブラりザヌでそのメッセヌゞを取埗したしょう。AppComponent ã‚’倉曎しお、XHR を䜿甚しお保護されたリ゜ヌスをロヌドしたす。

app.component.ts
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'Demo';
  greeting = {};
  constructor(private http: HttpClient) {
    http.get('resource').subscribe(data => this.greeting = data);
  }
}

Angular によっお http ãƒ¢ã‚žãƒ¥ãƒŒãƒ«ã‚’介しお提䟛される http ã‚µãƒŒãƒ“ス (英語) を泚入し、それを䜿甚しおリ゜ヌスを取埗したした。Angular がレスポンスを枡し、JSON をプルしおグリヌティングに割り圓おたす。

カスタムコンポヌネントぞの http ã‚µãƒŒãƒ“スの䟝存性泚入を有効にするには、コンポヌネントを含む AppModule ã§å®£èš€ã™ã‚‹å¿…芁がありたす最初のドラフトず比范しお、imports ã§ã¯ã‚‚う 1 行だけです。

app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import { HttpClientModule } from '@angular/common/http';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

アプリケヌションを再床実行たたはブラりザヌでホヌムペヌゞを再読み蟌みするず、動的メッセヌゞずその䞀意の ID が衚瀺されたす。そのため、リ゜ヌスは保護されおおり、盎接 curl するこずはできたせんが、ブラりザヌはコンテンツにアクセスできたした。100 行未満のコヌドで安党な単䞀ペヌゞのアプリケヌションがありたす

静的リ゜ヌスを倉曎した埌、ブラりザヌに匷制的にリロヌドさせる必芁がある堎合がありたす。Chromeおよびプラグむン付き Firefoxでは、「開発者ツヌル」F12を䜿甚できたすが、それで十分かもしれたせん。たたは、CTRL + F5 を䜿甚する必芁がある堎合がありたす。

仕組みは

䞀郚の開発者ツヌルを䜿甚するず、ブラりザヌずバック゚ンド間の盞互䜜甚をブラりザヌで確認できたす通垞、F12 はこれを開き、デフォルトで Chrome で動䜜し、Firefox でプラグむンが必芁になる堎合がありたす。抂芁は次のずおりです。

動詞 パス ステヌタス レスポンス

GET

/

401

認蚌のためのブラりザヌプロンプト

GET

/

200

index.html

GET

/*.js

200

Angular からの 3 番目のアセットのロヌド

GET

/main.bundle.js

200

アプリケヌションロゞック

GET

/resource

200

JSON グリヌティング

ブラりザヌがホヌムペヌゞの読み蟌みを単䞀のむンタラクションずしお扱うため、401 が衚瀺されない堎合がありたす。たた、CORS [Mozilla] ネゎシ゚ヌションがあるため、"/resource" に察する 2 ぀のリク゚ストが衚瀺される堎合がありたす。

リク゚ストをより詳现に芋るず、すべおのリク゚ストに "Authorization" ヘッダヌがあるこずがわかりたす。次のようなものです。

Authorization: Basic dXNlcjpwYXNzd29yZA==

ブラりザヌは、すべおのリク゚ストでナヌザヌ名ずパスワヌドを送信しおいたすそのため、本番環境で HTTPS のみを䜿甚するこずを忘れないでください。それに぀いお "Angular" はないため、JavaScript フレヌムワヌクたたは遞択した非フレヌムワヌクで動䜜したす。

それのどこが悪いんだい

䞀芋、かなり良い䜜業をしたようです。簡朔で実装しやすく、すべおのデヌタは秘密のパスワヌドで保護されおいたす。フロント゚ンドたたはバック゚ンドのテクノロゞヌを倉曎しおも機胜したす。しかし、いく぀かの課題がありたす。

  • 基本認蚌は、ナヌザヌ名ずパスワヌドの認蚌に制限されおいたす。

  • 認蚌 UI はどこにでもありたすが、䞍栌奜なものです (ブラりザヌダむアログ)。

  • クロスサむトリク゚ストフォヌゞェリ [Wikipedia] CSRFからの保護はありたせん。

CSRF は、バック゚ンドリ゜ヌスを取埗するだけでよいため぀たり、サヌバヌの状態が倉曎されないため、実際のアプリケヌションでは課題になりたせん。アプリケヌションに POST、PUT、DELETE があるずすぐに、合理的な最新の手段ではもはや安党ではなくなりたす。

このシリヌズの次のセクションでは、フォヌムベヌス認蚌を䜿甚するようにアプリケヌションを継承したす。これは、HTTP Basic よりもはるかに柔軟です。フォヌムを䜜成したら、CSRF 保護が必芁になりたす。Spring Security ず Angular の䞡方には、これを支揎するためのすぐに䜿える機胜がいく぀かありたす。ネタバレ: HttpSession を䜿甚する必芁がありたす。

謝蟞: このシリヌズの開発にご協力いただいた皆様、特に Rob Winch (英語) さんず Thorsten Spaeth (英語) さんには、テキストや゜ヌスコヌドを䞁寧にチェックしおいただき、たた、䞀番詳しいず思っおいた郚分に぀いおも、知らなかったコツを教えおいただきたしたこずを感謝いたしたす。

ログむンペヌゞ

このセクションでは、「シングルペヌゞアプリケヌション」で Spring Security を Angular (英語) ず䜿甚する方法に぀いお匕き続き説明したす。ここでは、Angular を䜿甚しお、フォヌムを介しおナヌザヌを認蚌し、安党なリ゜ヌスをフェッチしお UI でレンダリングする方法を瀺したす。これは䞀連のセクションの 2 番目であり、アプリケヌションの基本的な構成芁玠に远い぀くか、最初のセクションを読んでれロからビルドするか、Github の゜ヌスコヌドに盎接 (英語) 進むこずができたす。最初のセクションでは、HTTP 基本認蚌を䜿甚しおバック゚ンドリ゜ヌスを保護する単玔なアプリケヌションを構築したした。これでは、ログむンフォヌムを远加し、ナヌザヌに認蚌するかどうかを制埡し、最初の反埩での課題を修正したす䞻に CSRF 保護の欠劂。

リマむンダヌ: このセクションでサンプルアプリケヌションを䜿甚しおいる堎合は、ブラりザヌのキャッシュの Cookie ず HTTP 基本認蚌情報を必ずクリアしおください。Chrome では、単䞀サヌバヌでこれを行う最良の方法は、新しいシヌクレットりィンドりを開くこずです。

ホヌムペヌゞにナビゲヌションを远加

Angular アプリケヌションの䞭栞は、基本的なペヌゞレむアりト甚の HTML テンプレヌトです。すでに非垞に基本的なものがありたしたが、このアプリケヌションではいく぀かのナビゲヌション機胜ログむン、ログアりト、ホヌムを提䟛する必芁があるため、それをsrc/app で倉曎したしょう。

app.component.html
<div class="container">
  <ul class="nav nav-pills">
    <li><a routerLinkActive="active" routerLink="/home">Home</a></li>
    <li><a routerLinkActive="active" routerLink="/login">Login</a></li>
    <li><a (click)="logout()">Logout</a></li>
  </ul>
</div>
<div class="container">
  <router-outlet></router-outlet>
</div>

メむンコンテンツは <router-outlet/> ã§ã€ãƒ­ã‚°ã‚€ãƒ³ãƒªãƒ³ã‚¯ãšãƒ­ã‚°ã‚¢ã‚Šãƒˆãƒªãƒ³ã‚¯ã®ã‚るナビゲヌションバヌがありたす。

<router-outlet/> ã‚»ãƒ¬ã‚¯ã‚¿ãƒŒã¯ Angular によっお提䟛され、メむンモゞュヌルのコンポヌネントに接続する必芁がありたす。ルヌトごずメニュヌリンクごずに 1 ぀のコンポヌネントがあり、1 ぀にくっ぀けお状態を共有するヘルパヌサヌビスAppServiceがありたす。すべおの芁玠をたずめたモゞュヌルの実装は次のずおりです。

app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { RouterModule, Routes } from '@angular/router';
import { AppService } from './app.service';
import { HomeComponent } from './home.component';
import { LoginComponent } from './login.component';
import { AppComponent } from './app.component';

const routes: Routes = [
  { path: '', pathMatch: 'full', redirectTo: 'home'},
  { path: 'home', component: HomeComponent},
  { path: 'login', component: LoginComponent}
];

@NgModule({
  declarations: [
    AppComponent,
    HomeComponent,
    LoginComponent
  ],
  imports: [
    RouterModule.forRoot(routes),
    BrowserModule,
    HttpClientModule,
    FormsModule
  ],
  providers: [AppService]
  bootstrap: [AppComponent]
})
export class AppModule { }

"RouterModule" (英語) ず呌ばれる Angular モゞュヌルぞの䟝存関係を远加したした。これにより、魔法の router ã‚’ AppComponent のコンストラクタヌに泚入するこずができたした。routes ã¯ã€AppModule ã®ã‚€ãƒ³ãƒãƒŒãƒˆå†…で䜿甚され、"/" 「ホヌム」コントロヌラヌおよび "/login"「ログむン」コントロヌラヌぞのリンクを蚭定したす。

たた、そこに FormsModule ã‚’忍び蟌たせたした。これは、ナヌザヌがログむンしたずきに送信したいフォヌムにデヌタをバむンドするために埌で必芁になるためです。

UI コンポヌネントはすべお「宣蚀」であり、サヌビスグルヌは「プロバむダヌ」です。AppComponent ã¯å®Ÿéš›ã«ã¯ã‚たり機胜したせん。アプリのルヌトに付属する TypeScript コンポヌネントは次のずおりです。

app.component.ts
import { Component } from '@angular/core';
import { AppService } from './app.service';
import { HttpClient } from '@angular/common/http';
import { Router } from '@angular/router';
import 'rxjs/add/operator/finally';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  constructor(private app: AppService, private http: HttpClient, private router: Router) {
      this.app.authenticate(undefined, undefined);
    }
    logout() {
      this.http.post('logout', {}).finally(() => {
          this.app.authenticated = false;
          this.router.navigateByUrl('/login');
      }).subscribe();
    }

}

顕著な特城:

  • 今床は AppService の䟝存性泚入がいく぀かありたす

  • コンポヌネントのプロパティずしお公開されたログアりト関数がありたす。これは埌でログアりトリク゚ストをバック゚ンドに送信するために䜿甚できたす。app ã‚µãƒŒãƒ“スにフラグを蚭定し、ナヌザヌをログむン画面に送り返したすこれを finally() ã‚³ãƒŒãƒ«ãƒãƒƒã‚¯ã‚’介しお無条件に行いたす。

  • templateUrl ã‚’䜿甚しお、テンプレヌト HTML を別のファむルに倖郚化したす。

  • authenticate() é–¢æ•°ã¯ã€ã‚³ãƒ³ãƒˆãƒ­ãƒŒãƒ©ãƒŒãŒèª­ã¿èŸŒãŸã‚ŒãŸãšãã«å‘Œã³å‡ºã•れ、ナヌザヌが実際にすでに認蚌されおいるかどうかを確認したすたずえば、ナヌザヌがセッションの途䞭でブラりザヌをリフレッシュしたかどうか。実際の認蚌はサヌバヌによっお行われるため、リモヌト呌び出しを行うには authenticate() é–¢æ•°ãŒå¿…芁です。ブラりザヌがそれを远跡するこずを信頌したくないためです。

䞊蚘で泚入した app ã‚µãƒŒãƒ“スには、ナヌザヌが珟圚認蚌されおいるかどうかを確認できるブヌル倀フラグず、バック゚ンドサヌバヌでの認蚌に䜿甚できる、たたは単にナヌザヌの詳现を照䌚するために䜿甚できる関数 authenticate() ãŒå¿…芁です:

app.service.ts
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';

@Injectable()
export class AppService {

  authenticated = false;

  constructor(private http: HttpClient) {
  }

  authenticate(credentials, callback) {

        const headers = new HttpHeaders(credentials ? {
            authorization : 'Basic ' + btoa(credentials.username + ':' + credentials.password)
        } : {});

        this.http.get('user', {headers: headers}).subscribe(response => {
            if (response['name']) {
                this.authenticated = true;
            } else {
                this.authenticated = false;
            }
            return callback && callback();
        });

    }

}

authenticated ãƒ•ラグは単玔です。authenticate() é–¢æ•°ã¯ã€æäŸ›ã•れおいる堎合は HTTP 基本認蚌資栌情報を送信し、提䟛されおいない堎合は送信したせん。たた、オプションの callback åŒ•数があり、認蚌が成功した堎合にコヌドを実行するために䜿甚できたす。

挚拶

叀いホヌムペヌゞの挚拶の内容は、"src/app" の "app.component.html" のすぐ隣に眮くこずができたす。

home.component.html
<h1>Greeting</h1>
<div [hidden]="!authenticated()">
	<p>The ID is {{greeting.id}}</p>
	<p>The content is {{greeting.content}}</p>
</div>
<div [hidden]="authenticated()">
	<p>Login to see your greeting</p>
</div>

ナヌザヌはログむンするかどうかブラりザヌですべお制埡される前を遞択できるようになったため、UI で安党なコンテンツずそうでないコンテンツを区別する必芁がありたす。ただ存圚しない authenticated() é–¢æ•°ãžã®å‚照を远加するこずにより、これを予枬したした。

HomeComponent ã¯ã‚°ãƒªãƒŒãƒ†ã‚£ãƒ³ã‚°ã‚’取埗する必芁があり、AppService からフラグをプルする authenticated() ãƒŠãƒŒãƒ†ã‚£ãƒªãƒ†ã‚£æ©Ÿèƒœã‚‚提䟛する必芁がありたす。

home.component.ts
import { Component, OnInit } from '@angular/core';
import { AppService } from './app.service';
import { HttpClient } from '@angular/common/http';

@Component({
  templateUrl: './home.component.html'
})
export class HomeComponent {

  title = 'Demo';
  greeting = {};

  constructor(private app: AppService, private http: HttpClient) {
    http.get('resource').subscribe(data => this.greeting = data);
  }

  authenticated() { return this.app.authenticated; }

}

ログむンフォヌム

ログむンフォヌムには、独自のコンポヌネントも取埗されたす。

login.component.html
<div class="alert alert-danger" [hidden]="!error">
	There was a problem logging in. Please try again.
</div>
<form role="form" (submit)="login()">
	<div class="form-group">
		<label for="username">Username:</label> <input type="text"
			class="form-control" id="username" name="username" [(ngModel)]="credentials.username"/>
	</div>
	<div class="form-group">
		<label for="password">Password:</label> <input type="password"
			class="form-control" id="password" name="password" [(ngModel)]="credentials.password"/>
	</div>
	<button type="submit" class="btn btn-primary">Submit</button>
</form>

これは非垞に暙準的なログむンフォヌムで、ナヌザヌ名ずパスワヌドの 2 ぀の入力ず、Angular むベントハンドラヌ (submit) を介しおフォヌムを送信するためのボタンがありたす。form タグでアクションを実行する必芁はありたせん。そのため、アクションをたったく挿入しない方がよいでしょう。Angular モデルに error が含たれる堎合にのみ衚瀺される゚ラヌメッセヌゞもありたす。フォヌムコントロヌルは Angular フォヌム (英語) の ngModel ã‚’䜿甚しお HTML ず Angular コントロヌラヌ間でデヌタを枡したす。この堎合、credentials ã‚ªãƒ–ゞェクトを䜿甚しおナヌザヌ名ずパスワヌドを保持したす。

認蚌プロセス

远加したログむンフォヌムをサポヌトするには、さらに機胜を远加する必芁がありたす。クラむアント偎ではこれらは LoginComponent に実装され、サヌバヌでは Spring Security 構成になりたす。

ログむンフォヌムの送信

フォヌムを送信するには、すでに ng-submit を介しおフォヌムで参照した login() é–¢æ•°ãšã€ng-model を介しお参照した credentials ã‚ªãƒ–ゞェクトを定矩する必芁がありたす。「ログむン」コンポヌネントを具䜓化したす。

login.component.ts
import { Component, OnInit } from '@angular/core';
import { AppService } from './app.service';
import { HttpClient } from '@angular/common/http';
import { Router } from '@angular/router';

@Component({
  templateUrl: './login.component.html'
})
export class LoginComponent {

  credentials = {username: '', password: ''};

  constructor(private app: AppService, private http: HttpClient, private router: Router) {
  }

  login() {
    this.app.authenticate(this.credentials, () => {
        this.router.navigateByUrl('/');
    });
    return false;
  }

}

credentials ã‚ªãƒ–ゞェクトの初期化に加えお、フォヌムで必芁な login() ã‚’定矩したす。

authenticate() ã¯ã€ç›žå¯Ÿãƒªã‚œãƒŒã‚¹ïŒˆã‚¢ãƒ—リケヌションのデプロむルヌトに察しお"/user" に察しお GET を実行したす。login() é–¢æ•°ã‹ã‚‰å‘Œã³å‡ºã•れるず、Base64 で゚ンコヌドされたクレデンシャルがヘッダヌに远加されるため、サヌバヌ䞊で認蚌が行われ、代わりに Cookie が受け入れられたす。login() é–¢æ•°ã¯ã€èªèšŒã®çµæžœã‚’取埗するず、それに応じおロヌカル $scope.error ãƒ•ラグも蚭定したす。これは、ログむンフォヌムの䞊の゚ラヌメッセヌゞの衚瀺を制埡するために䜿甚されたす。

珟圚認蚌されおいるナヌザヌ

authenticate() é–¢æ•°ã‚’凊理するには、新しい゚ンドポむントをバック゚ンドに远加する必芁がありたす。

UiApplication.java
@SpringBootApplication
@RestController
public class UiApplication {

  @RequestMapping("/user")
  public Principal user(Principal user) {
    return user;
  }

  ...

}

これは、Spring Security アプリケヌションで圹立぀トリックです。"/user" リ゜ヌスが到達可胜である堎合、珟圚認蚌されおいるナヌザヌAuthentication [GitHub] (英語) を返したす。そうでない堎合、Spring Security はリク゚ストをむンタヌセプトし、AuthenticationEntryPoint [GitHub] (英語) を介しお 401 レスポンスを送信したす。

サヌバヌでのログむンリク゚ストの凊理

Spring Security を䜿甚するず、ログむンリク゚ストを簡単に凊理できたす。メむンアプリケヌションクラス [GitHub] (英語) にいく぀かの構成を远加するだけですたずえば、内郚クラスずしお。

UiApplication.java
@SpringBootApplication
@RestController
public class UiApplication {

  ...

  @Configuration
  @Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
  protected static class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
      http
        .httpBasic()
      .and()
        .authorizeRequests()
          .antMatchers("/index.html", "/", "/home", "/login").permitAll()
          .anyRequest().authenticated();
    }
  }

}

これは、Spring Security をカスタマむズした暙準 Spring Boot アプリケヌションであり、静的HTMLリ゜ヌスぞの匿名アクセスのみを蚱可したす。HTML リ゜ヌスは、明確になる理由のため、Spring Security によっお無芖されるだけでなく、匿名ナヌザヌが利甚できる必芁がありたす。

芚えおおく必芁がある最埌のこずは、Angular が提䟛する JavaScript コンポヌネントをアプリケヌションで匿名で利甚できるようにするこずです。䞊蚘の HttpSecurity æ§‹æˆã§ãã‚Œã‚’行うこずができたすが、静的コンテンツであるため、単玔に無芖する方が良いです:

application.yml
security:
  ignored:
  - "*.bundle.*"

デフォルトの HTTP リク゚ストヘッダヌの远加

この時点でアプリを実行するず、ブラりザヌがナヌザヌずパスワヌドの基本認蚌ダむアログをポップアップするこずがわかりたす。これは、"WWW-Authenticate" ヘッダヌを持぀ /user ãŠã‚ˆã³ /resource ãžã® XHR リク゚ストからの 401 応答を確認するためです。このポップアップを抑制する方法は、Spring Security から来るヘッダヌを抑制するこずです。たた、応答ヘッダヌを抑制する方法は、特別な埓来のリク゚ストヘッダヌ "X-Requested-With = XMLHttpRequest" を送信するこずです。以前は Angular のデフォルトでしたが、1.3.0 では削陀されたした [GitHub] (英語) 。Angular XHR リク゚ストでデフォルトのヘッダヌを蚭定する方法は次のずおりです。

最初に、Angular HTTP モゞュヌルによっお提䟛されるデフォルト RequestOptions ã‚’継承したす。

app.module.ts
@Injectable()
export class XhrInterceptor implements HttpInterceptor {

  intercept(req: HttpRequest<any>, next: HttpHandler) {
    const xhr = req.clone({
      headers: req.headers.set('X-Requested-With', 'XMLHttpRequest')
    });
    return next.handle(xhr);
  }
}

ここの構文は定型です。Class ã® implements ãƒ—ロパティはその基本クラスであり、コンストラクタヌに加えお、本圓に必芁なこずは、Angular によっお垞に呌び出され、ヘッダヌを远加するために䜿甚できる intercept() é–¢æ•°ã‚’オヌバヌラむドするこずだけです。

この新しい RequestOptions ãƒ•ァクトリをむンストヌルするには、AppModule の providers ã§å®£èš€ã™ã‚‹å¿…芁がありたす。

app.module.ts
@NgModule({
  ...
  providers: [AppService, { provide: HTTP_INTERCEPTORS, useClass: XhrInterceptor, multi: true }],
  ...
})
export class AppModule { }

ログアりト

アプリケヌションはほが関数に終了しおいたす。最埌に行う必芁があるのは、ホヌムペヌゞでスケッチしたログアりト機胜を実装するこずです。ナヌザヌが認蚌されおいる堎合は、「ログアりト」リンクを衚瀺し、AppComponent の logout() é–¢æ•°ã«ãƒ•ックしたす。"/logout" に HTTP POST を送信するこずを忘れないでください。これは、サヌバヌに実装する必芁がありたす。これは、Spring Security によっおすでに远加されおいるため簡単です぀たり、この単玔なナヌスケヌスでは䜕もする必芁はありたせん。ログアりトの動䜜をより现かく制埡するには、WebSecurityAdapter ã§ HttpSecurity ã‚³ãƒŒãƒ«ãƒãƒƒã‚¯ã‚’䜿甚しお、たずえば、ログアりト埌にビゞネスロゞックを実行できたす。

CSRF の保護

アプリケヌションはほずんど䜿甚する準備ができおおり、実際に実行するず、ログアりトリンクを陀いお、これたでに䜜成したすべおが実際に機胜するこずがわかりたす。それを䜿甚しおみお、ブラりザヌでレスポンスを参照しおください。その理由がわかりたす。

POST /logout HTTP/1.1
...
Content-Type: application/x-www-form-urlencoded

username=user&password=password

HTTP/1.1 403 Forbidden
Set-Cookie: JSESSIONID=3941352C51ABB941781E1DF312DA474E; Path=/; HttpOnly
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
...

{"timestamp":1420467113764,"status":403,"error":"Forbidden","message":"Expected CSRF token not found. Has your session expired?","path":"/login"}

それは、Spring Security に組み蟌たれおいる CSRF 保護が、足元で自分自身を撃぀こずを防ぐために開始されたこずを意味するため、良いこずです。必芁なのは、"X-CSRF" ずいうヘッダヌで送信されるトヌクンだけです。CSRF トヌクンの倀は、ホヌムペヌゞをロヌドした最初のリク゚ストからの HttpRequest å±žæ€§ã§ã‚µãƒŒãƒãƒŒåŽã§åˆ©ç”šå¯èƒœã§ã—た。クラむアントに枡すには、サヌバヌ䞊の動的な HTML ペヌゞを䜿甚しおレンダリングするか、カスタム゚ンドポむントを介しお公開するか、Cookie ずしお送信したす。Angular には Cookie に基づいた CSRF "XSRF" ず呌ばれるのサポヌトが組み蟌たれ (英語) おいるため、最埌の遞択が最適です。

そのため、サヌバヌには、Cookie を送信するカスタムフィルタヌが必芁です。Angular は Cookie 名を "XSRF-TOKEN" にするこずを望んでおり、Spring Security はデフォルトでそれをリク゚スト属性ずしお提䟛するため、リク゚スト属性から Cookie に倀を転送するだけです。幞い、Spring Security4.1.0 以降は、これを正確に行う特別な CsrfTokenRepository ã‚’提䟛したす。

UiApplication.java
@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class SecurityConfiguration extends WebSecurityConfigurerAdapter {
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http
      ...
      .and().csrf()
        .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
  }
}

これらの倉曎が行われるず、クラむアント偎で䜕もする必芁がなくなり、ログむンフォヌムが機胜するようになりたす。

仕組みは

䞀郚の開発者ツヌルを䜿甚するず、ブラりザヌずバック゚ンド間の盞互䜜甚をブラりザヌで確認できたす通垞、F12 はこれを開き、デフォルトで Chrome で動䜜し、Firefox でプラグむンが必芁になる堎合がありたす。抂芁は次のずおりです。

動詞 パス ステヌタス レスポンス

GET

/

200

index.html

GET

/*.js

200

角床からのアセット

GET

/user

401

䞍蚱可 (無芖されたした)

GET

/home

200

ホヌムペヌゞ

GET

/user

401

䞍蚱可 (無芖されたした)

GET

/resource

401

䞍蚱可 (無芖されたした)

GET

/user

200

資栌情報を送信しお JSON を取埗する

GET

/resource

200

JSON グリヌティング

䞊蚘で「無芖」ずマヌクされおいるレスポンスは、XHR 呌び出しで Angular によっお受信された HTML レスポンスであり、そのデヌタを凊理しおいないため、HTML はフロアにドロップされたす。"/user" リ゜ヌスの堎合、認蚌されたナヌザヌを探したすが、最初の呌び出しには存圚しないため、そのレスポンスはドロップされたす。

リク゚ストをよく芋るず、すべおのリク゚ストに Cookie が含たれおいるこずがわかりたす。クリヌンなブラりザヌたずえば Chrome のシヌクレットモヌドで起動した堎合、最初のリク゚ストではサヌバヌに送信される Cookie はありたせんが、サヌバヌは "JSESSIONID"通垞の HttpSessionおよび "X- XSRF-TOKEN」䞊蚘で蚭定した CRSF Cookie。埌続のリク゚ストにはすべおこれらの Cookie があり、それらは重芁です。アプリケヌションはそれらの Cookie なしでは機胜せず、いく぀かの本圓に基本的なセキュリティ機胜認蚌および CSRF 保護を提䟛しおいたす。Cookie の倀は、ナヌザヌがPOST 埌に認蚌するず倉曎されたす。これは、別の重芁なセキュリティ機胜ですセッション固定攻撃 [Wikipedia] を防止したす。

アプリケヌションからロヌドされたペヌゞにない堎合でもブラりザヌが自動的に送信するため、サヌバヌに送り返される Cookie に䟝存する CSRF 保護には䞍十分ですクロスサむトスクリプティング攻撃、別名 XSS [Wikipedia] 。ヘッダヌは自動的に送信されないため、オリゞンは制埡されたす。このアプリケヌションでは、CSRF トヌクンが Cookie ずしおクラむアントに送信されるため、ブラりザヌによっお自動的に返送されたすが、保護を提䟛するのはヘッダヌです。

ヘルプ、私のアプリケヌションはどのように拡匵されたすか

「しかし... 1 ペヌゞのアプリケヌションでセッションステヌトを䜿甚するのは非垞に悪いこずではありたせんか ? 」この質問に察する答えは、「ほずんど」䜿甚したす。なぜなら、認蚌ず CSRF 保護のためにセッションを䜿甚するこずは、間違いなく良いこずです。この状態はどこかに保存する必芁があり、セッションから取り出す堎合は、別の堎所に眮き、サヌバヌずクラむアントの䞡方で手動で管理する必芁がありたす。これは単にコヌドが増えただけであり、メンテナンスも増えただけであり、党䜓ずしお完党に車茪を再発明したこずになりたす。

「しかし、しかし 
 アプリケヌションを今どのように氎平にスケヌリングするのですか」これは䞊蚘で尋ねおいた「本圓」の質問ですが、「セッション状態が悪い、ステヌトレスでなければなりたせん」に短瞮される傟向がありたす。パニックにならないでください。ここで重芁なのは、セキュリティがステヌトフルであるこずです。安党でステヌトレスなアプリケヌションを持぀こずはできたせん。状態をどこに保存したすか これですべおです。Rob Winch (英語) は Spring Exchange 2014 (英語) で非垞に有益でむンサむトに富んだ講挔を行い、状態の必芁性およびその普遍性: TCP ず SSL はステヌトフルであるため、システムはそれを知っおいるかどうかに関係なくステヌトフルです。このトピックをさらに詳しく調べたす。

良いニュヌスは、遞択肢があるこずです。最も簡単な遞択は、セッションデヌタをメモリに保存し、ロヌドバランサヌのスティッキヌセッションに䟝存しお、同じセッションからのリク゚ストを同じ JVM にルヌティングするこずですすべおサポヌトしおいたす。これで十分に理解でき、非垞に倚くのナヌスケヌスで機胜したす。もう 1 ぀の遞択肢は、アプリケヌションのむンスタンス間でセッションデヌタを共有するこずです。厳栌でセキュリティデヌタのみを保存しおいる限り、デヌタは小さく、倉曎はめったに行われたせんナヌザヌがログむンたたはログアりトしたずき、たたはセッションがタむムアりトしたずきのみ。むンフラストラクチャに倧きな問題はありたせん。Spring Session [GitHub] (英語) を䜿甚するのも簡単です。このシリヌズの次のセクションで Spring Session を䜿甚するため、ここで蚭定する方法に぀いお詳しく説明する必芁はありたせんが、文字通り数行のコヌドず Redis サヌバヌであり、非垞に高速です。

共有セッション状態をセットアップするもう 1 ぀の簡単な方法は、アプリケヌションを WAR ファむルずしお Cloud Foundry Pivotal Web サヌビス (英語) にデプロむし、それを Redis サヌビスにバむンドするこずです。

しかし、カスタムトヌクンの実装ステヌトレス、倖芳はどうですか

それが最埌のセクションぞのレスポンスであった堎合、それをもう䞀床参照しおください。トヌクンをどこかに保存した堎合はおそらくステヌトレスではありたせんが、保存しおいない堎合たずえば、JWT ゚ンコヌドトヌクンを䜿甚しおいる堎合、CSRF 保護をどのように提䟛したすか それは重芁です。経隓則Rob Winch に起因は次のずおりです。アプリケヌションたたは API がブラりザヌからアクセスされる堎合、CSRF 保護が必芁です。セッションなしでそれができないずいうこずではなく、自分ですべおのコヌドを曞かなければならないずいうこずです。そしお、すでに実装されおおり、HttpSession ã®äžŠã§å®Œå…šã«ã†ãŸãæ©Ÿèƒœã™ã‚‹ãŸã‚ã€äœ•がポむントになるでしょう䜿甚し、最初から仕様に焌き付けおいるコンテナヌの  CSRF を必芁ずせず、完党に「ステヌトレス」非セッションベヌスのトヌクン実装を持っおいるず刀断したずしおも、それを消費しお䜿甚するためにクラむアントに远加のコヌドを蚘述する必芁がありたした。ブラりザヌずサヌバヌの独自の組み蟌み機胜: ブラりザヌは垞に Cookie を送信し、サヌバヌは垞にセッションを保持したすスむッチをオフにしない限り。そのコヌドはビゞネスロゞックではなく、収益を䞊げるものではなく、単なるオヌバヌヘッドであるため、さらに悪いこずにお金がかかりたす。

結論

珟圚のアプリケヌションは、ナヌザヌがラむブ環境の「実際の」アプリケヌションに期埅するものに近いものであり、おそらく、そのアヌキテクチャを備えたより機胜豊富なアプリケヌション静的な単䞀サヌバヌコンテンツず JSON リ゜ヌス。HttpSession ã‚’䜿甚しおセキュリティデヌタを保存し、クラむアントが送信する Cookie を考慮しお䜿甚するこずを頌りにしおいたすが、独自のビゞネスドメむンに集䞭できるため、それに満足しおいたす。次のセクションでは、アヌキテクチャを個別の認蚌および UI サヌバヌに加えお、JSON 甚のスタンドアロンリ゜ヌスサヌバヌに拡匵したす。これは明らかに、耇数のリ゜ヌスサヌバヌに簡単に䞀般化できたす。たた、Spring Session をスタックに導入し、それを䜿甚しお認蚌デヌタを共有する方法を瀺したす。

リ゜ヌスサヌバヌ

このセクションでは、「シングルペヌゞアプリケヌション」で Spring Security を Angular (英語) ず䜿甚する方法に぀いお匕き続き説明したす。ここでは、アプリケヌションの動的コンテンツずしお䜿甚しおいる「あいさ぀」リ゜ヌスを、たず保護されおいないリ゜ヌスずしお別のサヌバヌに分割し、次に Opaque トヌクンで保護したす。これは䞀連のセクションの 3 番目であり、アプリケヌションの基本的な構成芁玠に远い぀くか、最初のセクションを読んでれロからビルドするか、Github の゜ヌスコヌドに盎接進むこずができたす。2 ぀の郚分: リ゜ヌスが保護されおいない [GitHub] (英語) 郚分ず、トヌクンによっお保護されおいる郚分 [GitHub] (英語) 。

このセクションでサンプルアプリケヌションを䜿甚しおいる堎合は、ブラりザヌのキャッシュの Cookie ず HTTP 基本認蚌情報を必ずクリアしおください。Chrome では、単䞀サヌバヌでこれを行う最良の方法は、新しいシヌクレットりィンドりを開くこずです。

別のリ゜ヌスサヌバヌ

クラむアント偎の倉曎

クラむアント偎では、リ゜ヌスを別のバック゚ンドに移動するために行うこずはあたりありたせん。最埌のセクション [GitHub] (英語) の「ホヌム」コンポヌネントは次のずおりです。

home.component.ts
@Component({
  templateUrl: './home.component.html'
})
export class HomeComponent {

  title = 'Demo';
  greeting = {};

  constructor(private app: AppService, private http: HttpClient) {
    http.get('resource').subscribe(data => this.greeting = data);
  }

  authenticated() { return this.app.authenticated; }

}

これに必芁なのは、URL を倉曎するこずだけです。䟋: localhost で新しいリ゜ヌスを実行する堎合、次のようになりたす。

home.component.ts
        http.get('http://localhost:9000').subscribe(data => this.greeting = data);

サヌバヌ偎の倉曎

UI サヌバヌ [GitHub] (英語) の倉曎は簡単です。グリヌティングリ゜ヌスの @RequestMapping ã‚’削陀する必芁がありたす"/resource" でした。次に、新しいリ゜ヌスサヌバヌを䜜成する必芁がありたす。これは、Spring Boot Initializr を䜿甚しお最初のセクションで行ったように実行できたす。たずえば、UN*X ラむクなシステムで curl を䜿甚する:

$ mkdir resource && cd resource
$ curl https://start.spring.io/starter.tgz -d dependencies=web -d name=resource | tar -xzvf -

その埌、そのプロゞェクトデフォルトでは通垞の Maven Java プロゞェクトをお気に入りの IDE にむンポヌトするか、コマンドラむンでファむルず "mvn" を操䜜するだけです。

メむンアプリケヌションクラス [GitHub] (英語) に @RequestMapping ã‚’远加し、叀い UI [GitHub] (英語) から実装をコピヌするだけです:

ResourceApplication.java
@SpringBootApplication
@RestController
class ResourceApplication {

  @RequestMapping("/")
  public Message home() {
    return new Message("Hello World");
  }

  public static void main(String[] args) {
    SpringApplication.run(ResourceApplication.class, args);
  }

}

class Message {
  private String id = UUID.randomUUID().toString();
  private String content;
  public Message(String content) {
    this.content = content;
  }
  // ... getters and setters and default constructor
}

それが完了するず、アプリケヌションはブラりザヌにロヌド可胜になりたす。コマンドラむンでこれを行うこずができたす

$ mvn spring-boot:run -Dserver.port=9000

http://localhost:9000 のブラりザヌに移動するず、挚拶付きの JSON が衚瀺されたす。application.properties ïŒˆ "src/main/resources" 内でポヌトの倉曎をベむクできたす。

application.properties
server.port: 9000

ブラりザヌの UIポヌト 8080からそのリ゜ヌスをロヌドしようずするず、ブラりザヌが XHR リク゚ストを蚱可しないため、機胜しないこずがわかりたす。

CORS ネゎシ゚ヌション

ブラりザヌは、クロスオリゞンリ゜ヌス共有 [Mozilla] プロトコルに埓っおリ゜ヌスサヌバヌぞのアクセスが蚱可されおいるかどうかを確認するために、リ゜ヌスサヌバヌずネゎシ゚ヌトしようずしたす。Angular の責任ではないため、Cookie 契玄ず同様に、ブラりザヌ内のすべおの JavaScript でこのように機胜したす。2 ぀のサヌバヌは共通の発信元を宣蚀しおいないため、ブラりザヌはリク゚ストの送信を拒吊し、UI は壊れおいたす。

これを修正するには、「プリフラむト」OPTIONS リク゚ストず、呌び出し元の蚱可された動䜜をリストするいく぀かのヘッダヌを含む CORS プロトコルをサポヌトする必芁がありたす。Spring 4.2 には、きめの现かい CORS サポヌト (英語) がいく぀かありたす。そのため、コントロヌラヌマッピングにアノテヌションを远加するだけです。䟋:

ResourceApplication.java
@RequestMapping("/")
@CrossOrigin(origins="*", maxAge=3600)
public Message home() {
  return new Message("Hello World");
}
origins=* ã‚’簡単に䜿甚するのは簡単で汚れおおり、動䜜したすが、安党ではなく、決しお掚奚されたせん。

リ゜ヌスサヌバヌのセキュリティ保護

すごい 新しいアヌキテクチャヌで動䜜するアプリケヌションがありたす。唯䞀の問題は、リ゜ヌスサヌバヌにセキュリティがないこずです。

Spring Security の远加

UI サヌバヌのように、フィルタヌレむダヌずしおリ゜ヌスサヌバヌにセキュリティを远加する方法も確認できたす。最初のステップは本圓に簡単です: Spring Security を Maven POM のクラスパスに远加するだけです:

pom.xml
<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
  </dependency>
  ...
</dependencies>

リ゜ヌスサヌバヌを再起動したす。安党です:

$ curl -v localhost:9000
< HTTP/1.1 302 Found
< Location: http://localhost:9000/login
...

curl は Angular クラむアントず同じヘッダヌを送信しおいないため、ホワむトラベルログむンペヌゞぞのリダむレクトを取埗しおいたす。より類䌌したヘッダヌを送信するようにコマンドを倉曎したす。

$ curl -v -H "Accept: application/json" \
    -H "X-Requested-With: XMLHttpRequest" localhost:9000
< HTTP/1.1 401 Unauthorized
...

クラむアントにすべおのリク゚ストで資栌情報を送信するように教えるだけです。

トヌクン認蚌

むンタヌネットおよび人々の Spring バック゚ンドプロゞェクトには、カスタムトヌクンベヌスの認蚌゜リュヌションが散らばっおいたす。Spring Security は、あなた自身で始めるための最䜎限の Filter å®Ÿè£…を提䟛したすたずえば、AbstractPreAuthenticatedProcessingFilter [GitHub] (英語) および TokenService [GitHub] (英語) を参照。ただし、Spring Security には正芏の実装はありたせん。その理由の 1 ぀は、おそらくさらに簡単な方法があるからです。

このシリヌズのパヌト II から、Spring Security はデフォルトで HttpSession ã‚’䜿甚しお認蚌デヌタを保管するこずを思い出しおください。ただし、セッションず盎接察話するこずはありたせん。その間にはストレヌゞバック゚ンドを倉曎するために䜿甚できる抜象化レむダヌSecurityContextRepository [GitHub] (英語) がありたす。リ゜ヌスサヌバヌで、UI によっお認蚌が怜蚌されたストアをそのリポゞトリにポむントできる堎合、2 ぀のサヌバヌ間で認蚌を共有する方法がありたす。UI サヌバヌにはすでにそのようなストアHttpSessionがありたす。そのため、そのストアを配垃しおリ゜ヌスサヌバヌに開くこずができる堎合、ほずんどの゜リュヌションがありたす。

Spring Session

゜リュヌションのその郚分は Spring Session [GitHub] (英語) で非垞に簡単です。必芁なのは、共有デヌタストアRedis ず JDBC はすぐにサポヌトされおいたす、および Filter をセットアップするためのサヌバヌの構成行です。

UI アプリケヌションでは、POM [GitHub] (英語) にいく぀かの䟝存関係を远加する必芁がありたす。

pom.xml
<dependency>
  <groupId>org.springframework.session</groupId>
  <artifactId>spring-session</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

Spring Boot ず Spring Session は連携しお Redis に接続し、セッションデヌタを䞀元的に保存したす。

その 1 行のコヌドず localhost で実行されおいる Redis サヌバヌを䜿甚しお、UI アプリケヌションを実行し、有効なナヌザヌ資栌情報でログむンするず、セッションデヌタ認蚌が redis に保存されたす。

ロヌカルで実行しおいる redis サヌバヌがない堎合は、Docker (英語) で簡単に起動できたすWindows たたは MacOS では VM が必芁です。Github の゜ヌスコヌド [GitHub] (英語) には docker-compose.yml (英語) ファむルがあり、docker-compose up を䜿甚しおコマンドラむンで簡単に実行できたす。VM でこれを行うず、Redis サヌバヌは localhost ずは異なるホストで実行されるため、localhost にトンネリングするか、application.properties の正しい spring.redis.host ã‚’指すようにアプリを構成する必芁がありたす。

UI からカスタムトヌクンを送信する

䞍足しおいる唯䞀の郚分は、ストア内のデヌタぞのキヌの転送メカニズムです。キヌは HttpSession ID であるため、UI クラむアントでそのキヌを取埗できる堎合、リ゜ヌスサヌバヌにカスタムヘッダヌずしお送信できたす。そのため、「ホヌム」コントロヌラヌは、グリヌティングリ゜ヌスの HTTP リク゚ストの䞀郚ずしおヘッダヌを送信するように倉曎する必芁がありたす。䟋

home.component.ts
  constructor(private app: AppService, private http: HttpClient) {
    http.get('token').subscribe(data => {
      const token = data['token'];
      http.get('http://localhost:9000', {headers : new HttpHeaders().set('X-Auth-Token', token)})
        .subscribe(response => this.greeting = response);
    }, () => {});
  }

より゚レガントな゜リュヌションは、必芁に応じおトヌクンを取埗し、RequestOptionsService ã‚’䜿甚しお、リ゜ヌスサヌバヌぞのすべおのリク゚ストにヘッダヌを远加するこずです

"http://localhost:9000" に盎接移動する代わりに、"/token" の UI サヌバヌ䞊の新しいカスタム゚ンドポむントぞの呌び出しの成功コヌルバックでその呌び出しをラップしたした。その実装は簡単です。

UiApplication.java
@SpringBootApplication
@RestController
public class UiApplication {

  public static void main(String[] args) {
    SpringApplication.run(UiApplication.class, args);
  }

  ...

  @RequestMapping("/token")
  public Map<String,String> token(HttpSession session) {
    return Collections.singletonMap("token", session.getId());
  }

}

これで、UI アプリケヌションの準備が敎い、バック゚ンドぞのすべおの呌び出しに察しお "X-Auth-Token" ずいうヘッダヌにセッション ID が含たれたす。

リ゜ヌスサヌバヌでの認蚌

リ゜ヌスサヌバヌがカスタムヘッダヌを受け入れるこずができるように、リ゜ヌスサヌバヌに小さな倉曎が 1 ぀ありたす。CORS 構成では、そのヘッダヌをリモヌトクラむアントからの蚱可されたヘッダヌずしお指定する必芁がありたす。

ResourceApplication.java
@RequestMapping("/")
@CrossOrigin(origins = "*", maxAge = 3600,
    allowedHeaders={"x-auth-token", "x-requested-with", "x-xsrf-token"})
public Message home() {
  return new Message("Hello World");
}

ブラりザヌからのプリフラむトチェックは Spring MVC によっお凊理されるようになりたしたが、Spring Security に通過を蚱可するこずを䌝える必芁がありたす。

ResourceApplication.java
public class ResourceApplication extends WebSecurityConfigurerAdapter {

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.cors().and().authorizeRequests()
      .anyRequest().authenticated();
  }

  ...
すべおのリ゜ヌスに permitAll() ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹å¿…芁はありたせん。たた、リク゚ストがプリフラむトであるこずを認識しおいないため、機密デヌタを誀っお送信するハンドラヌが存圚する堎合がありたす。cors() æ§‹æˆãƒŠãƒŒãƒ†ã‚£ãƒªãƒ†ã‚£ã¯ã€ãƒ•ィルタヌ局ですべおのプリフラむトリク゚ストを凊理するこずにより、これを軜枛したす。

あずは、リ゜ヌスサヌバヌでカスタムトヌクンを取埗し、それを䜿甚しおナヌザヌを認蚌するだけです。必芁なのは、Spring Security にセッションリポゞトリの堎所ず、受信リク゚ストでトヌクンセッション IDを探す堎所を䌝えるだけなので、これは非垞に簡単です。最初に Spring Session ず Redis の䟝存関係を远加する必芁があり、次に Filter をセットアップできたす。

ResourceApplication.java
@SpringBootApplication
@RestController
class ResourceApplication {

  ...

  @Bean
  HeaderHttpSessionStrategy sessionStrategy() {
    return new HeaderHttpSessionStrategy();
  }

}

䜜成されたこの Filter ã¯ã€UI サヌバヌにあるもののミラヌむメヌゞであるため、Redis をセッションストアずしお確立したす。唯䞀の違いは、デフォルト "JSESSIONID" ずいう名前の Cookieではなく、ヘッダヌデフォルトでは "X-Auth-Token" を調べるカスタム HttpSessionStrategy ã‚’䜿甚するこずです。たた、ブラりザヌが認蚌されおいないクラむアントでダむアログをポップアップしないようにする必芁がありたす。アプリは安党ですが、デフォルトで WWW-Authenticate: Basic ã§ 401 を送信するため、ブラりザヌはナヌザヌ名ずパスワヌドのダむアログで応答したす。これを実珟する方法は耇数ありたすが、すでに Angular が "X-Requested-With" ヘッダヌを送信するようにしおいるため、Spring Security はデフォルトでそれを凊理したす。

リ゜ヌスサヌバヌには、新しい認蚌スキヌムで動䜜するように、最埌に 1 ぀の倉曎がありたす。Spring Boot のデフォルトセキュリティはステヌトレスであり、セッションに認蚌を保存するために、application.yml ïŒˆãŸãŸã¯ application.propertiesで明瀺的にする必芁がありたす。

application.yml
security:
  sessions: NEVER

これは、Spring Security に察しお「セッションを䜜成するこずはありたせんが、セッションが存圚する堎合はセッションを䜿甚する」ず蚀いたすUI での認蚌のためにすでに存圚したす。

リ゜ヌスサヌバヌを再起動し、新しいブラりザヌりィンドりで UI を開きたす。

なぜすべおが Cookie で機胜しないのですか

カスタムヘッダヌを䜿甚し、クラむアントにヘッダヌを入力するコヌドを蚘述する必芁がありたした。これはそれほど耇雑ではありたせんが、可胜な限り Cookie ずセッションを䜿甚するずいうパヌト II のアドバむスず矛盟するようです。そうしないず、䞍必芁な耇雑さが远加されるずいう議論がありたしたが、確かに、珟圚の実装はこれたで芋おきた䞭で最も耇雑です: ゜リュヌションの技術的な郚分は、ビゞネスロゞック明らかに小さいをはるかに䞊回っおいたす。これは間違いなく公正な批刀ですそしお、このシリヌズの次のセクションで説明する予定ですが、すべおの目的で Cookie ずセッションを䜿甚するほど単玔ではない理由を簡単に芋おみたしょう。

少なくずもただセッションを䜿甚しおいたすが、これは理にかなっおいたす。なぜなら Spring Security ず Servlet コンテナヌは私たちの努力なしにそれを行う方法を知っおいるからです。しかし、Cookie を䜿甚しお認蚌トヌクンを転送し続けるこずができなかったでしょうか それは玠晎らしかったでしょうが、それが機胜しない理由があり、ブラりザヌが私たちを認可しないずいうこずです。JavaScript クラむアントからブラりザヌの Cookie ストアをいじっおみるこずができたすが、いく぀かの制限があり、十分な理由がありたす。特に、サヌバヌから "HttpOnly" ずしお送信された Cookie にはアクセスできたせんセッション Cookie の堎合、デフォルトで衚瀺されたす。たた、送信リク゚ストに Cookie を蚭定できないため、"SESSION" CookieSpring Session のデフォルト Cookie 名を蚭定できなかったため、カスタムの "X-Session" ヘッダヌを䜿甚する必芁がありたした。これらの制限は䞡方ずもナヌザヌ自身の保護のためのものであるため、悪意のあるスクリプトは適切な認可なしにリ゜ヌスにアクセスできたせん。

TL; DR UI ずリ゜ヌスサヌバヌは共通の起源を持たないため、Cookie を共有できたせんSpring Session を䜿甚しお匷制的にセッションを共有できたす。

結論

このシリヌズのパヌト II のアプリケヌションの機胜を耇補したした。リモヌトバック゚ンドから取埗した挚拶を含むホヌムペヌゞで、ナビゲヌションバヌにログむンずログアりトのリンクがありたす。違いは、グリヌティングが UI サヌバヌに組み蟌たれおいるためはなく、スタンドアロンのリ゜ヌスサヌバヌから送信されるこずです。これにより、実装が倧幅に耇雑になりたしたが、幞いなこずに、ほずんど構成ベヌスの (実際には 100% 宣蚀型) ゜リュヌションを䜿甚できたす。新しいコヌドをすべおラむブラリ (Spring 構成ず Angular カスタムディレクティブ) に抜出するこずで、゜リュヌション 100% を宣蚀型にするこずもできたす。この興味深いタスクは、次の 2 回の蚘事以降に延期したす。次のセクションでは、珟圚の実装の耇雑さをすべお軜枛するための、別の本圓に優れた方法ずしお、API ゲヌトりェむパタヌン (クラむアントはすべおのリク゚ストを 1 ぀の堎所に送信し、その堎所で認蚌凊理) を芋おいきたす。

ここでは、Spring Session を䜿甚しお、論理的に同じアプリケヌションではない 2 ぀のサヌバヌ間でセッションを共有したした。これは巧劙なトリックであり、「通垞の」JEE 分散セッションでは䞍可胜です。

API Gateway

このセクションでは、「シングルペヌゞアプリケヌション」で Spring Security を Angular (英語) ず䜿甚する方法に぀いお匕き続き説明したす。ここでは、Spring Cloud を䜿甚しお認蚌ずバック゚ンドリ゜ヌスぞのアクセスを制埡する API ゲヌトりェむを構築する方法を瀺したす。これは䞀連のセクションの 4 番目であり、アプリケヌションの基本的な構成芁玠に远い぀くか、最初のセクションを読んでれロから構築するか、Github の゜ヌスコヌドに盎接 (英語) 進むこずができたす。前のセクションでは、Spring Session [GitHub] (英語) を䜿甚しおバック゚ンドリ゜ヌスを認蚌する単玔な分散アプリケヌションを構築したした。これでは、UI サヌバヌをバック゚ンドリ゜ヌスサヌバヌぞのリバヌスプロキシにし、最埌の実装カスタムトヌクン認蚌によっお導入された技術的な耇雑さの課題を修正し、ブラりザヌクラむアントからのアクセスを制埡するための倚くの新しいオプションを提䟛したす。

リマむンダヌ: このセクションでサンプルアプリケヌションを䜿甚しおいる堎合は、ブラりザヌのキャッシュの Cookie ず HTTP 基本認蚌情報を必ずクリアしおください。Chrome では、単䞀サヌバヌでこれを行う最良の方法は、新しいシヌクレットりィンドりを開くこずです。

API Gateway の䜜成

API Gateway は、フロント゚ンドクラむアントの単䞀の゚ントリポむントおよび制埡であり、ブラりザヌベヌスこのセクションの䟋のようなたたはモバむルの堎合がありたす。クラむアントは 1 ぀のサヌバヌの URL を知るだけでよく、バック゚ンドを倉曎せずに自由にリファクタリングできたす。これは倧きな利点です。集䞭化ず制埡に関しお、レヌト制限、認蚌、監査、ログ蚘録などの利点がありたす。Spring Cloud を䜿甚するず、単玔なリバヌスプロキシの実装は非垞に簡単です。

コヌドを順守しおいる堎合、最埌のセクションの最埌のアプリケヌション実装が少し耇雑であるこずがわかるため、それを繰り返すのに最適な堎所ではありたせん。ただし、より簡単に開始できる䞭間点があり、バック゚ンドリ゜ヌスはただ Spring Security で保護されおいたせんでした。この゜ヌスコヌドは Github (英語) の別個のプロゞェクトなので、そこから始めたす。UI サヌバヌずリ゜ヌスサヌバヌがあり、お互いに通信しおいたす。リ゜ヌスサヌバヌには Spring Security がただないため、最初にシステムを動䜜させおから、そのレむダヌを远加できたす。

1 行の宣蚀的リバヌスプロキシ

これを API Gateway に倉換するには、UI サヌバヌに 1 ぀の小さな調敎が必芁です。Spring 構成のどこかで、たずえばメむンのみアプリケヌションクラス [GitHub] (英語) に @EnableZuulProxy ã‚¢ãƒŽãƒ†ãƒŒã‚·ãƒ§ãƒ³ã‚’远加する必芁がありたす。

UiApplication.java
@SpringBootApplication
@RestController
@EnableZuulProxy
public class UiApplication {
  ...
}

たた、倖郚構成ファむルで、UI サヌバヌのロヌカルリ゜ヌスを倖郚構成 [GitHub] (英語) "application.yml" のリモヌトリ゜ヌスにマップする必芁がありたす。

application.yml
security:
  ...
zuul:
  routes:
    resource:
      path: /resource/**
      url: http://localhost:9000

これは、「このサヌバヌのパタヌン /resource/** のパスを、リモヌトサヌバヌの localhost:9000 にある同じパスにマップする」ずいう意味です。シンプルですが効果的です (YAML を含めお 6 行ですが、必ずしも必芁なわけではありたせん)。

この䜜業を行うために必芁なのは、クラスパス䞊の適切なものだけです。そのために、Maven POM にいく぀かの新しい行がありたす。

pom.xml
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-dependencies</artifactId>
      <version>Dalston.SR4</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

<dependencies>
  <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zuul</artifactId>
  </dependency>
  ...
</dependencies>

"spring-cloud-starter-zuul" の䜿甚に泚意しおください。これは Spring Boot のものず同じスタヌタヌ POM ですが、この Zuul プロキシに必芁な䟝存関係を管理したす。たた、掚移的な䟝存関係のすべおのバヌゞョンが正しいこずに䟝存できるようにするために、<dependencyManagement> ã‚’䜿甚しおいたす。

クラむアントでプロキシを䜿甚する

これらの倉曎が適切に適甚されおいおも、アプリケヌションは匕き続き機胜したすが、クラむアントを倉曎するたで、実際には新しいプロキシを䜿甚しおいたせん。幞いなこずにそれは簡単です。最埌のセクションで 「単䞀」から「バニラ」サンプルに行った倉曎を元に戻す必芁がありたす。

home.component.ts
constructor(private app: AppService, private http: HttpClient) {
  http.get('resource').subscribe(data => this.greeting = data);
}

サヌバヌを起動するず、すべおが機胜し、リク゚ストは UIAPI Gatewayを介しおリ゜ヌスサヌバヌにプロキシされたす。

さらなる簡玠化

さらに良い: リ゜ヌスサヌバヌに CORS フィルタヌはもう必芁ありたせん。すぐにそれを䞀緒に投げたしたが、技術的に手䜜業で焊点を合わせなければならないこずは特に危険でした特にセキュリティに関する堎合。幞いなこずにそれは冗長であるため、それを捚おお、倜眠りに戻るこずができたす

リ゜ヌスサヌバヌのセキュリティ保護

䞭間状態では、リ゜ヌスサヌバヌにセキュリティが蚭定されおいないこずを芚えおいるかもしれたせん。

䜙談: ネットワヌクアヌキテクチャがアプリケヌションアヌキテクチャを反映しおいる堎合、゜フトりェアセキュリティの欠劂は問題にならない堎合がありたすリ゜ヌスサヌバヌに UI サヌバヌ以倖の人が物理的にアクセスできないようにするこずができたす。その簡単なデモンストレヌションずしお、リ゜ヌスサヌバヌにロヌカルホストでのみアクセスできるようにしたす。これをリ゜ヌスサヌバヌの application.properties ã«è¿œåŠ ã™ã‚‹ã ã‘ã§ã™ã€‚

application.properties
server.address: 127.0.0.1

うわヌ、簡単でした デヌタセンタヌでのみ衚瀺されるネットワヌクアドレスを䜿甚しお、すべおのリ゜ヌスサヌバヌずすべおのナヌザヌデスクトップで機胜するセキュリティ゜リュヌションを実珟したす。

゜フトりェアレベルでセキュリティが必芁であるず刀断したず仮定したす倚くの理由により、かなりの可胜性がありたす。問題になるこずはありたせん。䟝存関係ずしお Spring Security をリ゜ヌスサヌバヌ POM [GitHub] (英語) に远加するだけですから:

pom.xml
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
</dependency>

セキュアなリ゜ヌスサヌバヌを取埗するにはこれで十分ですが、パヌト III にはなかったのず同じ理由で、ただ動䜜しおいるアプリケヌションは取埗できたせん。2 ぀のサヌバヌ間で共有認蚌状態はありたせん。

認蚌状態の共有

認蚌および CSRF状態を共有するために、前回ず同じメカニズム、぀たり Spring Session [GitHub] (英語) を䜿甚できたす。以前のように、䞡方のサヌバヌに䟝存関係を远加したす。

pom.xml
<dependency>
  <groupId>org.springframework.session</groupId>
  <artifactId>spring-session</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-redis</artifactId>
</dependency>

ただし、同じ Filter å®£èš€ã‚’䞡方に远加できるため、今回は構成がはるかに簡単になりたす。最初に UI サヌバヌ。すべおのヘッダヌを転送するこずを明瀺的に宣蚀したす぀たり、「機密」はありたせん。

application.yml
zuul:
  routes:
    resource:
      sensitive-headers:

その埌、リ゜ヌスサヌバヌに移動できたす。2 ぀の小さな倉曎が必芁です。1 ぀は、リ゜ヌスサヌバヌで HTTP Basic を明瀺的に無効にするこずですブラりザヌが認蚌ダむアログをポップアップするのを防ぐため。

ResourceApplication.java
@SpringBootApplication
@RestController
class ResourceApplication extends WebSecurityConfigurerAdapter {

  ...

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.httpBasic().disable();
    http.authorizeRequests().anyRequest().authenticated();
  }

}

䜙談: 認蚌ダむアログを防ぐ別の方法は、HTTP Basic を保持したたた、401 チャレンゞを "Basic" 以倖に倉曎するこずです。HttpSecurity æ§‹æˆã‚³ãƒŒãƒ«ãƒãƒƒã‚¯ã® AuthenticationEntryPoint ã® 1 行の実装でそれを行うこずができたす。

もう 1 ぀は、application.properties で非ステヌトレスセッション䜜成ポリシヌを明瀺的に芁求するこずです。

application.properties
security.sessions: NEVER

redis がバックグラりンドで実行されおいる限り起動する堎合は docker-compose.yml [GitHub] (英語) を䜿甚しおください、システムは動䜜したす。http://localhost:8080 で UI のホヌムペヌゞをロヌドしおログむンするず、バック゚ンドからのメッセヌゞがホヌムペヌゞに衚瀺されたす。

仕組みは

バックグラりンドで䜕が起こっおいたすか 最初に、UI サヌバヌおよび API Gatewayで HTTP リク゚ストを確認できたす。

動詞 パス ステヌタス レスポンス

GET

/

200

index.html

GET

/*.js

200

角床からのアセット

GET

/user

401

䞍蚱可 (無芖されたした)

GET

/resource

401

リ゜ヌスぞの認蚌されおいないアクセス

GET

/user

200

JSON 認蚌枈みナヌザヌ

GET

/resource

200

プロキシJSON グリヌティング

これは、Spring Session を䜿甚しおいるため、Cookie 名がわずかに異なる "JSESSIONID" ではなく "SESSION" ずいう点を陀いお、パヌト II の最埌のシヌケンスず同じです。ただし、アヌキテクチャは異なり、"/resource" ぞの最埌のリク゚ストは、リ゜ヌスサヌバヌにプロキシされおいるため、特別です。

UI サヌバヌの "/trace" ゚ンドポむントSpring Cloud の䟝存関係で远加した Spring Boot Actuator からを確認するこずで、リバヌスプロキシの動䜜を確認できたす。新しいブラりザヌで http://localhost:8080/trace に移動したすブラりザヌ甚の JSON プラグむンをただ入手しおいない堎合は、ブラりザヌを読みやすくしたす。HTTP Basicブラりザヌポップアップで認蚌する必芁がありたすが、ログむンフォヌムず同じ資栌情報が有効です。開始時たたは開始近くに、次のようなリク゚ストのペアが衚瀺されたす。

認蚌のクロスオヌバヌの可胜性がないように別のブラりザヌを䜿甚しおみおくださいたずえば、UI のテストに Chrome を䜿甚しない堎合は Firefox を䜿甚したす- アプリの動䜜を停止したせんが、含たれおいる堎合はトレヌスを読みにくくしたす。同じブラりザヌからの認蚌の混合。
/trace
{
  "timestamp": 1420558194546,
  "info": {
    "method": "GET",
    "path": "/",
    "query": ""
    "remote": true,
    "proxy": "resource",
    "headers": {
      "request": {
        "accept": "application/json, text/plain, */*",
        "x-xsrf-token": "542c7005-309c-4f50-8a1d-d6c74afe8260",
        "cookie": "SESSION=c18846b5-f805-4679-9820-cd13bd83be67; XSRF-TOKEN=542c7005-309c-4f50-8a1d-d6c74afe8260",
        "x-forwarded-prefix": "/resource",
        "x-forwarded-host": "localhost:8080"
      },
      "response": {
        "Content-Type": "application/json;charset=UTF-8",
        "status": "200"
      }
    },
  }
},
{
  "timestamp": 1420558200232,
  "info": {
    "method": "GET",
    "path": "/resource/",
    "headers": {
      "request": {
        "host": "localhost:8080",
        "accept": "application/json, text/plain, */*",
        "x-xsrf-token": "542c7005-309c-4f50-8a1d-d6c74afe8260",
        "cookie": "SESSION=c18846b5-f805-4679-9820-cd13bd83be67; XSRF-TOKEN=542c7005-309c-4f50-8a1d-d6c74afe8260"
      },
      "response": {
        "Content-Type": "application/json;charset=UTF-8",
        "status": "200"
      }
    }
  }
},

2 番目の゚ントリは "/resource" のクラむアントからゲヌトりェむぞのリク゚ストであり、Cookieブラりザヌによっお远加されたず CSRF ヘッダヌパヌト II に埓っお Angular によっお远加されたを確認できたす。最初の゚ントリには remote: true ãŒã‚り、これはリ゜ヌスサヌバヌぞの呌び出しをトレヌスしおいるこずを意味したす。これが URI パス "/" に送信されたこずがわかりたす。たた、重芁なこずにCookie ず CSRF ヘッダヌも送信されおいるこずがわかりたす。Spring Session がないず、これらのヘッダヌはリ゜ヌスサヌバヌにずっお意味がありたせんが、セットアップ方法では、これらのヘッダヌを䜿甚しお、認蚌ず CSRF トヌクンデヌタを䜿甚しおセッションを再構成できたす。リク゚ストは蚱可され、ビゞネスを行っおいたす

結論

このセクションではかなり倚くのこずを説明したしたが、2 台のサヌバヌに最小限のボむラヌプレヌトコヌドがあり、どちらも安党であり、ナヌザヌ゚クスペリ゚ンスが損なわれない、本圓に玠晎らしい堎所に到達したした。それだけが API Gateway パタヌンを䜿甚する理由になりたすが、実際には、䜿甚される可胜性のある衚面のほんの䞀郚に過ぎたせんNetflix は倚くのこ [GitHub] (英語) ずに䜿甚しおいたす。Spring Cloud を読んで、より倚くの機胜をゲヌトりェむに簡単に远加する方法を確認しおください。このシリヌズの次のセクションでは、認蚌の責任を別のサヌバヌに抜出するこずで、アプリケヌションアヌキテクチャを少し拡匵したすシングルサむンオンパタヌン。

OAuth2 を䜿甚したシングルサむンオン

このセクションでは、「シングルペヌゞアプリケヌション」で Spring Security を Angular (英語) ず䜿甚する方法に぀いお匕き続き説明したす。ここでは、Spring Security OAuth (英語) ず Spring Cloud を䜿甚しお API Gateway を継承し、シングルサむンオンず OAuth2 トヌクン認蚌をバック゚ンドリ゜ヌスに実行する方法を瀺したす。これは䞀連のセクションの 5 番目であり、アプリケヌションの基本的な構成芁玠に远い぀くか、最初のセクションを読んでれロからビルドするか、Github の゜ヌスコヌドに盎接 (英語) 進むこずができたす。最埌のセクションでは、Spring Session [GitHub] (英語) を䜿甚しおバック゚ンドリ゜ヌスを認蚌し、Spring Cloud を䜿甚しお UI サヌバヌに組み蟌み API ゲヌトりェむを実装する小さな分散アプリケヌションを構築したした。このセクションでは、認蚌サヌバヌぞの倚くのシングルサむンオンアプリケヌションの最初の UI サヌバヌを䜜成するために、認蚌の責任を別のサヌバヌに抜出したす。これは、䌁業および゜ヌシャルスタヌトアップの䞡方で、最近の倚くのアプリケヌションで䞀般的なパタヌンです。OAuth2 サヌバヌをオヌセンティケヌタヌずしお䜿甚するため、OAuth2 サヌバヌを䜿甚しおバック゚ンドリ゜ヌスサヌバヌのトヌクンを付䞎するこずもできたす。Spring Cloud はアクセストヌクンをバック゚ンドに自動的に䞭継し、UI サヌバヌずリ゜ヌスサヌバヌの䞡方の実装をさらに簡玠化できるようにしたす。

リマむンダヌ: このセクションでサンプルアプリケヌションを䜿甚しおいる堎合は、ブラりザヌのキャッシュの Cookie ず HTTP 基本認蚌情報を必ずクリアしおください。Chrome では、単䞀サヌバヌでこれを行う最良の方法は、新しいシヌクレットりィンドりを開くこずです。

OAuth2 認可サヌバヌの䜜成

最初のステップは、認蚌ずトヌクン管理を凊理する新しいサヌバヌを䜜成するこずです。パヌト I の手順に埓っお、Spring Boot Initializr から始めるこずができたす。䟋: UN*X のようなシステムで curl を䜿甚:

$ curl https://start.spring.io/starter.tgz -d dependencies=web,security -d name=authserver | tar -xzvf -

その埌、そのプロゞェクトデフォルトでは通垞の Maven Java プロゞェクトをお気に入りの IDE にむンポヌトするか、コマンドラむンでファむルず "mvn" を操䜜するだけです。

OAuth2 䟝存関係の远加

Spring OAuth (英語) 䟝存関係を远加する必芁があるため、POM [GitHub] (英語) に以䞋を远加したす。

pom.xml
<dependency>
  <groupId>org.springframework.security.oauth</groupId>
  <artifactId>spring-security-oauth2</artifactId>
</dependency>

認可サヌバヌの実装は非垞に簡単です。最小バヌゞョンは次のようになりたす。

AuthserverApplication.java
@SpringBootApplication
@EnableAuthorizationServer
public class AuthserverApplication extends WebMvcConfigurerAdapter {

  public static void main(String[] args) {
    SpringApplication.run(AuthserverApplication.class, args);
  }

}

@EnableAuthorizationServer を远加した埌あず 1 ぀だけ行う必芁がありたす。

application.properties
---
...
security.oauth2.client.clientId: acme
security.oauth2.client.clientSecret: acmesecret
security.oauth2.client.authorized-grant-types: authorization_code,refresh_token,password
security.oauth2.client.scope: openid
---

これにより、クラむアント "acme" がシヌクレットず "authorization_code" を含むいく぀かの認可された認可型で登録されたす。

次に、テスト甚の予枬可胜なパスワヌドを䜿甚しお、ポヌト 9999 で実行したす。

application.properties
server.port=9999
security.user.password=password
server.contextPath=/uaa
...

たた、デフォルト"/"を䜿甚しないようにコンテキストパスを蚭定したす。そうしないず、ロヌカルホスト䞊の他のサヌバヌの Cookie が間違ったサヌバヌに送信される可胜性がありたす。サヌバヌを実行するず、サヌバヌが機胜しおいるこずを確認できたす。

$ mvn spring-boot:run

たたは、IDE で main() ãƒ¡ã‚œãƒƒãƒ‰ã‚’開始したす。

認可サヌバヌのテスト

サヌバヌは Spring Boot のデフォルトのセキュリティ蚭定を䜿甚しおいるため、パヌト I のサヌバヌず同様に、HTTP Basic 認蚌によっお保護されたす。認可コヌドトヌクンの付䞎 [IETF] (英語) を開始するには、認可゚ンドポむントにアクセスしたす。http://localhost:9999/uaa/oauth/authorize?response_type=code&client_id=acme&redirect_uri=http://example.com で認蚌されるず、認蚌コヌドが添付された example.com ぞのリダむレクトが取埗されたす。䟋: http://example.com/?code=jYWioI (英語)

このサンプルアプリケヌションの目的のために、リダむレクトが登録されおいないクラむアント "acme" を䜜成したした。これにより、example.com ぞのリダむレクトを取埗できたす。本番アプリケヌションでは、垞にリダむレクトを登録するおよび HTTPS を䜿甚する必芁がありたす。

トヌクン゚ンドポむントの "acme" クラむアント資栌情報を䜿甚しお、コヌドをアクセストヌクンに亀換できたす。

$ curl acme:acmesecret@localhost:9999/uaa/oauth/token  \
-d grant_type=authorization_code -d client_id=acme     \
-d redirect_uri=http://example.com -d code=jYWioI
{"access_token":"2219199c-966e-4466-8b7e-12bb9038c9bb","token_type":"bearer","refresh_token":"d193caf4-5643-4988-9a4a-1c03c9d657aa","expires_in":43199,"scope":"openid"}

アクセストヌクンは UUID"2219199c 
 "であり、サヌバヌ内のメモリ内トヌクンストアによっおバックアップされたす。たた、珟圚のトヌクンが期限切れになったずきに新しいアクセストヌクンを取埗するために䜿甚できるリフレッシュトヌクンも取埗したした。

"acme" クラむアントに "password" 付䞎を認可したため、認蚌コヌドの代わりに curl ずナヌザヌ資栌情報を䜿甚しお、トヌクン゚ンドポむントからトヌクンを盎接取埗するこずもできたす。これはブラりザヌベヌスのクラむアントには適しおいたせんが、テストには圹立ちたす。

䞊蚘のリンクをたどるず、Spring OAuth が提䟛するホワむトラベル UI が衚瀺されたす。たず、これを䜿甚したす。埌から戻っお、パヌト II の自己完結型サヌバヌの堎合ず同じように匷化するこずができたす。

リ゜ヌスサヌバヌの倉曎

パヌト IV から続けるず、リ゜ヌスサヌバヌは認蚌に Spring Session [GitHub] (英語) を䜿甚しおいるため、それを取り出しお Spring OAuth に眮き換えるこずができたす。Spring Session ず Redis の䟝存関係も削陀する必芁があるため、これを眮き換えたす。

pom.xml
<dependency>
  <groupId>org.springframework.session</groupId>
  <artifactId>spring-session</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-redis</artifactId>
</dependency>

これずずもに:

pom.xml
<dependency>
  <groupId>org.springframework.security.oauth</groupId>
  <artifactId>spring-security-oauth2</artifactId>
</dependency>

次に、セッション Filter ã‚’メむンアプリケヌションクラス [GitHub] (英語) から削陀し、䟿利な @EnableResourceServer ã‚¢ãƒŽãƒ†ãƒŒã‚·ãƒ§ãƒ³ïŒˆSpring Security OAuth2 からに眮き換えたす。

ResourceApplication.java
@SpringBootApplication
@RestController
@EnableResourceServer
class ResourceApplication {

  @RequestMapping("/")
  public Message home() {
    return new Message("Hello World");
  }

  public static void main(String[] args) {
    SpringApplication.run(ResourceApplication.class, args);
  }
}

その 1 ぀の倉曎で、アプリは HTTP Basic ではなくアクセストヌクンを芁求する準備ができおいたすが、実際にプロセスを完了するには、構成を倉曎する必芁がありたす。リ゜ヌスサヌバヌが䞎えられたトヌクンをデコヌドしおナヌザヌを認蚌できるように、少量の倖郚構成"application.properties" を远加したす。

application.properties
...
security.oauth2.resource.userInfoUri: http://localhost:9999/uaa/user

これは、トヌクンを䜿甚しお "/user" ゚ンドポむントにアクセスし、それを䜿甚しお認蚌情報を取埗できるこずをサヌバヌに通知したすFacebook API の "/me" ゚ンドポむント (英語) に少し䌌おいたす。事実䞊、Spring OAuth2 の ResourceServerTokenServices ã‚€ãƒ³ã‚¿ãƒŒãƒ•ェヌスで衚されるように、リ゜ヌスサヌバヌがトヌクンをデコヌドする方法を提䟛したす。

アプリケヌションを実行し、コマンドラむンクラむアントでホヌムペヌゞにアクセスしたす。

$ curl -v localhost:9000
> GET / HTTP/1.1
> User-Agent: curl/7.35.0
> Host: localhost:9000
> Accept: */*
>
< HTTP/1.1 401 Unauthorized
...
< WWW-Authenticate: Bearer realm="null", error="unauthorized", error_description="An Authentication object was not found in the SecurityContext"
< Content-Type: application/json;charset=UTF-8
{"error":"unauthorized","error_description":"An Authentication object was not found in the SecurityContext"}

たた、ベアラヌトヌクンが必芁であるこずを瀺す "WWW-Authenticate" ヘッダヌを持぀ 401 が衚瀺されたす。

userInfoUri ã¯ã€ãƒˆãƒŒã‚¯ãƒ³ã‚’デコヌドする方法でリ゜ヌスサヌバヌを接続する唯䞀の方法ではありたせん。実際、これは最も䞀般的な分母のようなもので仕様の䞀郚ではありたせん、OAuth2 プロバむダヌFacebook、Cloud Foundry、Github などから非垞に頻繁に利甚でき、他の遞択肢も利甚できたす。たずえば、トヌクン自䜓でナヌザヌ認蚌を゚ンコヌドする䟋: JWT (英語) を䜿甚か、共有バック゚ンドストアを䜿甚したす。CloudFoundry には /token_info ã‚šãƒ³ãƒ‰ãƒã‚€ãƒ³ãƒˆã‚‚あり、ナヌザヌ情報゚ンドポむントよりも詳现な情報を提䟛したすが、より完党な認蚌が必芁です。さたざたなオプションが圓然さたざたな利点ずトレヌドオフを提䟛したすが、それらの詳现な説明はこのセクションの範囲倖です。

ナヌザヌ゚ンドポむントの実装

認可サヌバヌでは、その゚ンドポむントを簡単に远加できたす

AuthserverApplication.java
@SpringBootApplication
@RestController
@EnableAuthorizationServer
@EnableResourceServer
public class AuthserverApplication {

  @RequestMapping("/user")
  public Principal user(Principal user) {
    return user;
  }

  ...

}

パヌト II の UI サヌバヌず同じ @RequestMapping ã‚’远加し、Spring OAuth からの @EnableResourceServer ã‚¢ãƒŽãƒ†ãƒŒã‚·ãƒ§ãƒ³ã‚‚远加したした。これは、デフォルトで "/oauth/*" ゚ンドポむントを陀く認可サヌバヌ内のすべおを保護したす。

゚ンドポむントを配眮したら、認可サヌバヌによっお䜜成されたベアラヌトヌクンを受け入れるようになったため、それずグリヌティングリ゜ヌスをテストできたす。

$ TOKEN=2219199c-966e-4466-8b7e-12bb9038c9bb
$ curl -H "Authorization: Bearer $TOKEN" localhost:9000
{"id":"03af8be3-2fc3-4d75-acf7-c484d9cf32b1","content":"Hello World"}
$ curl -H "Authorization: Bearer $TOKEN" localhost:9999/uaa/user
{"details":...,"principal":{"username":"user",...},"name":"user"}

自分で認蚌サヌバヌから取埗したアクセストヌクンの倀を代入しお、それを自分で動䜜させたす。

UI サヌバヌ

完了する必芁があるこのアプリケヌションの最埌の郚分は、認蚌郚分を抜出し、認可サヌバヌに委譲する UI サヌバヌです。そのため、リ゜ヌスサヌバヌず同様に、たず Spring Session ず Redis の䟝存関係を削陀し、Spring OAuth2 に眮き換える必芁がありたす。UI 局で Zuul を䜿甚しおいるため、実際には spring-security-oauth2 ã§ã¯ãªã spring-cloud-starter-oauth2 ã‚’盎接䜿甚したすこれにより、プロキシを介しおトヌクンを䞭継するための自動構成が蚭定されたす。

それが完了したら、セッションフィルタヌず "/user" ゚ンドポむントも削陀し、@EnableOAuth2Sso ã‚¢ãƒŽãƒ†ãƒŒã‚·ãƒ§ãƒ³ã‚’䜿甚しお認可サヌバヌにリダむレクトするようにアプリケヌションを蚭定できたす。

UiApplication.java
@SpringBootApplication
@EnableZuulProxy
@EnableOAuth2Sso
public class UiApplication {

  public static void main(String[] args) {
    SpringApplication.run(UiApplication.class, args);
  }

...

}

UI サヌバヌが @EnableZuulProxy のおかげで API ゲヌトりェむずしお機胜し、YAML でルヌトマッピングを宣蚀できるこずをパヌト IV から思い出しおください。"/user" ゚ンドポむントを認蚌サヌバヌにプロキシできたす。

application.yml
zuul:
  routes:
    resource:
      path: /resource/**
      url: http://localhost:9000
    user:
      path: /user/**
      url: http://localhost:9999/uaa/user

最埌に、@EnableOAuth2Sso によっお蚭定された SSO フィルタヌチェヌンのデフォルトを倉曎するために䜿甚されるため、アプリケヌションを WebSecurityConfigurerAdapter ã«å€‰æ›Žã™ã‚‹å¿…芁がありたす。

SecurityConfiguration.java
@SpringBootApplication
@EnableZuulProxy
@EnableOAuth2Sso
public class UiApplication extends WebSecurityConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception {
      http
          .logout().logoutSuccessUrl("/").and()
          .authorizeRequests().antMatchers("/index.html", "/app.html", "/")
          .permitAll().anyRequest().authenticated().and()
          .csrf()
            .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
    }

}

䞻な倉曎点基本クラス名を陀くは、マッチャヌが独自のメ゜ッドに入るこずであり、formLogin() ã¯ã‚‚う必芁ありたせん。明瀺的な logout() æ§‹æˆã¯ã€ä¿è­·ã•れおいない成功 URL を明瀺的に远加するため、/logout ãžã® XHR リク゚ストは正垞に戻りたす。

@EnableOAuth2Sso ã‚¢ãƒŽãƒ†ãƒŒã‚·ãƒ§ãƒ³ã«ã¯ã€é©åˆ‡ãªèªå¯ã‚µãƒŒãƒãƒŒãšé€šä¿¡ã—お認蚌できるようにするためのいく぀かの必須の倖郚構成プロパティもありたす。application.yml でこれが必芁です:

application.yml
security:
  ...
  oauth2:
    client:
      accessTokenUri: http://localhost:9999/uaa/oauth/token
      userAuthorizationUri: http://localhost:9999/uaa/oauth/authorize
      clientId: acme
      clientSecret: acmesecret
    resource:
      userInfoUri: http://localhost:9999/uaa/user

その倧郚分は、OAuth2 クラむアント "acme" ず認可サヌバヌの堎所に関するものです。ナヌザヌが UI アプリ自䜓で認蚌できるように、userInfoUri ã‚‚ありたすリ゜ヌスサヌバヌの堎合ず同様。

UI アプリケヌションで期限切れのアクセストヌクンを自動的にリフレッシュできるようにするには、䞭継を行う Zuul フィルタヌに OAuth2RestOperations ã‚’挿入する必芁がありたす。これを行うには、その型の Bean を䜜成するだけです詳现に぀いおは OAuth2TokenRelayFilter ã‚’確認しおください。
@Bean
protected OAuth2RestTemplate OAuth2RestTemplate(
    OAuth2ProtectedResourceDetails resource, OAuth2ClientContext context) {
  return new OAuth2RestTemplate(resource, context);
}

クラむアント

フロント゚ンドの UI アプリケヌションには、認可サヌバヌぞのリダむレクトをトリガヌするためにただ行う必芁がある調敎がいく぀かありたす。この簡単なデモでは、Angular アプリを必芁最䜎限の芁玠にたずめお、䜕が起こっおいるかをより明確に確認できたす。そのため、今のずころ、フォヌムやルヌトの䜿甚を控え、単䞀の Angular コンポヌネントに戻りたす。

app.component.ts
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import 'rxjs/add/operator/finally';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {

  title = 'Demo';
  authenticated = false;
  greeting = {};

  constructor(private http: HttpClient) {
    this.authenticate();
  }

  authenticate() {

    this.http.get('user').subscribe(response => {
        if (response['name']) {
            this.authenticated = true;
            this.http.get('resource').subscribe(data => this.greeting = data);
        } else {
            this.authenticated = false;
        }
    }, () => { this.authenticated = false; });

  }
  logout() {
      this.http.post('logout', {}).finally(() => {
          this.authenticated = false;
      }).subscribe();
  }

}

AppComponent ã¯ã™ã¹ãŠã‚’凊理し、ナヌザヌの詳现ず、成功した堎合は挚拶を取埗したす。たた、logout æ©Ÿèƒœã‚‚提䟛したす。

次に、この新しいコンポヌネントのテンプレヌトを䜜成する必芁がありたす。

app.component.html

<div class="container">
  <ul class="nav nav-pills">
    <li><a>Home</a></li>
    <li><a href="login">Login</a></li>
    <li><a (click)="logout()">Logout</a></li>
  </ul>
</div>
<div class="container">
<h1>Greeting</h1>
<div [hidden]="!authenticated">
	<p>The ID is {{greeting.id}}</p>
	<p>The content is {{greeting.content}}</p>
</div>
<div [hidden]="authenticated">
	<p>Login to see your greeting</p>
</div>

それをホヌムペヌゞに <app-root/> ずしお含めたす。

「ログむン」のナビゲヌションリンクは、href ïŒˆAngular ルヌトではないずの通垞のリンクであるこずに泚意しおください。これが送信される "/login" ゚ンドポむントは Spring Security によっお凊理され、ナヌザヌが認蚌されおいない堎合は、認蚌サヌバヌにリダむレクトされたす。

仕組みは

すべおのサヌバヌを䞀緒に実行し、http://localhost:8080 のブラりザヌで UI にアクセスしたす。「ログむン」リンクをクリックするず、OAuth2 からフェッチされたグリヌティングを䜿甚しお UI のホヌムペヌゞにリダむレクトされる前に、認蚌サヌバヌHTTP Basic ポップアップにリダむレクトされ、トヌクン付䞎ホワむトラベル HTMLが認可されたす。UI を認蚌したのず同じトヌクンを䜿甚するリ゜ヌスサヌバヌ。

䞀郚の開発者ツヌルを䜿甚するず、ブラりザヌずバック゚ンド間の盞互䜜甚をブラりザヌで確認できたす通垞、F12 はこれを開き、デフォルトで Chrome で動䜜し、Firefox でプラグむンが必芁になる堎合がありたす。抂芁は次のずおりです。

動詞 パス ステヌタス レスポンス

GET

/

200

index.html

GET

/*.js

200

角床からのアセット

GET

/user

302

ログむンペヌゞにリダむレクト

GET

/login

302

認蚌サヌバヌにリダむレクトする

GET

uaa/oauth/authorize

401

無芖

GET

/login

302

認蚌サヌバヌにリダむレクトする

GET

uaa/oauth/authorize

200

HTTP 基本認蚌はここで発生したす

POST

uaa/oauth/authorize

302

ナヌザヌは蚱可を承認し、/login にリダむレクトしたす

GET

/login

302

ホヌムペヌゞにリダむレクト

GET

/user

200

プロキシJSON 認蚌枈みナヌザヌ

GET

/app.html

200

ホヌムペヌゞの HTML パヌシャル

GET

/resource

200

プロキシJSON グリヌティング

uaaで始たるリク゚ストは、認可サヌバヌに察するものです。「無芖」ずマヌクされたレスポンスは、XHR 呌び出しで Angular によっお受信されたレスポンスであり、そのデヌタを凊理しおいないため、フロアにドロップされたす。"/user" リ゜ヌスの堎合、認蚌されたナヌザヌを探したすが、最初の呌び出しには存圚しないため、そのレスポンスはドロップされたす。

UI の "/trace" ゚ンドポむント䞋にスクロヌルに、"/user" および "/resource" ぞのプロキシされたバック゚ンドリク゚ストが衚瀺されたす。認蚌には、Cookie の代わりに remote:true ãšãƒ™ã‚¢ãƒ©ãƒŒãƒˆãƒŒã‚¯ãƒ³ãŒäœ¿ç”šã•れたすパヌト IV の堎合ず同様。Spring Cloud Security がこれを凊理しおくれたした。@EnableOAuth2Sso ãš @EnableZuulProxy ãŒã‚るこずを認識するこずで、デフォルトでトヌクンをプロキシされたバック゚ンドに䞭継したいこずがわかりたした。

前のセクションず同様に、認蚌のクロスオヌバヌの可胜性がないように、"/trace" に別のブラりザヌを䜿甚しおみおくださいたずえば、UI のテストに Chrome を䜿甚した堎合は Firefox を䜿甚しおください。

ログアりト゚クスペリ゚ンス

「ログアりト」リンクをクリックするず、ナヌザヌが UI サヌバヌで認蚌されなくなるため、ホヌムペヌゞが倉曎されるグリヌティングが衚瀺されなくなるこずがわかりたす。「ログむン」をクリックしたすが、実際には認蚌サヌバヌで認蚌ず認可のサむクルに戻る必芁はありたせんログアりトしおいないため。それが望たしいナヌザヌ゚クスペリ゚ンスであるかどうかに぀いお意芋は分かれたすが、悪名高いトリッキヌな問題ですシングルサむンアりト: Science Direct の蚘事 (英語) および Shibboleth のドキュメント (英語) 。理想的なナヌザヌ゚クスペリ゚ンスは技術的に実珟可胜ではないかもしれたせん。たた、ナヌザヌが望むこずを本圓に望んでいるこずを疑わなければならないこずもありたす。「「ログアりト」しおログアりトしおほしい」ずいうのは簡単ですが、「䜕からログアりトしたすか この SSO サヌバヌによっお制埡されおいるすべおのシステムからログアりトしたすか、それずもあなただけのシステムからログアりトしたすか」 「ログアりト」リンクをクリックしたしたか」興味がある堎合は、このチュヌトリアルの埌のセクションで詳现に説明されおいたす。

結論

これで、Spring Security および Angular スタックの浅いツアヌはほが終了です。珟圚、UI/API ゲヌトりェむ、リ゜ヌスサヌバヌ、認可サヌバヌ / トヌクン付䞎者の 3 ぀の個別のコンポヌネントで明確な責任を持぀優れたアヌキテクチャがありたす。すべおのレむダヌの非ビゞネスコヌドの量が最小限になり、より倚くのビゞネスロゞックを䜿甚しお実装を継承および改善する堎所を簡単に確認できたす。次のステップは、認蚌サヌバヌの UI を敎理し、JavaScript クラむアントでのテストを含むいく぀かのテストを远加するこずです。もう 1 ぀の興味深いタスクは、すべおのボむラヌプレヌトコヌドを抜出し、Spring Security ず Spring Session の自動構成、および Angular ピヌスのナビゲヌションコントロヌラヌ甚のいく぀かの webjar リ゜ヌスを含むラむブラリ "spring-security-angular" などに配眮するこずです。このシリヌズのセクションを読んだら、Angular たたは Spring Security の内郚動䜜を孊びたいず思っおいた人はおそらくがっかりするでしょうが、それらがどのようにうたく連携し、少しの構成がどのように長くなるかを知りたい堎合は方法、うたくいけば、良い経隓をしたでしょう。Spring Cloud は新しく、これらのサンプルは䜜成時にスナップショットが必芁でしたが、リリヌス候補があり、GA リリヌスが間もなくリリヌスされるため、それをチェックしお、Github (英語) たたは gitter.im (英語) を介し [GitHub] (英語) おフィヌドバックを送信しおください。

シリヌズの次のセクションでは、アクセス決定認蚌以倖に぀いお説明し、同じプロキシの背埌で耇数の UI アプリケヌションを䜿甚したす。

補遺: 認可サヌバヌのブヌトストラップ UI および JWT トヌクン

このアプリケヌションの別のバヌゞョンは、Github の゜ヌスコヌドにありたす。Github の゜ヌスコヌドには、パヌト II (英語) のログむンペヌゞず同じように、きれいなログむンペヌゞずナヌザヌ承認ペヌゞが実装されおいたす。たた、JWT (英語) を䜿甚しおトヌクンを゚ンコヌドするため、"/user" ゚ンドポむントを䜿甚する代わりに、リ゜ヌスサヌバヌはトヌクン自䜓から十分な情報をプルしお単玔な認蚌を行うこずができたす。ブラりザヌクラむアントは匕き続き UI サヌバヌを介しおプロキシされおそれを䜿甚するため、ナヌザヌが認蚌されおいるかどうかを刀断できたす実際のアプリケヌションでのリ゜ヌスサヌバヌぞの呌び出しの可胜性ず比范しお、それほど頻繁に行う必芁はありたせん。

耇数の UI アプリケヌションずゲヌトりェむ

このセクションでは、「シングルペヌゞアプリケヌション」で Spring Security を Angular (英語) ず䜿甚する方法に぀いお匕き続き説明したす。ここでは、Spring Session (英語) ず Spring Cloud を䜿甚しお、パヌト II ず IV で構築したシステムの機胜を組み合わせ、実際にはたったく異なる責任を持぀ 3 ぀のシングルペヌゞアプリケヌションを構築する方法を瀺したす。目的は、API リ゜ヌスだけでなく、バック゚ンドサヌバヌから UI をロヌドするために䜿甚されるパヌト IV のようなゲヌトりェむを構築するこずです。ゲヌトりェむを䜿甚しお認蚌をバック゚ンドに枡すこずにより、パヌト II のトヌクンの問題を単玔化したす。次に、システムを継承しお、ゲヌトりェむで ID ず認蚌を制埡しながら、バック゚ンドでロヌカルで詳现なアクセス決定を行う方法を瀺したす。これは、䞀般に分散システムを構築するための非垞に匷力なモデルであり、構築するコヌドに機胜を導入する際に怜蚎できる倚くの利点がありたす。

リマむンダヌ: このセクションでサンプルアプリケヌションを䜿甚しおいる堎合は、ブラりザヌのキャッシュの Cookie ず HTTP 基本認蚌情報を必ずクリアしおください。Chrome でこれを行う最良の方法は、新しいシヌクレットりィンドりを開くこずです。

タヌゲットアヌキテクチャ

以䞋は、最初に構築する基本システムの図です。

Components of the System

このシリヌズの他のサンプルアプリケヌションず同様に、UIHTML および JavaScriptずリ゜ヌスサヌバヌがありたす。セクション IV のサンプルず同様に、ゲヌトりェむがありたすが、ここでは UI の䞀郚ではなく、別個のものです。UI は事実䞊バック゚ンドの䞀郚になり、機胜を再構成および再実装するための遞択肢がさらに広がりたす。たた、これから説明する他の利点ももたらしたす。

ブラりザヌはすべおのためにゲヌトりェむにアクセスし、バック゚ンドのアヌキテクチャを知る必芁はありたせん基本的に、バック゚ンドがあるこずを知りたせん。このゲヌトりェむでブラりザヌが行うこずの 1 ぀に認蚌がありたす。セクション II のようにナヌザヌ名ずパスワヌドを送信し、代わりに Cookie を取埗したす。埌続のリク゚ストでは、Cookie が自動的に提瀺され、ゲヌトりェむはそれをバック゚ンドに枡したす。Cookie の受け枡しを有効にするために、クラむアントでコヌドを蚘述する必芁はありたせん。バック゚ンドは Cookie を䜿甚しお認蚌し、すべおのコンポヌネントがセッションを共有するため、ナヌザヌに関する同じ情報を共有したす。これず比范しお、ゲヌトりェむで Cookie をアクセストヌクンに倉換する必芁があり、アクセストヌクンはすべおのバック゚ンドコンポヌネントで個別にデコヌドする必芁があるセクション V ず比范しおください。

セクション IV ず同様に、ゲヌトりェむはクラむアントずサヌバヌ間の察話を簡玠化し、セキュリティを凊理するための小さく明確に定矩された衚面を提䟛したす。䟋: クロスオリゞンリ゜ヌス共有 [Mozilla] に぀いお心配する必芁はありたせん。これは間違いを犯しやすいため、歓迎されたす。

ビルドするプロゞェクト党䜓の゜ヌスコヌドはここの Github (英語) にあるため、必芁に応じおプロゞェクトを耇補し、そこから盎接䜜業するこずができたす。このシステムの終了状態には远加のコンポヌネント"double-admin"があるため、今は無芖しおください。

バック゚ンドの構築

このアヌキテクチャでは、バック゚ンドはセクション III で構築した "spring-session" [GitHub] (英語) サンプルに非垞に䌌おいたすが、実際にはログむンペヌゞは必芁ありたせん。ここで必芁なものに到達する最も簡単な方法は、おそらくセクション III から「リ゜ヌス」サヌバヌをコピヌし、セクション I の「基本」 [GitHub] (英語) サンプルから UI を取埗するこずです。「基本」UI からここで必芁な UI に到達するには、いく぀かの䟝存関係を远加するだけですセクション III で Spring Session [GitHub] (英語) を最初に䜿甚したずきのように。

pom.xml
<dependency>
  <groupId>org.springframework.session</groupId>
  <artifactId>spring-session</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-redis</artifactId>
</dependency>

これは UI になっおいるため、"/resource" ゚ンドポむントは必芁ありたせん。これを行うず、非垞に単玔な Angular アプリケヌション「基本」サンプルず同じが䜜成され、その動䜜のテストず掚論が倧幅に簡玠化されたす。

最埌に、このサヌバヌをバック゚ンドずしお実行したいため、application.properties でリッスンするデフォルト以倖のポヌトを指定したす。

application.properties
server.port: 8081
security.sessions: NEVER

それがコンテンツ application.properties å…šäœ“である堎合、アプリケヌションは安党であり、"user" ず呌ばれるナヌザヌがランダムなパスワヌドでアクセスできたすが、起動時にコン゜ヌルにログレベル INFO で出力されたす。"security.sessions" 蚭定は、Spring Security が認蚌トヌクンずしお Cookie を受け入れるが、Cookie がすでに存圚しない限り Cookie を䜜成しないこずを意味したす。

リ゜ヌスサヌバヌ

リ゜ヌスサヌバヌは、既存のサンプルの 1 ぀から簡単に生成できたす。これは、セクション III の "spring-session" リ゜ヌスサヌバヌず同じです。分散セッションデヌタを取埗するための "/resource" ゚ンドポむント Spring Session だけです。このサヌバヌにリッスンするデフォルト以倖のポヌトを持たせ、セッションで認蚌を怜玢できるようにしたいため、これが必芁ですapplication.properties で。

application.properties
server.port: 9000
security.sessions: NEVER

このチュヌトリアルの新しい機胜であるメッセヌゞリ゜ヌスぞの倉曎を POST する予定です。これは、バック゚ンドで CSRF 保護が必芁になるこずを意味し、Spring Security を Angular ずうたく動䜜させるために通垞のトリックを行う必芁がありたす。

@Override
protected void configure(HttpSecurity http) throws Exception {
	http.csrf()
			.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}

完成したサンプルは、こちらの github (英語) にありたす。

ゲヌトりェむ

ゲヌトりェむの初期実装おそらく動䜜する可胜性のある最も単玔なものでは、空の Spring Boot Web アプリケヌションを取埗しお @EnableZuulProxy ã‚¢ãƒŽãƒ†ãƒŒã‚·ãƒ§ãƒ³ã‚’远加するだけです。セクション I で芋たように、それを行うにはいく぀かの方法があり、その 1 ぀は Spring Initializr を䜿甚しおスケルトンプロゞェクトを生成するこずです。さらに簡単なのは、Spring Cloud Initializr (英語) を䜿甚するこずですが、これは同じこずですが、Spring Cloud (英語) アプリケヌション甚です。セクション I ず同じ䞀連のコマンドラむン操䜜を䜿甚したす。

$ mkdir gateway && cd gateway
$ curl https://cloud-start.spring.io/starter.tgz -d style=web \
  -d style=security -d style=cloud-zuul -d name=gateway \
  -d style=redis | tar -xzvf -

次に、そのプロゞェクトデフォルトでは通垞の Maven Java プロゞェクトをお気に入りの IDE にむンポヌトするか、コマンドラむンでファむルず "mvn" を操䜜するだけです。そこから行きたい堎合は github (英語) にバヌゞョンがありたすが、ただ必芁のない远加機胜がいく぀かありたす。

空癜の Initializr アプリケヌションから始めお、Spring Session 䟝存関係を远加したす䞊蚘の UI のように。ゲヌトりェむを実行する準備はできおいたすが、バック゚ンドサヌビスに぀いおはただ知らないため、application.yml ã§ã‚»ãƒƒãƒˆã‚¢ãƒƒãƒ—しおみたしょう䞊蚘の curl を行った堎合は application.properties ã‹ã‚‰åå‰ã‚’倉曎したす。

application.yml
zuul:
  sensitive-headers:
  routes:
    ui:
      url: http://localhost:8081
   resource:
      url: http://localhost:9000
security:
  user:
    password:
      password
  sessions: ALWAYS

プロキシには 2 ぀のルヌトがあり、どちらも sensitive-headers ãƒ—ロパティを䜿甚しお Cookie をダりンストリヌムで枡したす。どちらも UI ずリ゜ヌスサヌバヌに 1 ぀ず぀あり、デフォルトのパスワヌドずセッション氞続性戊略を蚭定しおいたすSpring Security に垞にセッションを䜜成するように䌝えたす認蚌。認蚌ずセッションをゲヌトりェむで管理する必芁があるため、この最埌のビットは重芁です。

皌働

珟圚、3 ぀のポヌトで実行される 3 ぀のコンポヌネントがありたす。ブラりザヌを http://localhost:8080/ui/ に向けるず、HTTP Basic チャレンゞを取埗する必芁があり、「ナヌザヌ / パスワヌド」ゲヌトりェむの資栌情報ずしお認蚌できたす。これを行うず、バック゚ンド経由で UI に挚拶が衚瀺されたす。プロキシを介しおリ゜ヌスサヌバヌに呌び出したす。

䞀郚の開発者ツヌルを䜿甚するず、ブラりザヌずバック゚ンド間の盞互䜜甚をブラりザヌで確認できたす通垞、F12 はこれを開き、デフォルトで Chrome で動䜜し、Firefox でプラグむンが必芁になる堎合がありたす。抂芁は次のずおりです。

動詞 パス ステヌタス レスポンス

GET

/ui/

401

認蚌のためのブラりザヌプロンプト

GET

/ui/

200

index.html

GET

/ui/*.js

200

Angular アセット

GET

/ui/js/hello.js

200

アプリケヌションロゞック

GET

/ui/user

200

認蚌

GET

/resource/

200

JSON グリヌティング

ブラりザヌはホヌムペヌゞの読み蟌みを単䞀の察話ずしお扱うため、401 が衚瀺されない堎合がありたす。すべおのリク゚ストはプロキシされたす管理甚のアクチュ゚ヌタヌ゚ンドポむントを超えお、ゲヌトりェむにはただコンテンツがありたせん。

うたくいきたした 2 ぀のバック゚ンドサヌバヌがあり、その 1 ぀は UI であり、それぞれが独立した機胜を持ち、独立しおテストするこずができ、それらは制埡し、認蚌を構成したセキュアゲヌトりェむず䞀緒に接続されたす。ブラりザヌがバック゚ンドにアクセスできない堎合は重芁ではありたせん実際、物理的なセキュリティをさらに制埡できるため、おそらく利点です。

ログむンフォヌムの远加

セクション I の「基本」サンプルのように、ログむンフォヌムをゲヌトりェむに远加できたす。セクション II からコヌドをコピヌしたす。それを行うずき、ゲヌトりェむにいく぀かの基本的なナビゲヌション芁玠を远加するこずもできるため、ナヌザヌはプロキシの UI バック゚ンドぞのパスを知る必芁がありたせん。それでは、最初に静的アセットを「単䞀」UI からゲヌトりェむにコピヌし、メッセヌゞレンダリングを削陀しお、ログむンフォヌムをホヌムペヌゞ<app/> ã®ã©ã“かに挿入したす。

app.html
<div class="container" [hidden]="authenticated">
	<form role="form" (submit)="login()">
		<div class="form-group">
			<label for="username">Username:</label> <input type="text"
				class="form-control" id="username" name="username"
				[(ngModel)]="credentials.username" />
		</div>
		<div class="form-group">
			<label for="password">Password:</label> <input type="password"
				class="form-control" id="password" name="password"
				[(ngModel)]="credentials.password" />
		</div>
		<button type="submit" class="btn btn-primary">Submit</button>
	</form>
</div>

メッセヌゞのレンダリングの代わりに、玠敵な倧きなナビゲヌションボタンがありたす。

index.html
<div class="container" [hidden]="!authenticated">
	<a class="btn btn-primary" href="/ui/">Go To User Interface</a>
</div>

github でサンプルを芋おいる堎合、「ログアりト」ボタンのある最小限のナビゲヌションバヌもありたす。スクリヌンショットのログむンフォヌムは次のずおりです。

Login Page

ログむンフォヌムをサポヌトするには、<form/> で宣蚀した login() é–¢æ•°ã‚’実装するコンポヌネントを含む TypeScript が必芁です。たた、authenticated ãƒ•ラグを蚭定しお、ナヌザヌが認蚌されおいるかどうかに応じおホヌムペヌゞのレンダリングが異なるようにする必芁がありたす。䟋

app.component.ts
include::src/app/app.component.ts

login() é–¢æ•°ã®å®Ÿè£…はセクション II の実装に䌌おいたす。

この単玔なアプリケヌションにはコンポヌネントが 1 ぀しかないため、self ã‚’䜿甚しお authenticated ãƒ•ラグを栌玍できたす。

この匷化されたゲヌトりェむを実行する堎合、UI の URL を芚える必芁はなく、ホヌムペヌゞを読み蟌んでリンクをたどるこずができたす。認蚌枈みナヌザヌのホヌムペヌゞは次のずおりです。

Home Page

バック゚ンドでのきめ现かいアクセス決定

これたでのアプリケヌションは、関数にはセクション III たたはセクション IV のものず非垞によく䌌おいたすが、専甚のゲヌトりェむが远加されおいたす。䜙分なレむダヌの利点はただ明らかではないかもしれたせんが、システムを少し拡匵するこずで匷調できたす。ナヌザヌがメむン UI でコンテンツを「管理」するために、そのゲヌトりェむを䜿甚しお別のバック゚ンド UI を公開し、この機胜ぞのアクセスを特別なロヌルを持぀ナヌザヌに制限するずしたす。プロキシの背埌に「管理」アプリケヌションを远加するず、システムは次のようになりたす。

Components of the System

application.yml のゲヌトりェむには、新しいコンポヌネントAdminず新しいルヌトがありたす。

application.yml
zuul:
  sensitive-headers:
  routes:
    ui:
      url: http://localhost:8081
    admin:
      url: http://localhost:8082
    resource:
      url: http://localhost:9000

"USER" ロヌルのナヌザヌが既存の UI を䜿甚できるこずは、管理アプリケヌションにアクセスするために "ADMIN" ロヌルが必芁であるずいう事実ず同様に、ゲヌトりェむボックス緑色の文字の䞊のブロック図に瀺されおいたす。"ADMIN" ロヌルのアクセス決定は、ゲヌトりェむで適甚できたす。その堎合、WebSecurityConfigurerAdapter に衚瀺されるか、管理アプリケヌション自䜓に適甚できたすこれを行う方法に぀いおは埌述したす。

最初に、新しい Spring Boot アプリケヌションを䜜成するか、UI をコピヌしお線集したす。UI アプリで倉曎する必芁はありたせんが、最初に名前を倉曎する必芁がありたす。完成したアプリはここの Github (英語) にありたす。

管理アプリケヌション内で、"READER" ロヌルず "WRITER" ロヌルを区別し、監査者であるナヌザヌに、メむン管理者ナヌザヌによる倉曎の衚瀺を蚱可できるようにするずしたす。これはきめ现かいアクセス決定であり、ルヌルはバック゚ンドアプリケヌションでのみ知られ、知られるべきです。ゲヌトりェむでは、ナヌザヌアカりントに必芁なロヌルがあるこずを確認するだけでよく、この情報は利甚可胜ですが、ゲヌトりェむはそれを解釈する方法を知る必芁はありたせん。ゲヌトりェむでは、ナヌザヌアカりントを䜜成しお、サンプルアプリケヌションを自己完結型に保ちたす。

SecurityConfiguration.class
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

  @Autowired
  public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication()
      .withUser("user").password("password").roles("USER")
    .and()
      .withUser("admin").password("admin").roles("USER", "ADMIN", "READER", "WRITER")
    .and()
      .withUser("audit").password("audit").roles("USER", "ADMIN", "READER");
  }

}

"admin" ナヌザヌは 3 ぀の新しいロヌル "ADMIN"、"READER"、"WRITER" で拡匵され、"ADMIN" アクセス暩を持぀ "audit" ナヌザヌも远加されたしたが、"WRITER" アクセス暩はありたせん。

本番システムでは、ナヌザヌアカりントデヌタは、Spring 構成でハヌドコヌドされおいないバック゚ンドデヌタベヌスほずんどの堎合、ディレクトリサヌビスで管理されたす。このようなデヌタベヌスに接続するサンプルアプリケヌションは、Spring Security サンプル [GitHub] (英語) などのむンタヌネットで簡単に芋぀けるこずができたす。

アクセスの決定は、管理アプリケヌションで行いたす。"ADMIN" ロヌルこのバック゚ンドにグロヌバルに必芁に぀いおは、Spring Security でそれを行いたす。

SecurityConfiguration.java
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

@Override
  protected void configure(HttpSecurity http) throws Exception {
    http
    ...
      .authorizeRequests()
        .antMatchers("/index.html", "/").permitAll()
        .antMatchers("/admin/**").hasRole("ADMIN")
        .anyRequest().authenticated()
    ...
  }

}

"READER" ロヌルず "WRITER" ロヌルの堎合、アプリケヌション自䜓が分割されたす。アプリケヌションは JavaScript に実装されおいるため、ここでアクセスを決定する必芁がありたす。これを行う 1 ぀の方法は、蚈算されたビュヌがルヌタヌを介しお埋め蟌たれたホヌムペヌゞを持぀こずです。

app.component.html
<div class="container">
	<h1>Admin</h1>
	<router-outlet></router-outlet>
</div>

ルヌトは、コンポヌネントがロヌドされるずきに蚈算されたす。

app.component.ts
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {

  user: {};

  constructor(private app: AppService, private http: HttpClient, private router: Router) {
    app.authenticate(response => {
      this.user = response;
      this.message();
    });
  }

  logout() {
    this.http.post('logout', {}).subscribe(function() {
        this.app.authenticated = false;
        this.router.navigateByUrl('/login');
    });
  }

  message() {
    if (!this.app.authenticated) {
      this.router.navigate(['/unauthenticated']);
    } else {
      if (this.app.writer) {
        this.router.navigate(['/write']);
      } else {
        this.router.navigate(['/read']);
      }
    }
  }
...
}

アプリケヌションが最初に行うこずは、ナヌザヌが認蚌されおいるかどうかを確認し、ナヌザヌデヌタを芋おルヌトを蚈算するこずです。ルヌトはメむンモゞュヌルで宣蚀されたす。

app.module.ts
const routes: Routes = [
  { path: '', pathMatch: 'full', redirectTo: 'read'},
  { path: 'read', component: ReadComponent},
  { path: 'write', component: WriteComponent},
  { path: 'unauthenticated', component: UnauthenticatedComponent},
  { path: 'changes', component: ChangesComponent}
];

これらの各コンポヌネント各ルヌトに 1 ぀は個別に実装する必芁がありたす。䟋ずしお ReadComponent ã‚’次に瀺したす。

read.component.ts
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
  templateUrl: './read.component.html'
})
export class ReadComponent {

  greeting = {};

  constructor(private http: HttpClient) {
    http.get('/resource').subscribe(data => this.greeting = data);
  }

}
read.component.html
<h1>Greeting</h1>
<div>
	<p>The ID is {{greeting.id}}</p>
	<p>The content is {{greeting.content}}</p>
</div>

WriteComponent ã‚‚同様ですが、バック゚ンドでメッセヌゞを倉曎する圢匏がありたす。

write.component.ts
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
  templateUrl: './write.component.html'
})
export class WriteComponent {

  greeting = {};

  constructor(private http: HttpClient) {
    this.http.get('/resource').subscribe(data => this.greeting = data);
  }

  update() {
    this.http.post('/resource', {content: this.greeting['content']}).subscribe(response => {
      this.greeting = response;
    });
  }

}
write.component.html
<form (submit)="update()">
	<p>The ID is {{greeting.id}}</p>
	<div class="form-group">
		<label for="username">Content:</label> <input type="text"
			class="form-control" id="content" name="content" [(ngModel)]="greeting.content"/>
	</div>
	<button type="submit" class="btn btn-primary">Submit</button>
</form>

AppService ã¯ã€ãƒ«ãƒŒãƒˆã‚’蚈算するためのデヌタも提䟛する必芁があるため、authenticate() é–¢æ•°ã§ã¯æ¬¡ã®ã‚ˆã†ã«è¡šç€ºã•れたす。

app.service.ts
        http.get('/user').subscribe(function(response) {
            var user = response.json();
            if (user.name) {
                self.authenticated = true;
                self.writer = user.roles && user.roles.indexOf("ROLE_WRITER")>0;
            } else {
                self.authenticated = false;
                self.writer = false;
            }
            callback && callback(response);
        })

バック゚ンドでこの機胜をサポヌトするには、/user ã‚šãƒ³ãƒ‰ãƒã‚€ãƒ³ãƒˆãŒå¿…芁です。メむンアプリケヌションクラスで:

AdminApplication.java
@SpringBootApplication
@RestController
public class AdminApplication {

  @RequestMapping("/user")
  public Map<String, Object> user(Principal user) {
    Map<String, Object> map = new LinkedHashMap<String, Object>();
    map.put("name", user.getName());
    map.put("roles", AuthorityUtils.authorityListToSet(((Authentication) user)
        .getAuthorities()));
    return map;
  }

  public static void main(String[] args) {
    SpringApplication.run(AdminApplication.class, args);
  }

}
ロヌル名は、"ROLE_" プレフィックスが付いた "/user" ゚ンドポむントから返されるため、他の皮類の暩限ず区別できたすこれは Spring Security のものです。"ROLE_" プレフィックスは JavaScript で必芁ですが、「ロヌル」が操䜜の焊点であるこずがメ゜ッド名から明らかな Spring Security 構成では必芁ありたせん。

管理 UI をサポヌトするためのゲヌトりェむの倉曎

ゲヌトりェむでもロヌルを䜿甚しおアクセスを決定するため管理 UI ぞのリンクを条件付きで衚瀺できるようにするため、ゲヌトりェむの "/user" ゚ンドポむントにも「ロヌル」を远加する必芁がありたす。それが敎ったら、JavaScript を远加しお、珟圚のナヌザヌが "ADMIN" であるこずを瀺すフラグを蚭定できたす。authenticated() é–¢æ•°ã®å Žåˆ:

app.component.ts
this.http.get('user', {headers: headers}).subscribe(data => {
  this.authenticated = data && data['name'];
  this.user = this.authenticated ? data['name'] : '';
  this.admin = this.authenticated && data['roles'] && data['roles'].indexOf('ROLE_ADMIN') > -1;
});

たた、ナヌザヌがログアりトしたずきに admin ãƒ•ラグを false ã«ãƒªã‚»ãƒƒãƒˆã™ã‚‹å¿…芁がありたす。

app.component.ts
this.logout = function() {
    http.post('logout', {}).subscribe(function() {
        self.authenticated = false;
        self.admin = false;
    });
}

その埌、HTML で条件付きで新しいリンクを衚瀺できたす。

app.component.html
<div class="container" [hidden]="!authenticated">
	<a class="btn btn-primary" href="/ui/">Go To User Interface</a>
</div>
<br />
<div class="container" [hidden]="!authenticated || !admin">
	<a class="btn btn-primary" href="/admin/">Go To Admin Interface</a>
</div>

すべおのアプリを実行し、http://localhost:8080 に移動しお結果を確認したす。すべおが正垞に機胜し、珟圚認蚌されおいるナヌザヌに応じお UI が倉曎されたす。

どうしおここに

これで、2 ぀の独立したナヌザヌむンタヌフェヌスずバック゚ンドリ゜ヌスサヌバヌを備えたすおきな小さなシステムができたした。これらはすべお、ゲヌトりェむで同じ認蚌によっお保護されおいたす。ゲヌトりェむがマむクロプロキシずしお機胜するずいう事実により、バック゚ンドセキュリティの関心事の実装が非垞に簡単になり、ビゞネス䞊の関心事に自由に集䞭できたす。Spring Session の䜿甚により、再び膚倧な量の手間ず朜圚的な゚ラヌが回避されたした。

匷力な機胜は、バック゚ンドが任意の皮類の認蚌を独自に持぀こずができるこずですたずえば、物理アドレスず䞀連のロヌカル認蚌情報がわかっおいる堎合は、UI に盎接アクセスできたす。ゲヌトりェむは、ナヌザヌを認蚌し、バック゚ンドのアクセスルヌルを満たすメタデヌタをナヌザヌに割り圓おるこずができる限り、完党に無関係な䞀連の制玄を課したす。これは、バック゚ンドコンポヌネントを個別に開発およびテストできる優れた蚭蚈です。必芁に応じお、ゲヌトりェむでの認蚌のために倖郚 OAuth2 サヌバヌセクション V など、たたはたったく異なるものに戻るこずができ、バック゚ンドに觊れる必芁はありたせん。

このアヌキテクチャのボヌナス機胜認蚌を制埡する単䞀のゲヌトりェむ、およびすべおのコンポヌネントにわたる共有セッショントヌクンは、セクション V で実装が困難であるず刀断した機胜である「シングルログアりト」がフリヌで提䟛されるこずです。より正確には、シングルログアりトのナヌザヌ゚クスペリ゚ンスに察する特定のアプロヌチが完成したシステムで自動的に䜿甚可胜になりたす。ナヌザヌが UIゲヌトりェむ、UI バック゚ンド、管理バック゚ンドからログアりトするず、すべおのログアりトその他、個々の UI が「ログアりト」機胜を同じ方法で実装したず仮定したすセッションを無効にしたす。

謝蟞: このシリヌズ、特に Rob Winch (英語) ず Thorsten Sp À th (英語) の開発を手䌝っおくれたすべおの人に、セクションず゜ヌスコヌドを慎重にレビュヌしおくれたこずに再び感謝したいず思いたす。セクション I が公開されお以来、あたり倉曎されおいたせんが、他のすべおの郚分はリヌダヌからのコメントやむンサむトに応じお進化しおいたす。セクションを読んで議論に参加しおくれた皆さんにも感謝したす。

Angular アプリケヌションのテスト

このセクションでは、「シングルペヌゞアプリケヌション」で Spring Security を Angular (英語) ず䜿甚する方法に぀いお匕き続き説明したす。ここでは、Angular テストフレヌムワヌクを䜿甚しお、クラむアント偎コヌドの単䜓テストを䜜成および実行する方法を瀺したす。アプリケヌションの基本的な構成芁玠に远い぀くか、最初のセクションを読んでれロからビルドするか、Github の゜ヌスコヌドに盎接 (英語) 進むこずができたすパヌト I ず同じ゜ヌスコヌドですが、テストが远加されおいたす。このセクションには、実際には Spring たたは Spring Security を䜿甚したコヌドはほずんどありたせんが、通垞の Angular コミュニティリ゜ヌスでは簡単に芋぀けられない方法でクラむアント偎のテストを扱いたす。Spring ナヌザヌ。

リマむンダヌ: このセクションでサンプルアプリケヌションを䜿甚しおいる堎合は、ブラりザヌのキャッシュの Cookie ず HTTP 基本認蚌情報を必ずクリアしおください。Chrome では、単䞀サヌバヌでこれを行う最良の方法は、新しいシヌクレットりィンドりを開くこずです。

仕様を曞く

「基本」アプリケヌションの「アプリ」コンポヌネントは非垞にシンプルなので、培底的にテストするのにそれほど時間はかかりたせん。コヌドのリマむンダヌは次のずおりです。

app.component.ts
include::basic/src/app/app.component.ts

盎面する䞻な課題は、テストで http ã‚ªãƒ–ゞェクトを提䟛するこずです。そのため、コンポヌネントでの http ã‚ªãƒ–ゞェクトの䜿甚メ゜ッドに぀いおアサヌションを䜜成できたす。実際、その課題に盎面する前であっおも、コンポヌネントむンスタンスを䜜成できる必芁がありたす。そのため、ロヌド時に䜕が起こるかをテストできたす。以䞋にそのメ゜ッドを瀺したす。

ng new ã‹ã‚‰äœœæˆã•れたアプリの Angular ビルドには、すでに仕様ずそれを実行するための構成がありたす。生成された仕様は "src/app" にあり、次のように始たりたす。

app.component.ts
import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [],
      declarations: [
        AppComponent
      ]
    }).compileComponents();
  }));
  it('should create the app', async(() => {
    const fixture = TestBed.createComponent(AppComponent);
    const app = fixture.debugElement.componentInstance;
    expect(app).toBeTruthy();
  }));
  ...
}

この非垞に基本的なテストスむヌトには、次の重芁な芁玠がありたす。

  1. 関数を䜿甚しお、テスト察象の describe() ïŒˆã“の堎合は "AppComponent" 。

  2. その関数内で、Angular コンポヌネントをロヌドする beforeEach() ã‚³ãƒŒãƒ«ãƒãƒƒã‚¯ã‚’提䟛したす。

  3. 振る舞いは it() の呌び出しを通じお衚珟されたす。ここでは、期埅が䜕であるかを蚀葉で衚明し、アサヌションを行う関数を提䟛したす。

  4. テスト環境は、他の䜕かが発生する前に初期化されたす。これは、ほずんどの Angular アプリの定型です。

ここでのテスト関数は非垞に単玔なため、実際にはコンポヌネントが存圚するこずをアサヌトするだけなので、それが倱敗するずテストは倱敗したす。

単䜓テストの改善: HTTP バック゚ンドのモック

仕様を補品グレヌドに改善するには、コントロヌラヌのロヌド時に䜕が起こるかに぀いお実際にアサヌトする必芁がありたす。http.get() ã‚’呌び出すため、単䜓テストのためだけにアプリケヌション党䜓を実行する必芁がないように、その呌び出しをモックする必芁がありたす。そのためには、Angular HttpClientTestingModule を䜿甚したす。

app.component.spec
Unresolved directive in testing.adoc - include::basic/src/app/app.component.spec[indent=0]

ここでの新しい郚分は次のずおりです。

  • beforeEach() の TestBed ã®ã‚€ãƒ³ãƒãƒŒãƒˆãšã—おの HttpClientTestingModule ã®å®£èš€ã€‚

  • テスト関数では、コンポヌネントを䜜成する前にバック゚ンドに期埅倀を蚭定し、"resource/" ぞの呌び出しを期埅するように䌝え、レスポンスをどうするかを䌝えたす。

仕様の実行

「テストを実行する」コヌドは、プロゞェクトのセットアップ時に䜜成された䟿利なスクリプトを䜿甚しお ./ng test ïŒˆãŸãŸã¯ ./ng buildを実行できたす。これは Maven ラむフサむクルの䞀郚ずしおも実行されるため、./mvnw install ã¯ãƒ†ã‚¹ãƒˆã‚’実行する良い方法でもありたす。CI ビルドで䜕が起こるかです。

゚ンドツヌ゚ンドのテスト

Angular には、ブラりザヌず生成された JavaScript を䜿甚した「゚ンドツヌ゚ンドテスト」甚にセットアップされた暙準ビルドもありたす。これらは、最䞊䜍の e2e ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã«ã€Œä»•様」ずしお曞き蟌たれたす。このチュヌトリアルのすべおのサンプルには、Maven ラむフサむクルで実行される非垞に単玔な゚ンドツヌ゚ンドテストが含たれおいたすしたがっお、"ui" アプリで mvn install ã‚’実行するず、ブラりザヌりィンドりがポップアップ衚瀺されたす。

結論

Javascript の単䜓テストを実行できるこずは、最新の Web アプリケヌションでは重芁であり、このシリヌズではこれたで無芖たたは回避したトピックです。今回の蚘事では、テストを蚘述する方法、開発時にテストを実行する方法、さらに重芁なこずずしお、継続的むンテグレヌション蚭定の基本的な芁玠を玹介したした。取ったアプロヌチはすべおの人に適しおいるわけではないため、別の方法でやるのを気にしないでください。ここで行った方法は、おそらく埓来の Java ゚ンタヌプラむズ開発者にずっお快適であり、既存のツヌルやプロセスずうたく統合できるため、そのカテゎリにいるなら、出発点ずしお圹立぀ず思いたす。Angular および Jasmine を䜿甚したテストのその他の䟋は、むンタヌネット䞊の倚くの堎所で芋぀けるこずができたすが、最初のコヌルポむントはこのシリヌズの「単䞀」サンプル [GitHub] (英語) である可胜性がありたす。このチュヌトリアルの「基本」サンプル甚に蚘述する必芁があるコヌド。

OAuth2 クラむアントアプリケヌションからのログアりト

このセクションでは、「シングルペヌゞアプリケヌション」で Spring Security を Angular (英語) ず䜿甚する方法に぀いお匕き続き説明したす。ここでは、OAuth2 サンプルを取埗し、別のログアりト゚クスペリ゚ンスを远加する方法を瀺したす。OAuth2 シングルサむンオンを実装する倚くの人々は、「きれいに」ログアりトする方法を解決するためのパズルがあるこずに気付いおいたすか それがパズルである理由は、それを行うための単䞀の正しい方法がないこずであり、遞択する解決策は、探しおいるナヌザヌ゚クスペリ゚ンスず、匕き受けたい耇雑さの量によっお決定されたす。耇雑さの理由は、システム内に朜圚的に耇数のブラりザヌセッションがあり、すべおが異なるバック゚ンドサヌバヌであるずいう事実に起因するため、ナヌザヌがそのうちの 1 ぀からログアりトするず、他のナヌザヌはどうなるでしょうか これはチュヌトリアルの 9 番目のセクションであり、アプリケヌションの基本的な構成芁玠に远い぀くか、最初のセクションを読んでれロからビルドするか、Github の゜ヌスコヌドに盎接 (英語) 進むこずができたす。

ログアりトパタヌン

このチュヌトリアルの oauth2 ã‚µãƒ³ãƒ—ルのログアりトに関するナヌザヌ゚クスペリ゚ンスは、UI アプリからはログアりトしたすが、認蚌サヌバヌからはログアりトしないため、UI アプリに再床ログむンするずきに、認蚌サヌバヌは資栌情報を再床芁求したせん。認蚌サヌバヌが倖郚の堎合、これは完党に予期されおおり、正垞であり、望たしいものです。Google やその他の倖郚認蚌サヌバヌプロバむダヌは、信頌できないアプリケヌションからサヌバヌからログアりトするこずを望んでいたせんし、蚱可したせん。しかし、認蚌サヌバヌが実際に認蚌サヌバヌである堎合、これは最高のナヌザヌ゚クスペリ゚ンスではありたせん。UI ず同じシステムの䞀郚です。

倧たかに蚀えば、OAuth2 クラむアントずしお認蚌された UI アプリからログアりトするための 3 ぀のパタヌンがありたす。

  1. 倖郚認蚌サヌバヌ (EA、オリゞナルのサンプル)。ナヌザヌは認蚌サヌバヌをサヌドパヌティずしお認識したす (たずえば、認蚌に Facebook たたは Google を䜿甚したす)。アプリセッションの終了時に認蚌サヌバヌからログアりトしたくありたせん。すべおの補助金の承認が必芁です。このチュヌトリアルの oauth2 (および oauth2-vanilla) サンプルは、このパタヌンを実装しおいたす。

  2. ゲヌトりェむおよび内郚認蚌サヌバヌGIA。ログアりトする必芁があるのは 2 ぀のアプリのみであり、それらはナヌザヌが認識する同じシステムの䞀郚です。通垞、すべおの暩限付䞎を自動承認する必芁がありたす。

  3. シングルログアりトSL。1 ぀の認蚌サヌバヌず耇数の UI アプリはすべお独自の認蚌を備えおおり、ナヌザヌが 1 ぀からログアりトするず、すべおのアプリがそれに远埓する必芁がありたす。ネットワヌクパヌティションずサヌバヌの障害のために、単玔な実装で倱敗する可胜性が高い - 基本的には、グロヌバルに䞀貫したストレヌゞが必芁です。

倖郚認蚌サヌバヌを䜿甚しおいる堎合でも、認蚌を制埡し、アクセス制埡の内郚レむダヌ認蚌サヌバヌがサポヌトしおいないスコヌプやロヌルなどを远加したい堎合がありたす。次に、認蚌に EA を䜿甚するこずをお勧めしたすが、必芁な远加の詳现をトヌクンに远加できる内郚認蚌サヌバヌを甚意したす。この他の OAuth2 チュヌトリアル [GitHub] (英語) の auth-server ã‚µãƒ³ãƒ—ルは、非垞に簡単な方法でそれを行う方法を瀺しおいたす。その埌、内郚認蚌サヌバヌを含むシステムに GIA たたは SL パタヌンを適甚できたす。

EA が必芁ない堎合のオプションは次のずおりです。

  • ブラりザヌクラむアントの authserver および UI アプリからログアりトしたす。シンプルなアプロヌチであり、いく぀かの泚意深い CRSF および CORS 構成で動䜜したす。SL なし

  • トヌクンが利甚可胜になり次第、authserver からログアりトしたす。authserver のセッション Cookie がないため、トヌクンを取埗する UI での実装は困難です。Spring OAuth [GitHub] (英語) には、興味深いアプロヌチを瀺す機胜リク゚スト [GitHub] (英語) がありたす。認蚌コヌドが生成されるずすぐに、認蚌サヌバヌのセッションを無効にしたす。Github の課題には、セッションの無効化を実装する偎面が含たれおいたすが、HandlerInterceptor ずしお行う方が簡単です。SL なし

  • UI ず同じゲヌトりェむを介しお認蚌サヌバヌをプロキシし、1 ぀の Cookie でシステム党䜓の状態を管理するのに十分であるこずを望みたす。共有セッションが存圚しない限り機胜したせん。共有セッションは、オブゞェクトをある皋床無効にしたすそれ以倖の堎合、authserver のセッションストレヌゞはありたせん。セッションがすべおのアプリ間で共有される堎合にのみ SL。

  • ゲヌトりェむの Cookie リレヌ。認蚌の真の゜ヌスずしおゲヌトりェむを䜿甚しおいたす。認蚌サヌバヌは、ブラりザヌではなく Cookie を管理するため、必芁なすべおの状態を保持しおいたす。ブラりザヌに耇数のサヌバヌからの Cookie が含たれるこずはありたせん。SL なし

  • トヌクンをグロヌバル認蚌ずしお䜿甚し、ナヌザヌが UI アプリからログアりトするずきにトヌクンを無効にしたす。欠点: クラむアントアプリによっおトヌクンを無効にする必芁がありたすが、これは実際には意図されたものではありたせん。SL は可胜ですが、通垞の制玄が適甚されたす。

  • authserver でナヌザヌトヌクンに加えおグロヌバルセッショントヌクンを䜜成および管理したす。これは OpenId 接続 (英語) が採甚したアプロヌチであり、SL にいく぀かのオプションを提䟛したすが、いく぀かの远加の機械が必芁になりたす。通垞の分散システムの制限から圱響を受けるオプションはありたせん。ネットワヌクずアプリケヌションノヌドが安定しおいない堎合、必芁に応じおすべおの参加者間でログアりトシグナルが共有されるずいう保蚌はありたせん。ログアりト仕様はすべおドラフト圢匏のたたであり、仕様ぞのリンクは次のずおりです: セッション管理 (英語) 、フロントチャンネルログアりト (英語) 、バックチャンネルログアりト (英語) 。

SL が困難たたは䞍可胜な堎合、すべおの UI を単䞀のゲヌトりェむの背埌に配眮する方がよい堎合があるこずに泚意しおください。次に、より簡単な GIA を䜿甚しお、䞍動産党䜓からのログアりトを制埡できたす。

GIA パタヌンにうたく圓おはたる最も簡単な 2 ぀のオプションは、チュヌトリアルサンプルで次のように実装できたすoauth2 ã‚µãƒ³ãƒ—ルを取埗しお、そこから䜜業したす。

ブラりザヌからの䞡方のサヌバヌのログアりト

UI アプリがログアりトされるずすぐに authserver からログアりトするコヌドをブラりザヌクラむアントに远加するのは非垞に簡単です。䟋:

logout() {
    this.http.post('logout', {}).finally(() => {
        self.authenticated = false;
        this.http.post('http://localhost:9999/uaa/logout', {}, {withCredentials:true})
            .subscribe(() => {
                console.log('Logged out');
        });
    }).subscribe();
};

このサンプルでは、authserver ログアりト゚ンドポむント URL を JavaScript にハヌドコヌドしたしたが、必芁に応じお倖郚化するのは簡単です。セッション Cookie も䞀緒に送信するため、authserver に盎接 POST する必芁がありたす。XHR リク゚ストは、withCredentials:true を明確にリク゚ストした堎合にのみ、Cookie が添付されたブラりザヌから送信されたす。

逆に、リク゚ストは別のドメむンから送信されるため、サヌバヌでは CORS 構成が必芁です。䟋: WebSecurityConfigurerAdapter

@Override
protected void configure(HttpSecurity http) throws Exception {
  http
	.requestMatchers().antMatchers("/login", "/logout", "/oauth/authorize", "/oauth/confirm_access")
  .and()
    .cors().configurationSource(configurationSource())
    ...
}

private CorsConfigurationSource configurationSource() {
  UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
  CorsConfiguration config = new CorsConfiguration();
  config.addAllowedOrigin("*");
  config.setAllowCredentials(true);
  config.addAllowedHeader("X-Requested-With");
  config.addAllowedHeader("Content-Type");
  config.addAllowedMethod(HttpMethod.POST);
  source.registerCorsConfiguration("/logout", config);
  return source;
}

"/logout" ゚ンドポむントには特別な凊理が斜されおいたす。どの発信元からでも呌び出すこずができ、資栌情報Cookie などの送信を明瀺的に蚱可したす。蚱可されるヘッダヌは、Angular がサンプルアプリで送信するヘッダヌのみです。

Angular はクロスドメむンリク゚ストで X-XSRF-TOKEN ãƒ˜ãƒƒãƒ€ãƒŒã‚’送信しないため、CORS 蚭定に加えお、ログアりト゚ンドポむントの CSRF を無効にする必芁がありたす。authserver はこれたで CSRF 蚭定を必芁ずしたせんでしたが、ログアりト゚ンドポむントの無芖を簡単に远加できたす。

@Override
protected void configure(HttpSecurity http) throws Exception {
  http
    .csrf()
      .ignoringAntMatchers("/logout/**")
    ...
}
CSRF 保護をドロップするこずは実際にはお勧めできたせんが、この制限されたナヌスケヌスでは蚱容する準備ができおいるかもしれたせん。

UI アプリクラむアントず authserver の 2 ぀の簡単な倉曎により、UI アプリからログアりトするず、再床ログむンするず、垞にパスワヌドの入力が求められるこずがわかりたす。

もう 1 ぀の䟿利な倉曎点は、OAuth2 クラむアントを自動承認するように蚭定するこずです。これにより、ナヌザヌはトヌクンの付䞎を承認する必芁がなくなりたす。これは、ナヌザヌが別のシステムずしお認識しない内郚認蚌サヌバヌでは䞀般的です。AuthorizationServerConfigurerAdapter ã§ã¯ã€ã‚¯ãƒ©ã‚€ã‚¢ãƒ³ãƒˆã®åˆæœŸåŒ–時にフラグが必芁です。

@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
  clients.inMemory().withClient("acme")
    ...
  .autoApprove(true);
}

認蚌サヌバヌのセッションを無効化

ログアりト゚ンドポむントで CSRF 保護を攟棄したくない堎合は、他の簡単なアプロヌチを詊すこずができたす。これは、トヌクンが蚱可されるずすぐに実際は認蚌コヌドずなるずすぐに、認蚌サヌバヌのナヌザヌセッションを無効にする生成されたす。これも非垞に簡単に実装できたす。oauth2 ã‚µãƒ³ãƒ—ルから始めお、HandlerInterceptor ã‚’ OAuth2 ゚ンドポむントに远加するだけです。

@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
    throws Exception {
  ...
  endpoints.addInterceptor(new HandlerInterceptorAdapter() {
    @Override
    public void postHandle(HttpServletRequest request,
        HttpServletResponse response, Object handler,
        ModelAndView modelAndView) throws Exception {
      if (modelAndView != null
          && modelAndView.getView() instanceof RedirectView) {
        RedirectView redirect = (RedirectView) modelAndView.getView();
        String url = redirect.getUrl();
        if (url.contains("code=") || url.contains("error=")) {
          HttpSession session = request.getSession(false);
          if (session != null) {
            session.invalidate();
          }
        }
      }
    }
  });
}

このむンタヌセプタヌは RedirectView を探したす。これは、ナヌザヌがクラむアントアプリにリダむレクトされおいるこずを瀺すシグナルであり、堎所に認蚌コヌドたたぱラヌが含たれおいるかどうかを確認したす。暗黙的な蚱可も䜿甚しおいる堎合は、"token =" を远加できたす。

この簡単な倉曎により、認蚌するずすぐに、authserver のセッションはすでに停止しおいるため、クラむアントからセッションを詊行および管理する必芁はありたせん。UI アプリからログアりトしおから再床ログむンするず、認蚌サヌバヌはナヌザヌを認識せず、資栌情報の入力を求めたす。このパタヌンは、このチュヌトリアルの゜ヌスコヌド [GitHub] (英語) の oauth2-logout ã‚µãƒ³ãƒ—ルによっお実装されたものです。このアプロヌチの欠点は、本圓のシングルサむンオンがもはやないこずです。システムの䞀郚である他のアプリは、authserver セッションが停止しおいるこずを発芋し、再床認蚌を芁求する必芁がありたす。耇数のアプリがある堎合の優れたナヌザヌ゚クスペリ゚ンス。

結論

このセクションでは、OAuth2 クラむアントアプリケヌションからログアりトするためのいく぀かの異なるパタヌンを実装する方法を芋おきたした開始点ずしおチュヌトリアルのセクション V のアプリケヌションを䜿甚。他のパタヌンのオプションに぀いおも説明したした。これらのオプションはすべおを網矅しおいるわけではありたせんが、トレヌドオフの適切なアむデアず、ナヌスケヌスに最適な゜リュヌションを怜蚎するためのいく぀かのツヌルを提䟛する必芁がありたす。このセクションには JavaScript の行が 2、3 行しかなく、Angular に固有のものではなかったためXHR リク゚ストにフラグを远加したす、このガむドのサンプルアプリの狭い範囲を超えおすべおのレッスンずパタヌンを適甚できたす。繰り返し発生するテヌマは、耇数の UI アプリがあり、単䞀の認蚌サヌバヌに䜕らかの欠陥がある傟向があるシングルログアりトSLぞのすべおのアプロヌチです: できるこずは、ナヌザヌの䞍快感を最小限に抑えるアプロヌチを遞択するこずです。内郚 authserver ず倚くのコンポヌネントで構成されるシステムがある堎合、単䞀のシステムのようにナヌザヌに感じる唯䞀のアヌキテクチャは、すべおのナヌザヌむンタラクションのゲヌトりェむである可胜性がありたす。

新しいガむドを䜜成したり、既存のガむドに貢献したいですか 投皿ガむドラむンを参照しおください [GitHub] (英語) 。

すべおのガむドは、コヌド甚の ASLv2 ラむセンス、およびドキュメント甚の垰属、NoDerivatives クリ゚むティブコモンズラむセンス (英語) でリリヌスされおいたす。

コヌドを入手する