日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Angular SPA基于Ocelot API网关与IdentityServer4的身份认证与授权(三)

發布時間:2023/12/4 编程问答 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Angular SPA基于Ocelot API网关与IdentityServer4的身份认证与授权(三) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在前面兩篇文章中,我介紹了基于IdentityServer4的一個Identity Service的實現,并且實現了一個Weather API和基于Ocelot的API網關,然后實現了通過Ocelot API網關整合Identity Service做身份認證的API請求。今天,我們進入前端開發,設計一個簡單的Angular SPA,并在Angular SPA上調用受Ocelot API網關和Identity Service保護的Weather API。

回顧

  • 《Angular SPA基于Ocelot API網關與IdentityServer4的身份認證與授權(一)》

  • 《Angular SPA基于Ocelot API網關與IdentityServer4的身份認證與授權(二)》

Angular SPA的實現

我們搭建一個Angular SPA的應用程序,第一步先實現一些基礎功能,比如頁面布局和客戶端路由;第二步先將Ocelot API網關中設置的身份認證功能關閉,并設計一個Component,在Component中調用未受保護的Weather API,此時可以毫無阻攔地在Angular SPA中調用Weather API并將結果顯示在頁面上;第三步,我們在Ocelot API網關上開啟身份認證,然后修改Angular SPA,使其提供登錄按鈕以實現用戶登錄與身份認證,進而訪問受保護的Weather API。在進行接下來的實操演練之前,請確保已經安裝Angular 8 CLI。

基礎功能的實現

在文件系統中,使用ng new命令,新建一個Angular 8的單頁面應用,為了有比較好的界面布局,我使用了Bootstrap。方法很簡單,在項目目錄下,執行npm install –save bootstrap,然后,打開angular.json文件,將bootstrap的js和css添加到配置中:


"styles": [

????"src/styles.css",

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

],

"scripts": [

????"node_modules/bootstrap/dist/js/bootstrap.min.js"

]

然后,修改app.component.html,使用下面代碼覆蓋:


<nav class="navbar navbar-expand-md navbar-dark bg-dark">

??<a class="navbar-brand" href="#">Identity Demo</a>

??<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">

????<span class="navbar-toggler-icon"></span>

??</button>

?

??<div class="collapse navbar-collapse" id="navbarSupportedContent">

????<ul class="navbar-nav mr-auto">

??????<li class="nav-item active">

????????<a class="nav-link" href="#">首頁 <span class="sr-only">(current)</span></a>

??????</li>

??????<li class="nav-item">

????????<a class="nav-link" href="#">API</a>

??????</li>

??????<li class="nav-item">

????????<a class="nav-link" href="#">關于</a>

??????</li>

???????

????</ul>

????<form class="form-inline my-2 my-md-0">

??????<ul class="navbar-nav mr-auto">

????????<a class="nav-link" href="javascript:void(0)">登錄</a>

??????</ul>

????</form>

??</div>

</nav>

ng serve跑起來,得到一個具有標題欄的空頁面:

接下來,使用ng g c命令創建3個component,分別是HomeComponent,ApiComponent和AboutComponent,并且修改app.modules.ts文件,將這三個components加入到router中:


import { BrowserModule } from '@angular/platform-browser';

import { NgModule } from '@angular/core';

import { Routes, RouterModule } from '@angular/router';

?

import { AppComponent } from './app.component';

import { HomeComponent } from './home/home.component';

import { ApiComponent } from './api/api.component';

import { AboutComponent } from './about/about.component';

?

const appRoutes: Routes = [

??{ path: 'about', component: AboutComponent },

??{ path: 'home', component: HomeComponent },

??{ path: 'api', component: ApiComponent },

??{ path: '**', component: HomeComponent }

];

?

@NgModule({

??declarations: [

????AppComponent,

????HomeComponent,

????ApiComponent,

????AboutComponent

??],

??imports: [

????BrowserModule,

????RouterModule.forRoot(

??????appRoutes,

??????{ enableTracing: false }

????)

??],

??providers: [],

??bootstrap: [AppComponent]

})

