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

作者: 程序员长春 | 来源:发表于2017-03-10 17:00 被阅读1183次

    前言

    一个网站,通常都会包含公开页面和保护页面两种,如果是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)

    相关文章

      网友评论

        本文标题:Angular 2.0 SPA应用 - 身份认证(2)

        本文链接:https://www.haomeiwen.com/subject/pziwgttx.html