angular2创建应用

tech2022-09-04  122

angular2创建应用

This article was originally published on OKTA Developer Blog. Thank you for supporting the partners who make SitePoint possible.

本文最初发布在OKTA开发人员博客上 。 感谢您支持使SitePoint成为可能的合作伙伴。

Angular (formerly called Angular 2.0) is quickly becoming one of the most powerful ways to build a modern single-page app. A core strength is Angular’s focus on building reusable components, which help you decouple the various concerns in your application. Take authentication, for example: it can be painful to build, but once you wrap it in a component, the authentication logic can be reused throughout your application.

Angular(以前称为Angular 2.0)正Swift成为构建现代单页应用程序的最强大方法之一。 Angular的核心优势在于致力于构建可重用组件,这有助于您消除应用程序中的各种问题。 以身份验证为例:构建可能很麻烦,但是一旦将其包装在组件中,身份验证逻辑就可以在整个应用程序中重用。

The Angular CLI makes it easy to scaffold new components, and even entire projects. If you haven’t used the Angular CLI to quickly generate Angular code, you’re in for a treat!

Angular CLI使安装新组件甚至整个项目变得容易。 如果您还没有使用Angular CLI来快速生成Angular代码,那么您将大有收获!

In this example, you’ll build a simple web application with Angular CLI, a tool for Angular development. You’ll create an application with search and edit features, then add authentication.

在此示例中,您将使用Angular CLI(一个用于Angular开发的工具)构建一个简单的Web应用程序。 您将创建一个具有搜索和编辑功能的应用程序,然后添加身份验证。

创建一个Angular应用程序 (Create an Angular Application)

TIP: If you’d like to skip building the Angular application and get right to adding authentication, you can clone my ng-demo project, then skip to the Create an OpenID Connect App in Okta section.

提示:如果您想跳过构建Angular应用程序并直接添加身份验证,可以克隆我的ng-demo项目,然后跳到“ 在Okta中创建OpenID Connect应用程序”部分。

git clone https://github.com/mraible/ng-demo.git

您需要什么 (What You’ll Need)

About 20 minutes

约20分钟

A favorite text editor or IDE. I recommend IntelliJ IDEA

最喜欢的文本编辑器或IDE。 我推荐IntelliJ IDEA

Node.js and npm installed. I recommend using nvm

已安装Node.js和npm。 我建议使用nvm

Angular CLI installed. If you don’t have Angular CLI installed, install it using npm install -g @angular/cli

安装了Angular CLI 。 如果您没有安装Angular CLI,请使用npm install -g @angular/cli

Create a new project using the ng new command:

使用ng new命令创建一个新项目:

ng new ng-demo

This will create a ng-demo project and run npm install in it. It should take about a minute to complete, but that could vary depending on your connection speed.

这将创建一个ng-demo项目并在其中运行npm install 。 大约需要一分钟才能完成,但是具体取决于您的连接速度。

[mraible:~/dev] $ ng new ng-demo installing ng create .editorconfig create README.md create src/app/app.component.css create src/app/app.component.html create src/app/app.component.spec.ts create src/app/app.component.ts create src/app/app.module.ts create src/assets/.gitkeep create src/environments/environment.prod.ts create src/environments/environment.ts create src/favicon.ico create src/index.html create src/main.ts create src/polyfills.ts create src/styles.css create src/test.ts create src/tsconfig.app.json create src/tsconfig.spec.json create src/typings.d.ts create .angular-cli.json create e2e/app.e2e-spec.ts create e2e/app.po.ts create e2e/tsconfig.e2e.json create .gitignore create karma.conf.js create package.json create protractor.conf.js create tsconfig.json create tslint.json Successfully initialized git. Installing packages for tooling via npm. Installed packages for tooling via npm. You can `ng set --global packageManager=yarn`. Project 'ng-demo' successfully created. [mraible:~] 46s $

You can see the what version of Angular CLI you’re using with ng --version.

您可以通过ng --version查看正在使用的Angular CLI ng --version 。

