Angular 2.0 SPA应用 - 身份认证(2)

前言

一个网站,通常都会包含公开页面和保护页面两种,如果是OA或者企业应用网站,甚至可能全部都是保护页面,访问者需要在进行身份认证后,才能正常的浏览相关页面。

路由进阶应用

在上一篇 Angular 2.0 SPA应用 - 从脚手架开始 (1) 文章中,我们介绍了如何从一个脚手架Angular 2.0开始,添加一个首页和登录页面,并实现了相关的路由功能。
本文中,我们将会添加一个邮件发送页面,同时,希望只有登录用户可以访问此页面。

  1. 路由守卫(Route Guard)
    添加AuthGuard,随机返回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到登录页面,包含返回URL参数,然后返回False
     this.router.navigate(['/login'], { queryParams: { returnUrl: state.url }});
     return false;
  }
  private checkLogin(): boolean {
     //随机返回Ture /False
     let loggedIn:boolean = Math.random() < 0.5;
     if(!loggedIn){
         console.log("AuthGuard: 用户未登陆。");
     }
     return loggedIn;
  }
}
  1. 邮件组件 (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 {
}
  1. 修改app.ts
    添加MailComponent和AuthGuard引用,添加MailComponent路由并对MailComponent使用AuthGuard守护。
......
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属性声明路由守卫(Route Guard)
  • providers属性提供依赖注入(Dependency Injection)。
  1. 修改app.template.html
    在原来的Home菜单旁边,添加Mail菜单
<ul class="nav navbar-nav">
        <li><a routerLink="home" routerLinkActive="active">首页</a></li>
        <li><a routerLink="mail" routerLinkActive="active">Mail</a></li>
</ul>
  • canActivate属性声明路由守卫(Route Guard)
  • providers属性声明依赖注入(Dependency Injection)。

身份验证

在上面的AuthGuard中,我们并没有真正实现用户身份的验证,只是随机返回True/False来模拟用户已登录或未登陆状态下,访问守护页面时,路由导航应有的反应。
将验证逻辑从AuthGuard分离,实现一个authenticationService,应该包含以下功能:

  • 是否已通过身份验证 isAuth
  • 登录身份验证 Login(username, password)
  • 注销当前登录 Logout()
  1. ** 添加 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');
 }
}

好吧,我必须承认我偷懒,现在还是假的验证逻辑,登陆用户名和密码都是"admin",就通过验证。这样一来,我们可以先不管复杂的后端验证逻辑,先修改并测试前端登录界面。

  1. ** 修改 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到登录页面,包含返回URL参数,然后返回False
       this.router.navigate(['/login'], { queryParams: { returnUrl: state.url }});
       return false;
     }
}
......
  1. ** 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的构造函数需要 AuthenticationService 依赖注入,我们需要回过头去,修改app.ts文件,加入相关代码,这儿就不详细写出来,请读者自行摸索一下。

总结

在本文中,学习了Route Guard,加入身份认证,登录界面做了修改,基本可以使用了,还多次使用依赖注入。如果你对这些知识点还有不清楚的地方,建议可以到 Angular 2.0 查阅文档。
Plunker Demo

下篇预告

在两篇文章中,基本完成了Angular SPA常用的功能介绍,貌似太快了一点点。
下篇写啥呢,有点失去方向,身份认证继续发展,就是引入后端服务的时候,要么介绍一下Interception和mockBackendService技术,如果你有什么建议和意见,不妨告诉我,谢谢!

系列文章目录

  1. Angular 2.0 SPA应用 - 从脚手架开始 (1)
  2. Angular 2.0 SPA应用 - 身份认证(2)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容

  • 前言 今天是2017年3月7日,Angular 2.0目前最新版本是 2.0.0-beta.17。网络上搜索到的A...
    程序员长春阅读 3,732评论 1 22
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139
  • 第一节:初识Angular-CLI第二节:登录组件的构建第三节:建立一个待办事项应用第四节:进化!模块化你的应用第...
    接灰的电子产品阅读 13,641评论 64 25
  • 第一节:初识Angular-CLI第二节:登录组件的构建第三节:建立一个待办事项应用第四节:进化!模块化你的应用第...
    接灰的电子产品阅读 21,189评论 51 43
  • 【与萌共长】20171122学习力践行Day43 今天回家继续磨那首英文儿歌,萌已经能完整唱出。然后萌就开始跳绳,...
    艳萍和萌宝阅读 164评论 0 0