export class AppModule { }

然后,在app.component.html中,加入:

1

<router-outlet></router-outlet>

再次運行站點,可以看到,我們已經可以通過菜單來切換component了:

在Angular頁面中調用API顯示結果

Angular調用API的方法我就不詳細介紹了,Angular的官方文檔有很詳細的內容可以參考。在這個演練中,我們需要注意的是,首先將上篇文章中對于Weather API的認證功能關閉,以便測試API的調用是否成功。關閉認證功能其實很簡單,只需要將Ocelot API網關中有關Ocelot的配置的相關節點注釋掉就行了:


{

??"ReRoutes": [

????{

??????"DownstreamPathTemplate": "/weatherforecast",

??????"DownstreamScheme": "http",

??????"DownstreamHostAndPorts": [

????????{

??????????"Host": "localhost",

??????????"Port": 5000

????????}

??????],

??????"UpstreamPathTemplate": "/api/weather",

??????"UpstreamHttpMethod": [ "Get" ],

??????//"AuthenticationOptions": {

??????//? "AuthenticationProviderKey": "AuthKey",

??????//? "AllowedScopes": []

??????//}

????}

??]

}

接下來修改Angular單頁面應用,在app.module.ts中加入HttpClientModule:


imports: [

????BrowserModule,

????HttpClientModule,

????RouterModule.forRoot(

??????appRoutes,

??????{ enableTracing: false }

????)

??],

然后實現一個調用Weather API的Service(服務):


import { Injectable } from '@angular/core';

import { HttpClient } from '@angular/common/http';

import { WeatherData } from '../models/weather-data';

import { Observable } from 'rxjs';

?

@Injectable({

??providedIn: 'root'

})

export class WeatherService {

?

??constructor(private httpClient: HttpClient) { }

?

??getWeather(): Observable<WeatherData[]> {

????return this.httpClient.get<WeatherData[]>('http://localhost:9000/api/weather');

??}

}

在這個Service實現中,沒有加入異常處理部分,因為作為一個研究性質的項目,沒有必要進行異常處理,到瀏覽器的調試窗口查看錯誤信息就行。上面的代碼引用了一個類型,就是WeatherData,它其實非常簡單,對應著Weather API所返回的數據模型:


export class WeatherData {

????constructor(public temperatureF: number,

????????public temperatureC: number,

????????private summary: string,

????????private date: string) { }

}

現在,修改api.component.ts,通過調用這個WeatherService來獲取Weather API的數據:

1

import { Component, OnInit } from '@angular/core';

import { WeatherService } from '../services/weather.service';

import { WeatherData } from '../models/weather-data';

?

@Component({

??selector: 'app-api',

??templateUrl: './api.component.html',

??styleUrls: ['./api.component.css']

})

export class ApiComponent implements OnInit {

?

??data: WeatherData[];

?

??constructor(private api: WeatherService) { }

?

??ngOnInit() {

????this.api.getWeather()

??????.subscribe(ret => this.data = ret);

??}

}

并顯示在前端:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

<div class="container" *ngIf="data">

????<table class="table table-striped">

????????<thead>

??????????<tr>

????????????<th scope="col">Summary</th>

????????????<th scope="col">TempF</th>

????????????<th scope="col">TempC</th>

????????????<th scope="col">Date</th>

??????????</tr>

????????</thead>

????????<tbody>

??????????<tr *ngFor="let d of data">

????????????<td>{{d.summary}}</td>

????????????<td>{{d.temperatureF}}</td>

????????????<td>{{d.temperatureC}}</td>

????????????<td>{{d.date}}</td>

??????????</tr>

????????</tbody>

??????</table>

</div>

完成之后,啟動Weather API和Ocelot API網關,然后運行Angular單頁面應用,我們已經可以在API這個頁面顯示調用結果了:

開啟身份認證

在Ocelot API網關的配置中,打開被注釋掉的部分,重新啟用身份認證功能,再次刷新Angular頁面,發現頁面已經打不開了,在開發者工具的Console中輸出了錯誤信息:401 (Unauthorized),表示身份認證部分已經起作用了。