$ ng --version _ _ ____ _ ___ / \ _ __ __ _ _ _| | __ _ _ __ / ___| | |_ _| / △ \ | '_ \ / _` | | | | |/ _` | '__| | | | | | | / ___ \| | | | (_| | |_| | | (_| | | | |___| |___ | | /_/ \_\_| |_|\__, |\__,_|_|\__,_|_| \____|_____|___| |___/ @angular/cli: 1.3.2 node: 8.4.0 os: darwin x64

运行您的Angular应用程序 (Run Your Angular Application)

The project is configured with webpack dev server. To start it, make sure you’re in the ng-demo directory, then run:

该项目使用webpack dev server配置。 要启动它,请确保您位于ng-demo目录中,然后运行:

ng serve

You should see a screen like the one below at http://localhost:4200.

您应该在http:// localhost:4200看到类似以下的屏幕。

You can make sure your new project’s tests pass, run ng test:

您可以确保新项目的测试通过,运行ng test :

$ ng test ... Chrome 60.0.3112 (Mac OS X 10.12.6): Executed 3 of 3 SUCCESS (0.239 secs / 0.213 secs)

添加搜索功能 (Add a Search Feature)

To add a search feature, open the project in an IDE or your favorite text editor. For IntelliJ IDEA, use File > New Project > Static Web and point to the ng-demo directory.

要添加搜索功能,请在IDE或您喜欢的文本编辑器中打开项目。 对于IntelliJ IDEA,使用文件>新建项目>静态Web并指向ng-demo目录。

In a terminal window, cd into your project’s directory and run the following command. This will create a search component.

在终端窗口中,进入项目目录,然后运行以下命令。 这将创建一个搜索组件。

$ ng g component search installing component create src/app/search/search.component.css create src/app/search/search.component.html create src/app/search/search.component.spec.ts create src/app/search/search.component.ts update src/app/app.module.ts

Open src/app/search/search.component.html and replace its default HTML with the following:

打开src/app/search/search.component.html并将其默认HTML替换为以下内容:

<h2>Search</h2> <form> <input type="search" name="query" [(ngModel)]="query" (keyup.enter)="search()"> <button type="button" (click)="search()">Search</button> </form> <pre>{{searchResults | json}}</pre>

The Router documentation for Angular provides the information you need to setup a route to the SearchComponent you just generated. Here’s a quick summary:

Angular的路由器文档提供了设置到刚刚生成的SearchComponent的路由所需的信息。 快速摘要:

In src/app/app.module.ts, add an appRoutes constant and import it in @NgModule:

在src/app/app.module.ts ,添加一个appRoutes常量并将其导入@NgModule :

import { Routes, RouterModule } from '@angular/router'; const appRoutes: Routes = [ {path: 'search', component: SearchComponent}, {path: '', redirectTo: '/search', pathMatch: 'full'} ]; @NgModule({ ... imports: [ ... RouterModule.forRoot(appRoutes) ] ... }) export class AppModule { }

In src/app/app.component.html, adjust the placeholder content and add a <router-outlet> tag to display routes.

在src/app/app.component.html ,调整占位符内容并添加<router-outlet>标记以显示路由。

<h1>Welcome to {{title}}!</h1> <!-- Routed views go here --> <router-outlet></router-outlet>

Now that you have routing setup, you can continue writing the search feature.

有了路由设置后,就可以继续编写搜索功能了。

If you still have ng serve running, your browser should refresh automatically. If not, navigate to http://localhost:4200. You will likely see a blank screen. Open your JavaScript console and you’ll see the problem.

如果您仍在运行ng serve ,则浏览器应会自动刷新。 如果不是,请导航至http:// localhost:4200。 您可能会看到黑屏。 打开您JavaScript控制台,您将看到问题。

To solve this, open src/app/app.module.ts and add FormsModule as an import in @NgModule:

要解决此问题,请打开src/app/app.module.ts并将FormsModule作为导入添加到@NgModule :

import { FormsModule } from '@angular/forms'; @NgModule({ ... imports: [ ... FormsModule ] ... }) export class AppModule { }

Now you should see the search form.

现在,您应该看到搜索表单。

If you want to add CSS for this components, open src/app/search/search.component.css and add some CSS. For example:

如果要为此组件添加CSS,请打开src/app/search/search.component.css并添加一些CSS。 例如:

:host { display: block; padding: 0 20px; }

This section has shown you how to generate a new component to a basic Angular application with Angular CLI. The next section will show you how to create and use a JSON file and localStorage to create a fake API.

本节介绍了如何使用Angular CLI为基本Angular应用程序生成新组件。 下一节将向您展示如何创建和使用JSON文件和localStorage创建伪造的API。

To get search results, create a SearchService that makes HTTP requests to a JSON file. Start by generating a new service.

要获取搜索结果,请创建一个SearchService ,该服务向JSON文件发出HTTP请求。 首先生成新服务。

$ ng g service search installing service create src/app/search.service.spec.ts create src/app/search.service.ts WARNING Service is generated but not provided, it must be provided to be used

Move the generated search.service.ts and its test to app/shared/search. You’ll need to create this directory.

将生成的search.service.ts及其测试移至app/shared/search 。 您需要创建此目录。

mkdir -p src/app/shared/search mv src/app/search.service.* src/app/shared/search/.

Create src/assets/data/people.json to hold your data.

创建src/assets/data/people.json来保存数据。

[ { "id": 1, "name": "Peyton Manning", "phone": "(303) 567-8910", "address": { "street": "1234 Main Street", "city": "Greenwood Village", "state": "CO", "zip": "80111" } }, { "id": 2, "name": "Demaryius Thomas", "phone": "(720) 213-9876", "address": { "street": "5555 Marion Street", "city": "Denver", "state": "CO", "zip": "80202" } }, { "id": 3, "name": "Von Miller", "phone": "(917) 323-2333", "address": { "street": "14 Mountain Way", "city": "Vail", "state": "CO", "zip": "81657" } } ]

Modify src/app/shared/search/search.service.ts and provide Http as a dependency in its constructor. In this same file, create a getAll() method to gather all the people. Also, define the Address and Person classes that JSON will be marshalled to.

修改src/app/shared/search/search.service.ts并在其构造函数中提供Http作为依赖项。 在同一文件中,创建一个getAll()方法来收集所有人员。 另外,定义将被编组到JSON的Address和Person类。

import { Injectable } from '@angular/core'; import { Http, Response } from '@angular/http'; import 'rxjs/add/operator/map'; @Injectable() export class SearchService { constructor(private http: Http) {} getAll() { return this.http.get('assets/data/people.json') .map((res: Response) => res.json()); } } export class Address { street: string; city: string; state: string; zip: string; constructor(obj?: any) { this.street = obj && obj.street || null; this.city = obj && obj.city || null; this.state = obj && obj.state || null; this.zip = obj && obj.zip || null; } } export class Person { id: number; name: string; phone: string; address: Address; constructor(obj?: any) { this.id = obj && Number(obj.id) || null; this.name = obj && obj.name || null; this.phone = obj && obj.phone || null; this.address = obj && obj.address || null; } }

To make these classes available for consumption by your components, edit src/app/shared/index.ts and add the following:

要使这些类可供组件使用,请编辑src/app/shared/index.ts并添加以下内容:

export * from './search/search.service';

The reason for creating this file is so you can import multiple classes on a single line rather than having to import each individual class on separate lines.

创建此文件的原因是,您可以在一行上导入多个类,而不必在单独的行上导入每个类。

In src/app/search/search.component.ts, add imports for these classes.

在src/app/search/search.component.ts ,为这些类添加导入。

import { Person, SearchService } from '../shared';

You can now add query and searchResults variables. While you’re there, modify the constructor to inject the SearchService.

现在,您可以添加query和searchResults变量。 在那里,修改构造函数以注入SearchService 。

export class SearchComponent implements OnInit { query: string; searchResults: Array<Person>; constructor(private searchService: SearchService) {}

Then implement a search() method to call the service’s getAll() method.

然后实现search()方法来调用服务的getAll()方法。

search(): void { this.searchService.getAll().subscribe( data => { this.searchResults = data; }, error => console.log(error) ); }

At this point, you’ll likely see the following message in your browser’s console.

此时,您可能会在浏览器的控制台中看到以下消息。

ORIGINAL EXCEPTION: No provider for SearchService!

To fix the “No provider” error from above, update app.module.ts to import the SearchService and add the service to the list of providers. Because SearchService depends on Http, you’ll need to import HttpModule as well.

要从上方修复“无提供程序”错误,请更新app.module.ts以导入SearchService并将服务添加到提供程序列表中。 因为SearchService依赖于Http ,所以您还需要导入HttpModule 。

import { SearchService } from './shared'; import { HttpModule } from '@angular/http'; @NgModule({ ... imports: [ ... HttpModule ], providers: [SearchService], bootstrap: [AppComponent] })

Now clicking the search button should work. To make the results look better, remove the <pre> tag and replace it with a <table> in src/app/search/search.component.html.

现在,单击搜索按钮应该可以了。 为了使结果看起来更好,请删除src/app/search/search.component.html中的<pre>标签,并用<table>替换它。

<table *ngIf="searchResults"> <thead> <tr> <th>Name</th> <th>Phone</th> <th>Address</th> </tr> </thead> <tbody> <tr *ngFor="let person of searchResults; let i=index"> <td>{{person.name}}</td> <td>{{person.phone}}</td> <td>{{person.address.street}}<br/> {{person.address.city}}, {{person.address.state}} {{person.address.zip}} </td> </tr> </tbody> </table>

Then add some additional CSS in src/app/search/search.component.css to improve its table layout.

然后在src/app/search/search.component.css添加一些其他CSS来改善其表布局。

table { margin-top: 10px; border-collapse: collapse; } th { text-align: left; border-bottom: 2px solid #ddd; padding: 8px; } td { border-top: 1px solid #ddd; padding: 8px; }

Now the search results look better.

现在搜索结果看起来更好。

But wait, you still don’t have search functionality! To add a search feature, add a search() method to SearchService.

但是,等等,您仍然没有搜索功能! 要添加搜索功能,请将search()方法添加到SearchService 。

import { Observable } from 'rxjs'; search(q: string): Observable<any> { if (!q || q === '*') { q = ''; } else { q = q.toLowerCase(); } return this.getAll().map(data => data.filter(item => JSON.stringify(item).toLowerCase().includes(q))); }

Then refactor SearchComponent to call this method with its query variable.

然后重构SearchComponent以使用其query变量来调用此方法。

search(): void { this.searchService.search(this.query).subscribe( data => { this.searchResults = data; }, error => console.log(error) ); }

Now search results will be filtered by the query value you type in.

现在,搜索结果将通过您键入的查询值进行过滤。

This section showed you how to fetch and display search results. The next section builds on this and shows how to edit and save a record.

本节介绍了如何获取和显示搜索结果。 下一节将以此为基础,并说明如何编辑和保存记录。

添加编辑功能 (Add an Edit Feature)

Modify src/app/search/search.component.html to add a link for editing a person.

修改src/app/search/search.component.html以添加用于编辑人物的链接。

<td><a [routerLink]="['/edit', person.id]">{{person.name}}</a></td>

Run the following command to generate an EditComponent.

运行以下命令以生成EditComponent 。

$ ng g component edit installing component create src/app/edit/edit.component.css create src/app/edit/edit.component.html create src/app/edit/edit.component.spec.ts create src/app/edit/edit.component.ts update src/app/app.module.ts

Add a route for this component in src/app/app.module.ts:

在src/app/app.module.ts为此组件添加路由:

const appRoutes: Routes = [ {path: 'search', component: SearchComponent}, {path: 'edit/:id', component: EditComponent}, {path: '', redirectTo: '/search', pathMatch: 'full'} ];

Update src/app/edit/edit.component.html to display an editable form. You might notice I’ve added id attributes to most elements. This is to make things easier when writing integration tests with Protractor.

更新src/app/edit/edit.component.html以显示可编辑的表单。 您可能会注意到,我已经向大多数元素添加了id属性。 使用Protractor编写集成测试时,这使事情变得容易。

<div *ngIf="person"> <h3>{{editName}}</h3> <div> <label>Id:</label> {{person.id}} </div> <div> <label>Name:</label> <input [(ngModel)]="editName" name="name" id="name" placeholder="name"/> </div> <div> <label>Phone:</label> <input [(ngModel)]="editPhone" name="phone" id="phone" placeholder="Phone"/> </div> <fieldset> <legend>Address:</legend> <address> <input [(ngModel)]="editAddress.street" id="street"><br/> <input [(ngModel)]="editAddress.city" id="city">, <input [(ngModel)]="editAddress.state" id="state" size="2"> <input [(ngModel)]="editAddress.zip" id="zip" size="5"> </address> </fieldset> <button (click)="save()" id="save">Save</button> <button (click)="cancel()" id="cancel">Cancel</button> </div>

Modify EditComponent to import model and service classes and to use the SearchService to get data.

修改EditComponent以导入模型和服务类,并使用SearchService来获取数据。

import { Component, OnInit, OnDestroy } from '@angular/core'; import { Address, Person, SearchService } from '../shared'; import { Subscription } from 'rxjs'; import { ActivatedRoute, Router } from '@angular/router'; @Component({ selector: 'app-edit', templateUrl: './edit.component.html', styleUrls: ['./edit.component.css'] }) export class EditComponent implements OnInit, OnDestroy { person: Person; editName: string; editPhone: string; editAddress: Address; sub: Subscription; constructor(private route: ActivatedRoute, private router: Router, private service: SearchService) { } ngOnInit() { this.sub = this.route.params.subscribe(params => { const id = + params['id']; // (+) converts string 'id' to a number this.service.get(id).subscribe(person => { if (person) { this.editName = person.name; this.editPhone = person.phone; this.editAddress = person.address; this.person = person; } else { this.gotoList(); } }); }); } ngOnDestroy() { this.sub.unsubscribe(); } cancel() { this.router.navigate(['/search']); } save() { this.person.name = this.editName; this.person.phone = this.editPhone; this.person.address = this.editAddress; this.service.save(this.person); this.gotoList(); } gotoList() { if (this.person) { this.router.navigate(['/search', {term: this.person.name} ]); } else { this.router.navigate(['/search']); } } }

Modify SearchService to contain functions for finding a person by their id, and saving them. While you’re in there, modify the search() method to be aware of updated objects in localStorage.

修改SearchService以包含用于通过其ID查找人员并保存人员的函数。 当您在那里时,请修改search()方法以了解localStorage的更新对象。

search(q: string): Observable<any> { if (!q || q === '*') { q = ''; } else { q = q.toLowerCase(); } return this.getAll().map(data => { const results: any = []; data.map(item => { // check for item in localStorage if (localStorage['person' + item.id]) { item = JSON.parse(localStorage['person' + item.id]); } if (JSON.stringify(item).toLowerCase().includes(q)) { results.push(item); } }); return results; }); } get(id: number) { return this.getAll().map(all => { if (localStorage['person' + id]) { return JSON.parse(localStorage['person' + id]); } return all.find(e => e.id === id); }); } save(person: Person) { localStorage['person' + person.id] = JSON.stringify(person); }

You can add CSS to src/app/edit/edit.component.css if you want to make the form look a bit better.

如果要使表单看起来更好一点,可以将CSS添加到src/app/edit/edit.component.css 。

:host { display: block; padding: 0 20px; } button { margin-top: 10px; }

At this point, you should be able to search for a person and update their information.

此时,您应该可以搜索一个人并更新其信息。

The <form> in src/app/edit/edit.component.html calls a save() function to update a person’s data. You already implemented this above. The function calls a gotoList() function that appends the person’s name to the URL when sending the user back to the search screen.

src/app/edit/edit.component.html的<form>调用save()函数来更新一个人的数据。 您已经在上面实现了这一点。 该函数调用gotoList()函数,该函数将用户的名字发送回搜索屏幕时将其姓名附加到URL上。

gotoList() { if (this.person) { this.router.navigate(['/search', {term: this.person.name} ]); } else { this.router.navigate(['/search']); } }

Since the SearchComponent doesn’t execute a search automatically when you execute this URL, add the following logic to do so in its constructor.

由于执行此URL时SearchComponent不会自动执行搜索,因此请在其构造函数中添加以下逻辑来执行此操作。

import { ActivatedRoute } from '@angular/router'; import { Subscription } from 'rxjs'; ... sub: Subscription; constructor(private searchService: SearchService, private route: ActivatedRoute) { this.sub = this.route.params.subscribe(params => { if (params['term']) { this.query = decodeURIComponent(params['term']); this.search(); } }); }

You’ll want to implement OnDestroy and define the ngOnDestroy method to clean up this subscription.

您将要实现OnDestroy并定义ngOnDestroy方法来清理此预订。

import { Component, OnInit, OnDestroy } from '@angular/core'; export class SearchComponent implements OnInit, OnDestroy { ... ngOnDestroy() { this.sub.unsubscribe(); } }

After making all these changes, you should be able to search/edit/update a person’s information. If it works – nice job!

进行所有这些更改之后,您应该能够搜索/编辑/更新一个人的信息。 如果有效,那就好!

表格验证 (Form Validation)

One thing you might notice is you can clear any input element in the form and save it. At the very least, the name field should be required. Otherwise, there’s nothing to click on in the search results.

您可能会注意到的一件事是,您可以清除表单中的任何输入元素并将其保存。 至少, name字段是必填字段。 否则,搜索结果中没有任何可单击的内容。

To make name required, modify edit.component.html to add a required attribute to the name <input>.

要使名称为必填项,请修改edit.component.html以将required属性添加到名称<input> 。

<input [(ngModel)]="editName" name="name" id="name" placeholder="name" required/>

You’ll also need to wrap everything in a <form> element. Add <form> after the <h3> tag and close it before the last </div>. You’ll also need to add an (ngSubmit) handler to the form and change the save button to be a regular submit button.

您还需要将所有内容包装在<form>元素中。 在<h3>标记之后添加<form> ,并在最后一个</div>之前将其关闭。 您还需要向表单添加一个(ngSubmit)处理函数,并将保存按钮更改为常规的提交按钮。

<h3>{{editName}}</h3> <form (ngSubmit)="save()" ngNativeValidate> ... <button type="submit" id="save">Save</button> <button (click)="cancel()" id="cancel">Cancel</button> </form>

After making these changes, any field with a required attribute will be required.

进行这些更改后,将需要具有required属性的任何字段。

In this screenshot, you might notice the address fields are blank. This is explained by the error in your console.

在此屏幕截图中,您可能会注意到地址字段为空白。 这是由控制台中的错误解释的。

If ngModel is used within a form tag, either the name attribute must be set or the form control must be defined as 'standalone' in ngModelOptions. Example 1: <input [(ngModel)]="person.firstName" name="first"> Example 2: <input [(ngModel)]="person.firstName" [ngModelOptions]="{standalone: true}">

To fix, add a name attribute to all the address fields. For example:

要解决此问题,请在所有地址字段中添加name属性。 例如:

<address> <input [(ngModel)]="editAddress.street" name="street" id="street"><br/> <input [(ngModel)]="editAddress.city" name="city" id="city">, <input [(ngModel)]="editAddress.state" name="state" id="state" size="2"> <input [(ngModel)]="editAddress.zip" name="zip" id="zip" size="5"> </address>

Now values should display in all fields and name should be required.

现在,值应显示在所有字段中,并且name是必需的。

If you want to provide your own validation messages instead of relying on the browser’s, complete the following steps:

如果要提供自己的验证消息而不是依赖浏览器的验证消息,请完成以下步骤:

Remove ngNativeValidate and add #editForm="ngForm" to the <form> element.

删除ngNativeValidate并将#editForm="ngForm"添加到<form>元素。

Add #name="ngModel" to the <input id="name"> element.

将#name="ngModel"添加到<input id="name">元素。

Add [disabled]="!editForm.form.valid" to the Save button.

将[disabled]="!editForm.form.valid"到“ 保存”按钮。

Add the following under the name field to display a validation error.

在name字段下添加以下内容以显示验证错误。

<div [hidden]="name.valid || name.pristine" style="color: red"> Name is required </div>

To learn more about forms and validation, see Angular forms documentation.

要了解有关表单和验证的更多信息,请参阅Angular表单文档 。

在Okta中创建OpenID Connect应用程序 (Create an OpenID Connect App in Okta)

OpenID Connect (OIDC) is built on top of the OAuth 2.0 protocol. It allows clients to verify the identity of the user and, as well as to obtain their basic profile information. To learn more, see https://openid.net/connect.

OpenID Connect(OIDC)建立在OAuth 2.0协议之上。 它允许客户端验证用户的身份,以及获取其基本配置文件信息。 要了解更多信息,请参阅https://openid.net/connect 。

To integrate Okta for user authentication, you’ll first need to register and create an OIDC application.

要集成Okta进行用户身份验证,您首先需要注册并创建OIDC应用程序。

Login to your Okta account, or create one if you don’t have one. Navigate to Applications and click on the Add Application button. Select SPA and click Next. On the next page, specify http://localhost:4200 as a Base URI, Login redirect URI, and Logout redirect URI. Click Done and you should see settings like the following.

登录到您的Okta帐户,如果没有,请创建一个。 导航到应用程序 ,然后单击添加应用程序按钮。 选择SPA并单击Next 。 在下一页上,将http://localhost:4200指定为基本URI,登录重定向URI和注销重定向URI。 单击完成 ,您将看到类似以下的设置。

Install Manfred Steyer’s project to add OAuth 2 and OpenID Connect support using npm.

安装Manfred Steyer的项目,以使用npm 添加OAuth 2和OpenID Connect支持 。

npm install --save angular-oauth2-oidc

Modify src/app/app.component.ts to import OAuthService and configure your app to use your Okta application’s settings.

修改src/app/app.component.ts以导入OAuthService并配置您的应用程序以使用Okta应用程序的设置。

import { OAuthService, JwksValidationHandler } from 'angular-oauth2-oidc'; ... constructor(private oauthService: OAuthService) { this.oauthService.redirectUri = window.location.origin; this.oauthService.clientId = '{client-id}'; this.oauthService.scope = 'openid profile email'; this.oauthService.issuer = 'https://dev-{dev-id}.oktapreview.com'; this.oauthService.tokenValidationHandler = new JwksValidationHandler(); // Load Discovery Document and then try to login the user this.oauthService.loadDiscoveryDocument().then(() => { this.oauthService.tryLogin(); }); } ...

Create src/app/home/home.component.ts and configure it to have Login and Logout buttons.

创建src/app/home/home.component.ts并将其配置为具有“ 登录”和“ 注销”按钮。

import { Component } from '@angular/core'; import { OAuthService } from 'angular-oauth2-oidc'; @Component({ template: ` <div *ngIf="givenName"> <h2>Welcome, {{givenName}}!</h2> <button (click)="logout()">Logout</button> <p><a routerLink="/search" routerLinkActive="active">Search</a></p> </div> <div *ngIf="!givenName"> <button (click)="login()">Login</button> </div>` }) export class HomeComponent { constructor(private oauthService: OAuthService) { } login() { this.oauthService.initImplicitFlow(); } logout() { this.oauthService.logOut(); } get givenName() { const claims = this.oauthService.getIdentityClaims(); if (!claims) { return null; } return claims['name']; } }

Create src/app/shared/auth/auth.guard.service.ts to navigate to the HomeComponent if the user is not authenticated.

创建src/app/shared/auth/auth.guard.service.ts导航到HomeComponent如果用户不被认证。

import { Injectable } from '@angular/core'; import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router'; import { OAuthService } from 'angular-oauth2-oidc'; @Injectable() export class AuthGuard implements CanActivate { constructor(private oauthService: OAuthService, private router: Router) {} canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { if (this.oauthService.hasValidIdToken()) { return true; } this.router.navigate(['/home']); return false; } }

Export AuthGuard in src/shared/index.ts:

出口AuthGuard在src/shared/index.ts :

export * from './auth/auth.guard.service';

Import the OAuthModule in src/app/app.module.ts, configure the new HomeComponent, and lock the /search and /edit routes down with the AuthGuard.

导入OAuthModule在src/app/app.module.ts ,配置新的HomeComponent ,并锁定/search和/edit路线打倒AuthGuard 。

import { OAuthModule } from 'angular-oauth2-oidc'; import { HomeComponent } from './home/home.component'; import { SearchService, AuthGuard } from './shared'; const appRoutes: Routes = [ {path: 'search', component: SearchComponent, canActivate: [AuthGuard]}, {path: 'edit/:id', component: EditComponent, canActivate: [AuthGuard]}, {path: 'home', component: HomeComponent}, {path: '', redirectTo: 'home', pathMatch: 'full'}, {path: '**', redirectTo: 'home'} ]; @NgModule({ declarations: [ ... HomeComponent ], imports: [ ... OAuthModule.forRoot() ], providers: [ AuthGuard, SearchService ], bootstrap: [AppComponent] }) export class AppModule { }

After making these changes, you should be able to run ng serve and see a login button.

进行这些更改后,您应该能够运行ng serve并看到一个登录按钮。

Click the Login button and sign-in with one of the people that’s configured in your Okta application.

单击“ 登录”按钮,然后使用Okta应用程序中配置的人员之一登录 。

After logging in, you’ll be able to click Search and view people’s information.

登录后,您将可以单击搜索并查看人们的信息。

If it works – great! If you want to build your own login form in your app, continue reading to learn how to use the Okta Auth SDK with OAuthService.

如果可行,那就太好了! 如果您想在应用程序中构建自己的登录表单,请继续阅读以了解如何将Okta Auth SDK与OAuthService 。

使用Okta Auth SDK进行身份验证 (Authentication with the Okta Auth SDK)

The Okta Auth SDK builds on top of Otka’s Authentication API and OAuth 2.0 API to enable you to create a fully branded sign-in experience using JavaScript.

Okta Auth SDK建立在Otka的Authentication API和OAuth 2.0 API的基础上 ,使您能够使用JavaScript创建完全品牌化的登录体验。

Install it using npm:

使用npm安装它:

npm install @okta/okta-auth-js --save

Add a reference to this library’s main JavaScript file in .angular-cli.json:

在.angular-cli.json添加对该库的主要JavaScript文件.angular-cli.json :

"scripts": [ "../node_modules/@okta/okta-auth-js/dist/okta-auth-js.min.js" ],

The components in this section use Bootstrap CSS classes. Install Bootstrap 4.

本节中的组件使用Bootstrap CSS类。 安装Bootstrap 4。

npm install bootstrap@4.0.0-beta --save

Modify src/styles.css to add a reference to Bootstrap’s CSS file.

修改src/styles.css以添加对BootstrapCSS文件的引用。

@import "~bootstrap/dist/css/bootstrap.css";

Update src/app/app.component.html to use Bootstrap classes for its navbar and grid system.

更新src/app/app.component.html以将Bootstrap类用于其导航栏和网格系统。

<nav class="navbar navbar-light bg-secondary"> <a class="navbar-brand text-light" href="#">Welcome to {{title}}!</a> </nav> <div class="container-fluid"> <router-outlet></router-outlet> </div>

Create src/app/shared/auth/okta.auth.wrapper.ts to wrap the Okta Auth SDK and integrate it with OAuthService. Its login() method uses OktaAuth to get a session token and exchange it for ID and access tokens.

创建src/app/shared/auth/okta.auth.wrapper.ts来包装Okta Auth SDK并将其与OAuthService集成。 它的login()方法使用OktaAuth获取会话令牌并将其交换为ID和访问令牌。

import { OAuthService } from 'angular-oauth2-oidc'; import { Injectable } from '@angular/core'; declare const OktaAuth: any; @Injectable() export class OktaAuthWrapper { private authClient: any; constructor(private oauthService: OAuthService) { this.authClient = new OktaAuth({ url: this.oauthService.issuer }); } login(username: string, password: string): Promise<any> { return this.oauthService.createAndSaveNonce().then(nonce => { return this.authClient.signIn({ username: username, password: password }).then((response) => { if (response.status === 'SUCCESS') { return this.authClient.token.getWithoutPrompt({ clientId: this.oauthService.clientId, responseType: ['id_token', 'token'], scopes: ['openid', 'profile', 'email'], sessionToken: response.sessionToken, nonce: nonce, redirectUri: window.location.origin }) .then((tokens) => { const idToken = tokens[0].idToken; const accessToken = tokens[1].accessToken; const keyValuePair = `#id_token=${encodeURIComponent(idToken)}&access_token=${encodeURIComponent(accessToken)}`; return this.oauthService.tryLogin({ <1> customHashFragment: keyValuePair, disableOAuth2StateCheck: true }); }); } else { return Promise.reject('We cannot handle the ' + response.status + ' status'); } }); }); } }

In the above code, oauthService.tryLogin() parses and stores the idToken and accessToken so they can be retrieved using OAuthService.getIdToken() and OAuthService.getAccessToken().

在上面的代码中, oauthService.tryLogin()解析并存储idToken和accessToken以便可以使用OAuthService.getIdToken()和OAuthService.getAccessToken()进行检索。

Export OktaAuthWrapper in src/shared/index.ts:

出口OktaAuthWrapper在src/shared/index.ts :

export * from './auth/okta.auth.wrapper';

Add OktaAuthWrapper as a provider in app.module.ts.

添加OktaAuthWrapper作为一个供应商app.module.ts 。

import { SearchService, AuthGuard, OktaAuthWrapper } from './shared'; @NgModule({ ... providers: [ ... OktaAuthWrapper ], bootstrap: [AppComponent] })

Change HomeComponent to declare OktaAuth and modify its template so it has a button to login, as well as a sign-in form.

更改HomeComponent申报OktaAuth并修改其template所以它有一个按钮来登录,以及一个登录表单。

@Component({ template: ` <div *ngIf="givenName" class="col-12 mt-2"> <button (click)="logout()" class="btn btn-sm btn-outline-primary float-right">Logout</button> <h2>Welcome, {{givenName}}!</h2> <p><a routerLink="/search" routerLinkActive="active">Search</a></p> </div> <div class="card mt-2" *ngIf="!givenName"> <div class="card-body"> <h4 class="card-title">Login with Authorization Server</h4> <button class="btn btn-primary" (click)="login()">Login</button> </div> </div> <div class="card mt-2" *ngIf="!givenName"> <div class="card-body"> <h4 class="card-title">Login with Username/Password</h4> <p class="alert alert-error" *ngIf="loginFailed"> Login wasn't successful. </p> <div class="form-group"> <label>Username</label> <input class="form-control" [(ngModel)]="username"> </div> <div class="form-group"> <label>Password</label> <input class="form-control" type="password" [(ngModel)]="password"> </div> <div class="form-group"> <button class="btn btn-primary" (click)="loginWithPassword()">Login</button> </div> </div> </div>` })

After making these changes, the HomeComponent should render as follows.

进行这些更改后, HomeComponent应该呈现如下。

Add local variables for the username and password fields, import OktaAuthWrapper, and implement a loginWithPassword() method in HomeComponent.

添加局部变量的用户名和密码字段,进口OktaAuthWrapper ,并实施loginWithPassword()的方法HomeComponent 。

import { OktaAuthWrapper } from '../shared'; ... username; password; constructor(private oauthService: OAuthService, private oktaAuthWrapper: OktaAuthWrapper) { } loginWithPassword() { this.oktaAuthWrapper.login(this.username, this.password) .then(_ => console.debug('logged in')) .catch(err => console.error('error logging in', err)); }

You should be able to sign in using the form, using one of your app’s registered users. After logging in, you’ll be able to click the Search link and view people’s information.

您应该能够使用您应用的注册用户之一使用该表单登录。 登录后,您将可以单击“ 搜索”链接并查看人们的信息。

角+ Okta (Angular + Okta)

If everything works – congrats! If you encountered issues, please post a question to Stack Overflow with an okta tag, or hit me up on Twitter @mraible.

如果一切正常–恭喜! 如果遇到问题,请使用okta标签将问题发布到Stack Overflow,或在Twitter @mraible上打我。

You can find a completed version of the application created in this blog post on GitHub. To learn more about security in Angular, see Angular’s Security documentation. If you’d like to learn more about OpenID Connect, I’d recommend watching the soothing video below.

您可以在GitHub上的此博客文章中找到该应用程序的完整版本。 要了解有关Angular中安全性的更多信息,请参阅Angular的安全性文档 。 如果您想了解有关OpenID Connect的更多信息,建议您观看下面的舒缓视频。

翻译自: https://www.sitepoint.com/angular-authentication-oidc/

angular2创建应用

相关资源:CSharpExpenseCalculator:ASP.NET Web API,Angular CLI,实体框架(带有令牌身份验证的预算应用)-源码
最新回复(0)