Spring Cloud –添加Angular 4

1.概述

在上一篇Spring Cloud文章中,我們將Zipkin支持添加到了應用程序中。在本文中,我們將向堆棧中添加一個前端應用程序。

到目前為止,我們一直在後端上完全構建我們的雲應用程序。但是,如果沒有UI,Web應用程序有什麼好處?在本文中,我們將通過將單個頁面應用程序集成到我們的項目中來解決該問題。

我們將使用AngularBootstrap編寫此應用程序。 Angular 4代碼的風格非常像編寫Spring應用程序,這對於Spring開發人員來說是自然的選擇!儘管前端代碼將使用Angular,但本文的內容可以輕鬆地擴展到任何前端框架,而無需花費太多精力。

在本文中,我們將構建一個Angular 4應用並將其連接到我們的雲服務。我們將演示如何在SPA和Spring Security之間集成登錄。我們還將展示如何使用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();

 }

首先,我們添加一個默認的成功URL指向/home/index.html因為這將是Angular應用程序所在的位置。接下來,我們配置螞蟻匹配器以允許通過網關的任何請求,但Eureka資源除外。這會將所有安全檢查委派給後端服務。

接下來,我們刪除了註銷成功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. Angular CLI和Starter項目

在開始新的Angular項目之前,請確保安裝最新版本的Node.js和npm

3.1。安裝Angular CLI

首先,我們需要使用npm下載並安裝Angular命令行界面。打開一個終端並運行:

npm install -g @angular/cli

這將在全球範圍內下載並安裝CLI。

3.2。安裝新項目

仍然在終端中時,導航到網關項目並進入gateway / src / main文件夾。創建一個名為“ angular”的目錄並導航至該目錄。從這裡運行:

ng new ui

耐心一點; CLI會建立一個全新的項目,並使用npm下載所有JavaScript依賴項。花費幾分鐘的時間並不少見。

ng命令是Angular CLI的快捷方式, new參數指示CLI創建一個新項目,而ui命令為我們的項目命名。

3.3。運行項目

new命令完成後。導航到已創建並運行的ui文件夾:

ng serve

項目建立後,導航至http:// localhost:4200。我們應該在瀏覽器中看到:

Spring

恭喜你!我們剛剛構建了一個Angular應用程序!

3.4。安裝引導程序

讓我們使用npm安裝引導程序。在ui目錄中運行以下命令:

npm install [email protected] --save

這會將引導程序下載到node_modules文件夾中。

ui目錄中,打開.angular-cli.json文件。這是配置有關我們項目的某些屬性的文件。找到apps > styles屬性,並添加我們的Bootstrap CSS類的文件位置:

"styles": [

 "styles.css",

 "../node_modules/bootstrap/dist/css/bootstrap.min.css"

 ],

這將指示Angular將Bootstrap包含在由項目構建的已編譯CSS文件中。

3.5。設置構建輸出目錄

接下來,我們需要告訴Angular構建文件的放置位置,以便我們的Spring Boot應用程序可以為它們提供服務。 Spring Boot可以從resources文件夾中的兩個位置提供文件:

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

由於我們已經在使用靜態文件夾為Eureka提供一些資源,並且每次運行構建時Angular都會刪除此文件夾,因此讓我們將Angular應用構建到公用文件夾中。

再次打開.angular-cli.json文件,然後找到apps > outDir屬性。更新該string:

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

如果Angular項目位於src / main / angular / ui中,則它將構建到src / main / resources / public文件夾中。如果應用程序在另一個文件夾中,則需要修改此字符串以正確設置位置。

3.6。使用Maven自動化構建

最後,我們將建立一個自動構建,以便在編譯代碼時運行。每當運行“ mvn compile”時,此ant任務就會運行Angular CLI構建任務。將這一步添加到網關的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>

我們應該注意,此設置確實要求Angular CLI在類路徑上可用。將此腳本推送到不具有該依賴項的環境將導致構建失敗。

現在開始構建Angular應用程序!

4. Angular

在本教程的這一部分中,我們將在頁面中構建身份驗證機制。我們使用基本身份驗證並遵循簡單的流程使其工作。

用戶具有一個登錄表單,可以在其中輸入其用戶名和密碼。

接下來,我們使用他們的憑據來創建base64身份驗證令牌並請求“/me”端點。端點返回一個Principal對象,其中包含該用戶的角色。

最後,我們將憑據和主體存儲在客戶端上,以在後續請求中使用。

讓我們看看這是怎麼做的!

4.1。模板

在網關項目中,導航到src/main/angular/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)”並將表單傳遞給該函數。在jumbotron 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在依賴項注入系統中註冊。打開app.module.ts文件並找到provider屬性。將HttpService添加到該數組。結果應如下所示:

providers: [HttpService],

4.4。添加Principal

接下來,讓我們在Typescript代碼中添加Principal 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類。這是兩個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。構建和查看

現在從網關文件夾運行“ mvn compile ”。這將編譯我們的java源並將Angular應用構建到公用文件夾。讓我們啟動其他雲應用程序: configdiscoveryzipkin 。然後運行網關項目。服務啟動後,導航至http://localhost:8080以查看我們的應用程序。我們應該看到這樣的東西:

Spring

接下來,讓我們跟隨登錄頁面的鏈接:

Spring

使用用戶/密碼憑據登錄。單擊“登錄”,我們應該重定向到加載我們的單頁應用程序的/home/index.html。

Spring

看來我們的jumbotron指示我們已經以用戶身份登錄!現在,通過單擊右上角的鏈接註銷,這次使用admin/admin憑據登錄。

Spring

看起來不錯!現在,我們以管理員身份登錄。

5.結論

在本文中,我們看到了將單個頁面應用程序集成到我們的雲系統中是多麼容易。我們採用了現代化的框架,並將有效的安全配置集成到了我們的應用程序中。

使用這些示例,嘗試編寫一些代碼來調用book-servicerating-service 。由於我們現在有進行HTTP調用並將數據連接到模板的示例,因此這應該相對容易一些。

如果您想了解網站其餘部分的構建方式,可以在Github上找到源代碼。