前言
一個(gè)網(wǎng)站,通常都會(huì)包含公開頁(yè)面和保護(hù)頁(yè)面兩種,如果是OA或者企業(yè)應(yīng)用網(wǎng)站掸冤,甚至可能全部都是保護(hù)頁(yè)面,訪問(wèn)者需要在進(jìn)行身份認(rèn)證后友雳,才能正常的瀏覽相關(guān)頁(yè)面稿湿。
路由進(jìn)階應(yīng)用
在上一篇 Angular 2.0 SPA應(yīng)用 - 從腳手架開始 (1) 文章中,我們介紹了如何從一個(gè)腳手架Angular 2.0開始押赊,添加一個(gè)首頁(yè)和登錄頁(yè)面饺藤,并實(shí)現(xiàn)了相關(guān)的路由功能。
本文中流礁,我們將會(huì)添加一個(gè)郵件發(fā)送頁(yè)面涕俗,同時(shí),希望只有登錄用戶可以訪問(wèn)此頁(yè)面神帅。
- 路由守衛(wèi)(Route Guard)
添加AuthGuard再姑,隨機(jī)返回True/False(分別為50%概率)。
import { Injectable } from '@angular/core';
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private router: Router) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
console.log('AuthGuard#canActivate called');
if (this.checkLogin()) {
// l已登錄找御,返回Ture
console.log("AuthGuard: 用戶已登陸元镀。");
return true;
}
// 未登陸绍填,重定向URL到登錄頁(yè)面,包含返回URL參數(shù)栖疑,然后返回False
this.router.navigate(['/login'], { queryParams: { returnUrl: state.url }});
return false;
}
private checkLogin(): boolean {
//隨機(jī)返回Ture /False
let loggedIn:boolean = Math.random() < 0.5;
if(!loggedIn){
console.log("AuthGuard: 用戶未登陸讨永。");
}
return loggedIn;
}
}
- 郵件組件 (MailComponent)
//mail.component.ts
import {Component} from '@angular/core'
@Component({
selector: 'mail',
moduleId: __moduleName,
template: `
<div class="container" style="margin-top:100px;">
<h1>Mail Page</h1>
<div>
`
})
export class MailComponent {
}
- 修改app.ts
添加MailComponent和AuthGuard引用,添加MailComponent路由并對(duì)MailComponent使用AuthGuard守護(hù)蔽挠。
......
import { MailComponent } from './mail/mail.component';
import { AuthGuard } from './login/auth.guard.ts';
const appRoutes: Routes = [
......
{ path: 'mail', component: MailComponent, canActivate: [AuthGuard] },
......
];
@NgModule({
......
declarations: [ App, HomeComponent, LoginComponent, MailComponent ],
providers: [ AuthGuard ],
......
})
新增或修改的代碼主要功能是:
- canActivate屬性聲明路由守衛(wèi)(Route Guard)
- providers屬性提供依賴注入(Dependency Injection)住闯。
- 修改app.template.html
在原來(lái)的Home菜單旁邊,添加Mail菜單
<ul class="nav navbar-nav">
<li><a routerLink="home" routerLinkActive="active">首頁(yè)</a></li>
<li><a routerLink="mail" routerLinkActive="active">Mail</a></li>
</ul>
- canActivate屬性聲明路由守衛(wèi)(Route Guard)
- providers屬性聲明依賴注入(Dependency Injection)澳淑。
身份驗(yàn)證
在上面的AuthGuard中比原,我們并沒(méi)有真正實(shí)現(xiàn)用戶身份的驗(yàn)證,只是隨機(jī)返回True/False來(lái)模擬用戶已登錄或未登陸狀態(tài)下杠巡,訪問(wèn)守護(hù)頁(yè)面時(shí)量窘,路由導(dǎo)航應(yīng)有的反應(yīng)。
將驗(yàn)證邏輯從AuthGuard分離氢拥,實(shí)現(xiàn)一個(gè)authenticationService蚌铜,應(yīng)該包含以下功能:
- 是否已通過(guò)身份驗(yàn)證 isAuth
- 登錄身份驗(yàn)證 Login(username, password)
- 注銷當(dāng)前登錄 Logout()
- ** 添加 src/auth/authentication.service.ts **
import { Injectable } from '@angular/core';
@Injectable()
export class AuthenticationService {
isAuth() {
if (localStorage.getItem('currentUser')) {
return true;
}
else { return false; }
}
login(username: string, password: string) {
if (username=='admin' && password=="admin") {
localStorage.setItem('currentUser', username);
return true;
}
else {
return false;
}
}
logout() {
// remove user from local storage to log user out
localStorage.removeItem('currentUser');
}
}
好吧,我必須承認(rèn)我偷懶嫩海,現(xiàn)在還是假的驗(yàn)證邏輯冬殃,登陸用戶名和密碼都是"admin",就通過(guò)驗(yàn)證叁怪。這樣一來(lái)审葬,我們可以先不管復(fù)雜的后端驗(yàn)證邏輯,先修改并測(cè)試前端登錄界面奕谭。
- ** 修改 src/login/authGuard.ts **
import { Injectable } from '@angular/core';
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { AuthenticationService } from '../auth/authentication.service.ts'
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private router: Router, private authService: AuthenticationService) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
console.log('AuthGuard#canActivate called');
if (this.authService.isAuth()) {
// l已登錄涣觉,返回Ture
return true;
}
// 未登陸,重定向URL到登錄頁(yè)面血柳,包含返回URL參數(shù)官册,然后返回False
this.router.navigate(['/login'], { queryParams: { returnUrl: state.url }});
return false;
}
}
......
- ** Login 組件**
login.template.html
<div class="container">
<div id="loginbox" style="margin-top:100px;" class="mainbox col-md-6 col-md-offset-3 col-sm-8 col-sm-offset-2">
<div class="panel panel-info">
<div class="panel-heading">
<div class="panel-title">Login</div>
</div>
<form name="form" (ngSubmit)="f.form.valid && login()" #f="ngForm" novalidate>
<div style="padding:30px" class="panel-body">
<div class="form-group" [ngClass]="{ 'has-error': f.submitted && !username.valid }">
<label for="username">Username</label>
<input type="text" class="form-control" name="username" [(ngModel)]="model.username" #username="ngModel" required />
<div *ngIf="f.submitted && !username.valid" class="help-block">Username is required</div>
</div>
<div class="form-group" [ngClass]="{ 'has-error': f.submitted && !password.valid }">
<label for="password">Password</label>
<input type="password" class="form-control" name="password" [(ngModel)]="model.password" #password="ngModel" required />
<div *ngIf="f.submitted && !password.valid" class="help-block">Password is required</div>
</div>
<div class="form-group">
<button [disabled]="loading" class="btn btn-primary">Login</button>
<img *ngIf="loading" src="data:image/gif;base64,R0lGODlhEAAQAPIAAP///wAAAMLCwkJCQgAAAGJiYoKCgpKSkiH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAADMwi63P4wyklrE2MIOggZnAdOmGYJRbExwroUmcG2LmDEwnHQLVsYOd2mBzkYDAdKa+dIAAAh+QQJCgAAACwAAAAAEAAQAAADNAi63P5OjCEgG4QMu7DmikRxQlFUYDEZIGBMRVsaqHwctXXf7WEYB4Ag1xjihkMZsiUkKhIAIfkECQoAAAAsAAAAABAAEAAAAzYIujIjK8pByJDMlFYvBoVjHA70GU7xSUJhmKtwHPAKzLO9HMaoKwJZ7Rf8AYPDDzKpZBqfvwQAIfkECQoAAAAsAAAAABAAEAAAAzMIumIlK8oyhpHsnFZfhYumCYUhDAQxRIdhHBGqRoKw0R8DYlJd8z0fMDgsGo/IpHI5TAAAIfkECQoAAAAsAAAAABAAEAAAAzIIunInK0rnZBTwGPNMgQwmdsNgXGJUlIWEuR5oWUIpz8pAEAMe6TwfwyYsGo/IpFKSAAAh+QQJCgAAACwAAAAAEAAQAAADMwi6IMKQORfjdOe82p4wGccc4CEuQradylesojEMBgsUc2G7sDX3lQGBMLAJibufbSlKAAAh+QQJCgAAACwAAAAAEAAQAAADMgi63P7wCRHZnFVdmgHu2nFwlWCI3WGc3TSWhUFGxTAUkGCbtgENBMJAEJsxgMLWzpEAACH5BAkKAAAALAAAAAAQABAAAAMyCLrc/jDKSatlQtScKdceCAjDII7HcQ4EMTCpyrCuUBjCYRgHVtqlAiB1YhiCnlsRkAAAOwAAAAAAAAAAAA=="
/>
</div>
</div>
</form>
</div>
</div>
</div>
login.component.ts
import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { AuthenticationService } from '../auth/authentication.service';
@Component({
selector: 'login',
moduleId: __moduleName,
templateUrl: './login.template.html'
})
export class LoginComponent {
model: any = {};
loading = false;
returnUrl: string;
constructor(private route: ActivatedRoute, private router: Router, private authService: AuthenticationService) {}
ngOnInit() {
// reset login status
this.authService.logout();
// get return url from route parameters or default to '/'
this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
}
login() {
this.loading = true;
if (this.authService.login(this.model.username, this.model.password)) {
this.router.navigate([this.returnUrl]);
}
else {
this.loading = false;
}
}
}
由于LoginComponent.ts的構(gòu)造函數(shù)需要 AuthenticationService 依賴注入,我們需要回過(guò)頭去难捌,修改app.ts文件膝宁,加入相關(guān)代碼,這兒就不詳細(xì)寫出來(lái)根吁,請(qǐng)讀者自行摸索一下昆汹。
總結(jié)
在本文中,學(xué)習(xí)了Route Guard婴栽,加入身份認(rèn)證满粗,登錄界面做了修改,基本可以使用了愚争,還多次使用依賴注入映皆。如果你對(duì)這些知識(shí)點(diǎn)還有不清楚的地方挤聘,建議可以到 Angular 2.0 查閱文檔。
Plunker Demo
下篇預(yù)告
在兩篇文章中捅彻,基本完成了Angular SPA常用的功能介紹组去,貌似太快了一點(diǎn)點(diǎn)。
下篇寫啥呢步淹,有點(diǎn)失去方向从隆,身份認(rèn)證繼續(xù)發(fā)展,就是引入后端服務(wù)的時(shí)候缭裆,要么介紹一下Interception和mockBackendService技術(shù)键闺,如果你有什么建議和意見,不妨告訴我澈驼,謝謝辛燥!