下面我們來解決這個問題。既然是需要身份認證才能訪問Weather API,那么我們就在Angular頁面上實現登錄功能。首先在Angular單頁面應用中安裝oidc-client,oidc-client是一款為Javascript應用程序提供OpenID Connect和OAuth2協議支持的框架,在Angular中使用也非常的方便。用npm install來安裝這個庫:

1

npm install oidc-client

然后,實現一個用于身份認證的Service:


import { Injectable } from '@angular/core';

import { BehaviorSubject } from 'rxjs';

import { UserManager, UserManagerSettings, User } from 'oidc-client';

?

@Injectable({

??providedIn: 'root'

})

export class AuthService {

?

??private authStatusSource = new BehaviorSubject<boolean>(false);

??private userNameStatusSource = new BehaviorSubject<string>('');

??private userManager = new UserManager(this.getUserManagerSettings());

??private user: User | null;

?

??authStatus$ = this.authStatusSource.asObservable();

??userNameStatus$ = this.userNameStatusSource.asObservable();

?

??constructor() {

????this.userManager.getUser().then(user => {

??????this.user = user;

??????this.authStatusSource.next(this.isAuthenticated());

??????this.userNameStatusSource.next(this.user.profile.name);

????});

??}

?

??async login() {

????await this.userManager.signinRedirect();

??}

?

??async logout() {

????await this.userManager.signoutRedirect();

??}

?

??async completeAuthentication() {

????this.user = await this.userManager.signinRedirectCallback();

????this.authStatusSource.next(this.isAuthenticated());

????this.userNameStatusSource.next(this.user.profile.name);

??}

?

??isAuthenticated(): boolean {

????return this.user != null && !this.user.expired;

??}

?

??get authorizationHeaderValue(): string {

????return `${this.user.token_type} ${this.user.access_token}`;

??}

?

??private getUserManagerSettings(): UserManagerSettings {

????return {

??????authority: 'http://localhost:7889',

??????client_id: 'angular',

??????redirect_uri: 'http://localhost:4200/auth-callback',

??????post_logout_redirect_uri: 'http://localhost:4200/',

??????response_type: 'id_token token',

??????scope: 'openid profile email api.weather.full_access',

??????filterProtocolClaims: true,

??????loadUserInfo: true,

??????automaticSilentRenew: true,

??????silent_redirect_uri: 'http://localhost:4200/silent-refresh.html'

????};

??}

}

AuthService為Angular應用程序提供了用戶身份認證的基本功能,比如登錄、注銷,以及判斷是否經過身份認證(isAuthenticated)等。需要注意的是getUserManagerSettings方法,它為oidc-client提供了基本的參數配置,其中的authority為Identity Service的URL;redirect_uri為認證完成后,Identity Service需要返回到哪個頁面上;post_logout_redirect_uri表示用戶注銷以后,需要返回到哪個頁面上;client_id和scope為Identity Service中為Angular應用所配置的Client的ClientId和Scope(參考Identity Service中的Config.cs文件)。

接下來,修改app.component.html,將原來的“登錄”按鈕改為:


<form class="form-inline my-2 my-md-0">

????<ul class="navbar-nav mr-auto">

????<a *ngIf="!isAuthenticated" class="nav-link" href="javascript:void(0)" (click)="onLogin()">登錄</a>

????<li *ngIf="isAuthenticated" class="nav-item dropdown">

??????<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown"

????????aria-haspopup="true" aria-expanded="false">

????????{{userName}}

??????</a>

??????<div class="dropdown-menu" aria-labelledby="navbarDropdown">

????????<a class="dropdown-item" href="javascript:void(0)" (click)="onLogOut()">注銷</a>

??????</div>

????</li>

????</ul>

</form>

然后,修改app.component.ts,完成登錄和注銷部分的代碼:


import { Component, OnInit, OnDestroy } from '@angular/core';

import { AuthService } from './services/auth.service';

import { Subscription } from 'rxjs';

?

@Component({

??selector: 'app-root',

??templateUrl: './app.component.html',

??styleUrls: ['./app.component.css']

})

