本文最初发布在OKTA开发人员博客上 。 感谢您支持使SitePoint成为可能的合作伙伴。
Angular(以前称为Angular 2.0)正迅速成为构建现代单页应用程序的最强大方法之一。 Angular的核心优势是致力于构建可重用组件,这有助于您消除应用程序中的各种问题。 以身份验证为例:构建它可能很麻烦,但是一旦将其包装在一个组件中,身份验证逻辑就可以在整个应用程序中重用。
Angular CLI使安装新组件甚至整个项目变得容易。 如果您还没有使用Angular CLI来快速生成Angular代码,那么您将大有收获!
在此示例中,您将使用Angular CLI(一个用于Angular开发的工具)构建一个简单的Web应用程序。 您将创建一个具有搜索和编辑功能的应用程序,然后添加身份验证。
提示:如果您想跳过构建Angular应用程序并直接添加身份验证,可以克隆我的ng-demo
项目,然后跳到“ 在Okta中创建OpenID Connect应用程序”部分。
git clone https://github.com/mraible/ng-demo.git
npm install -g @angular/cli
使用ng new
命令创建一个新项目:
ng new ng-demo
这将创建一个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 $
您可以通过ng --version
查看正在使用的Angular CLI ng --version
。
$ ng --version
_ _ ____ _ ___
/ \ _ __ __ _ _ _| | __ _ _ __ / ___| | |_ _|
/ △ \ | '_ \ / _` | | | | |/ _` | '__| | | | | | |
/ ___ \| | | | (_| | |_| | | (_| | | | |___| |___ | |
/_/ \_\_| |_|\__, |\__,_|_|\__,_|_| \____|_____|___|
|___/
@angular/cli: 1.3.2
node: 8.4.0
os: darwin x64
该项目使用webpack dev server配置。 要启动它,请确保您位于ng-demo
目录中,然后运行:
ng serve
您应该在http:// localhost:4200看到类似以下的屏幕。
您可以确保新项目的测试通过,运行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)
要添加搜索功能,请在IDE或您喜欢的文本编辑器中打开项目。 对于IntelliJ IDEA,使用文件>新建项目>静态Web并指向ng-demo
目录。
在终端窗口中,进入您的项目目录cd,然后运行以下命令。 这将创建一个搜索组件。
$ 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
打开src/app/search/search.component.html
并将其默认HTML替换为以下内容:
Search
{{searchResults | json}}
Angular的路由器文档提供了设置到刚刚生成的SearchComponent
的路由所需的信息。 快速摘要:
在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 { }
在src/app/app.component.html
,调整占位符内容并添加
标记以显示路由。
Welcome to {{title}}!
有了路由设置后,就可以继续编写搜索功能了。
如果您仍在运行ng serve
,则浏览器应会自动刷新。 如果不是,请导航至http:// localhost:4200。 您可能会看到黑屏。 打开JavaScript控制台,您将看到问题。
要解决此问题,请打开src/app/app.module.ts
并将FormsModule
作为导入添加到@NgModule
:
import { FormsModule } from '@angular/forms';
@NgModule({
...
imports: [
...
FormsModule
]
...
})
export class AppModule { }
现在,您应该看到搜索表单。
如果要为此组件添加CSS,请打开src/app/search/search.component.css
并添加一些CSS。 例如:
:host {
display: block;
padding: 0 20px;
}
本节介绍了如何使用Angular CLI为基本Angular应用程序生成新组件。 下一节将向您展示如何创建和使用JSON文件和localStorage
创建伪造的API。
要获取搜索结果,请创建一个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
将生成的search.service.ts
及其测试移至app/shared/search
。 您需要创建此目录。
mkdir -p src/app/shared/search
mv src/app/search.service.* src/app/shared/search/.
创建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"
}
}
]
修改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;
}
}
要使这些类可供组件使用,请编辑src/app/shared/index.ts
并添加以下内容:
export * from './search/search.service';
创建此文件的原因是,您可以在一行上导入多个类,而不必在单独的行上导入每个类。
在src/app/search/search.component.ts
,为这些类添加导入。
import { Person, SearchService } from '../shared';
现在,您可以添加query
和searchResults
变量。 在那里,修改构造函数以注入SearchService
。
export class SearchComponent implements OnInit {
query: string;
searchResults: Array;
constructor(private searchService: SearchService) {}
然后实现search()
方法来调用服务的getAll()
方法。
search(): void {
this.searchService.getAll().subscribe(
data => { this.searchResults = data; },
error => console.log(error)
);
}
此时,您可能会在浏览器的控制台中看到以下消息。
ORIGINAL EXCEPTION: No provider for SearchService!
要从上方修复“无提供程序”错误,请更新app.module.ts
以导入SearchService
并将服务添加到提供程序列表中。 因为SearchService
依赖于Http
,所以您还需要导入HttpModule
。
import { SearchService } from './shared';
import { HttpModule } from '@angular/http';
@NgModule({
...
imports: [
...
HttpModule
],
providers: [SearchService],
bootstrap: [AppComponent]
})
现在,单击搜索按钮应该可以了。 为了使结果看起来更好,请删除 然后在 现在,搜索结果看起来更好。 但是,等等,您仍然没有搜索功能! 要添加搜索功能,请将 然后重构 现在,搜索结果将通过您键入的查询值进行过滤。 本节介绍了如何获取和显示搜索结果。 下一节将以此为基础,并说明如何编辑和保存记录。 修改 运行以下命令以生成 在 更新 修改 修改 如果要使表单看起来更好一点,可以将CSS添加到 此时,您应该可以搜索一个人并更新其信息。 由于执行此URL时 您将要实现 进行所有这些更改之后,您应该能够搜索/编辑/更新一个人的信息。 如果可行–做得好! 您可能会注意到的一件事是,您可以清除表单中的任何输入元素并将其保存。 至少,必须输入 要使名称为必需,请修改 您还需要将所有内容包装在 进行这些更改后,将需要具有 在此屏幕截图中,您可能会注意到地址字段为空白。 控制台中的错误说明了这一点。 要解决此问题,请将 现在,值应显示在所有字段中,并且 如果要提供自己的验证消息而不是依赖浏览器的验证消息,请完成以下步骤: 要了解有关表单和验证的更多信息,请参阅Angular表单文档 。 OpenID Connect(OIDC)建立在OAuth 2.0协议之上。 它允许客户端验证用户的身份,以及获取其基本配置文件信息。 要了解更多信息,请参阅https://openid.net/connect 。 要集成Okta进行用户身份验证,您首先需要注册并创建OIDC应用程序。 登录到您的Okta帐户,如果没有,请创建一个。 导航到应用程序 ,然后单击添加应用程序按钮。 选择SPA并单击Next 。 在下一页上,将 安装Manfred Steyer的项目,以使用npm 添加OAuth 2和OpenID Connect支持 。 修改 创建 创建 出口 导入 进行这些更改之后,您应该可以运行 单击“ 登录”按钮,然后使用Okta应用程序中配置的人员之一登录 。 登录后,您将可以单击搜索并查看人们的信息。 如果可行,那就太好了! 如果要在应用程序中构建自己的登录表单,请继续阅读以了解如何将Okta Auth SDK与 Okta Auth SDK建立在Otka的Authentication API和OAuth 2.0 API的基础上 ,使您能够使用JavaScript创建完全品牌化的登录体验。 使用npm安装它: 在 本节中的组件使用Bootstrap CSS类。 安装Bootstrap 4。 修改 更新 创建 在上面的代码中, 出口 添加 更改
Login wasn't successful.
进行这些更改后, 添加局部变量的用户名和密码字段,进口 您应该能够使用应用程序的注册用户之一使用该表单登录。 登录后,您将可以单击“ 搜索”链接并查看人们的信息。 如果一切正常–恭喜! 如果遇到问题,请使用okta标签将问题发布到Stack Overflow,或在Twitter @mraible上打我。 您可以在GitHub上的此博客文章中找到该应用程序的完整版本。 要了解有关Angular安全性的更多信息,请参阅Angular的安全性文档 。 如果您想了解有关OpenID Connect的更多信息,建议您观看下面的舒缓视频。 From: https://www.sitepoint.com/angular-authentication-oidc/src/app/search/search.component.html
中的标签,并用
替换它。
Name
Phone
Address
{{person.name}}
{{person.phone}}
{{person.address.street}}
{{person.address.city}}, {{person.address.state}} {{person.address.zip}}
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;
}
search()
方法添加到SearchService
。 import { Observable } from 'rxjs';
search(q: string): Observable
SearchComponent
以使用其query
变量来调用此方法。 search(): void {
this.searchService.search(this.query).subscribe(
data => { this.searchResults = data; },
error => console.log(error)
);
}
添加编辑功能
src/app/search/search.component.html
以添加用于编辑人物的链接。 {{person.name}}
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
src/app/app.module.ts
为此组件添加路由: const appRoutes: Routes = [
{path: 'search', component: SearchComponent},
{path: 'edit/:id', component: EditComponent},
{path: '', redirectTo: '/search', pathMatch: 'full'}
];
src/app/edit/edit.component.html
以显示可编辑的表单。 您可能会注意到,我已经向大多数元素添加了id
属性。 使用Protractor编写集成测试时,这使事情变得容易。 {{editName}}
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']);
}
}
}
SearchService
以包含用于通过其ID查找人员并保存人员的函数。 当您在那里时,请修改search()
方法以了解localStorage
的更新对象。 search(q: string): Observable
src/app/edit/edit.component.css
。 :host {
display: block;
padding: 0 20px;
}
button {
margin-top: 10px;
}
src/app/edit/edit.component.html
的调用
save()
函数来更新一个人的数据。 您已经在上面实现了这一点。
该函数调用gotoList()
函数,该函数将用户的名字发送回搜索屏幕时,将人的名字附加到URL上。 gotoList() {
if (this.person) {
this.router.navigate(['/search', {term: this.person.name} ]);
} else {
this.router.navigate(['/search']);
}
}
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();
}
});
}
OnDestroy
并定义ngOnDestroy
方法来清理此预订。 import { Component, OnInit, OnDestroy } from '@angular/core';
export class SearchComponent implements OnInit, OnDestroy {
...
ngOnDestroy() {
this.sub.unsubscribe();
}
}
表格验证
name
字段。 否则,搜索结果中没有任何可单击的内容。 edit.component.html
以将required
属性添加到名称 。
元素中。 在
标记之后添加
,并在最后一个
之前将其关闭。 您还需要向表单添加一个
(ngSubmit)
处理函数,并将保存按钮更改为常规的提交按钮。 {{editName}}
required
属性的任何字段。 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:
Example 2:
name
属性添加到所有地址字段。 例如:
,
name
是必需的。
ngNativeValidate
并将#editForm="ngForm"
添加到元素。
#name="ngModel"
添加到元素。
[disabled]="!editForm.form.valid"
到“ 保存”按钮。 name
字段下添加以下内容以显示验证错误。 在Okta中创建OpenID Connect应用程序
http://localhost:4200
指定为基本URI,登录重定向URI和注销重定向URI。 单击完成 ,您应该看到类似以下的设置。 npm install --save angular-oauth2-oidc
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();
});
}
...
src/app/home/home.component.ts
并将其配置为具有“ 登录”和“ 注销”按钮。 import { Component } from '@angular/core';
import { OAuthService } from 'angular-oauth2-oidc';
@Component({
template: `
Welcome, {{givenName}}!
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;
}
}
AuthGuard
在src/shared/index.ts
: export * from './auth/auth.guard.service';
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 { }
ng serve
并看到一个登录按钮。 OAuthService
。 使用Okta Auth SDK进行身份验证
npm install @okta/okta-auth-js --save
.angular-cli.json
添加对该库的主要JavaScript文件.angular-cli.json
: "scripts": [
"../node_modules/@okta/okta-auth-js/dist/okta-auth-js.min.js"
],
npm install [email protected] --save
src/styles.css
以添加对Bootstrap的CSS文件的引用。 @import "~bootstrap/dist/css/bootstrap.css";
src/app/app.component.html
以将Bootstrap类用于其导航栏和网格系统。
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
oauthService.tryLogin()
解析并存储idToken
和accessToken
以便可以使用OAuthService.getIdToken()
和OAuthService.getAccessToken()
进行检索。 OktaAuthWrapper
在src/shared/index.ts
: export * from './auth/okta.auth.wrapper';
OktaAuthWrapper
作为一个供应商app.module.ts
。 import { SearchService, AuthGuard, OktaAuthWrapper } from './shared';
@NgModule({
...
providers: [
...
OktaAuthWrapper
],
bootstrap: [AppComponent]
})
HomeComponent
申报OktaAuth
并修改其template
所以它有一个按钮来登录,以及一个登录表单。 @Component({
template: `
Welcome, {{givenName}}!
Login with Authorization Server
Login with Username/Password
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));
}
角+ Okta
你可能感兴趣的:(json,git,javascript,ViewUI)