Spring Cloud –添加Angular 4
1.概述
在上一篇Spring Cloud文章中,我們將Zipkin支持添加到了應用程序中。在本文中,我們將向堆棧中添加一個前端應用程序。
到目前為止,我們一直在後端上完全構建我們的雲應用程序。但是,如果沒有UI,Web應用程序有什麼好處?在本文中,我們將通過將單個頁面應用程序集成到我們的項目中來解決該問題。
我們將使用Angular
和Bootstrap
編寫此應用程序。 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。我們應該在瀏覽器中看到:
恭喜你!我們剛剛構建了一個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應用構建到公用文件夾。讓我們啟動其他雲應用程序: config
, discovery
和zipkin
。然後運行網關項目。服務啟動後,導航至http://localhost:8080
以查看我們的應用程序。我們應該看到這樣的東西:
接下來,讓我們跟隨登錄頁面的鏈接:
使用用戶/密碼憑據登錄。單擊“登錄”,我們應該重定向到加載我們的單頁應用程序的/home/index.html。
看來我們的jumbotron
指示我們已經以用戶身份登錄!現在,通過單擊右上角的鏈接註銷,這次使用admin/admin
憑據登錄。
看起來不錯!現在,我們以管理員身份登錄。
5.結論
在本文中,我們看到了將單個頁面應用程序集成到我們的雲系統中是多麼容易。我們採用了現代化的框架,並將有效的安全配置集成到了我們的應用程序中。
使用這些示例,嘗試編寫一些代碼來調用book-service
或rating-service
。由於我們現在有進行HTTP調用並將數據連接到模板的示例,因此這應該相對容易一些。
如果您想了解網站其餘部分的構建方式,可以在Github上找到源代碼。