export class AppComponent implements OnInit, OnDestroy {

??title = 'identity-demo-spa';

?

??isAuthenticated: boolean;

??authStatusSubscription: Subscription;

??userNameSubscription: Subscription;

??userName: string;

???

??constructor(private authService: AuthService) { }

?

??ngOnDestroy(): void {

????this.authStatusSubscription.unsubscribe();

????this.userNameSubscription.unsubscribe();

??}

?

??ngOnInit(): void {

????this.authStatusSubscription = this.authService.authStatus$.subscribe(status => this.isAuthenticated = status);

????this.userNameSubscription = this.authService.userNameStatus$.subscribe(status => this.userName = status);

??}

?

??async onLogin() {

????await this.authService.login();

??}

?

??async onLogOut() {

????await this.authService.logout();

??}

}

我們還需要增加一個新的component:AuthCallbackComponent,用來接收登錄成功之后的回調,它會通知AuthService以更新登錄狀態和用戶信息:


import { Component, OnInit } from '@angular/core';

import { AuthService } from '../services/auth.service';

import { Router, ActivatedRoute } from '@angular/router';

?

@Component({

??selector: 'app-auth-callback',

??templateUrl: './auth-callback.component.html',

??styleUrls: ['./auth-callback.component.css']

})

export class AuthCallbackComponent implements OnInit {

?

??constructor(private authService: AuthService, private router: Router, private route: ActivatedRoute) { }

?

??async ngOnInit() {

????await this.authService.completeAuthentication();

????this.router.navigate(['/home']);

??}

?

}

最后將AuthCallbackComponent添加到Route中:


const appRoutes: Routes = [

??{ path: 'about', component: AboutComponent },

??{ path: 'home', component: HomeComponent },

??{ path: 'api', component: ApiComponent },

??{ path: 'auth-callback', component: AuthCallbackComponent },

??{ path: '**', component: HomeComponent }

];

重新運行Angular應用,你會看到以下效果:

現在我們就可以在Angular的頁面中完成用戶登錄和注銷了。如你所見:

  • 登錄界面來自Identity Service,本身也是由IdentityServer4提供的界面,開發者可以自己修改Identity Service來定制界面

  • 登錄成功后,原本的“登錄”按鈕變成了顯示用戶名稱的下拉菜單,選擇菜單就可以點擊“注銷”按鈕退出登錄

  • 此時訪問API頁面,仍然無法正確調用Weather API,因為我們還沒有將Access Token傳入API調用

  • 登錄狀態下的API調用

    接下來,我們將Access Token傳入,使得Angular應用可以使用登錄用戶獲取的Access Token正確調用Weather API。修改AuthService如下:


    export class WeatherService {

    ?

    ??constructor(private httpClient: HttpClient, private authService: AuthService) { }

    ?

    ??getWeather(): Observable<WeatherData[]> {

    ????const authHeaderValue = this.authService.authorizationHeaderValue;

    ????const httpOptions = {

    ??????headers: new HttpHeaders({

    ????????'Content-Type': 'application/json',

    ????????Authorization: authHeaderValue

    ??????})

    ????};

    ?

    ????return this.httpClient.get<WeatherData[]>('http://localhost:9000/api/weather', httpOptions);

    ??}

    }

    再次運行Angular應用,可以看到,已經可以在登錄的狀態下成功調用Weather API。你也可以試試,在退出登錄的狀態下,是否還能正確調用API。

    小結

    本文詳細介紹了Angular單頁面應用作為Ocelot API網關的客戶端,通過Identity Service進行身份認證和API調用的整個過程。當然,很多細節部分沒有做到那么完美,本身也是為了能夠演示開發過程中遇到的問題。從下一講開始,我會開始介紹基于Ocelot API網關的授權問題。

    源代碼

    訪問以下Github地址以獲取源代碼:

    https://github.com/daxnet/identity-demo

    創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

    總結

    以上是生活随笔為你收集整理的Angular SPA基于Ocelot API网关与IdentityServer4的身份认证与授权(三)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。