检查用户是否登录 Angular 2 中的任何页面更改

Check if the user logged in on any page change in Angular 2

A​​ngular2 进展缓慢但稳步推进。现在我面临以下挑战。我想检查用户是否在每次页面更改时登录(换句话说,在加载每个组件时)。当然,我可以在每一个中实现 OnInit 接口,但那是代码味道。

是否有任何有效的方法来执行应用程序每个页面上所需的任何内容?我很想听听有关如何处理此任务的最佳实践的任何其他建议。

我正在使用这个库 (https://auth0.com/blog/2015/11/10/introducing-angular2-jwt-a-library-for-angular2-authentication/) 进行基于 jwt 的登录,我已经有了一个很好的服务 class,它封装了所有与身份验证相关的功能。因此,实际检查用户登录的位置已经完成并经过测试。

谢谢,

我认为扩展 Router Outlet 是实现此目的的常用方法

CaptainCodeman 不久前在 Gitter 中发布的示例(我自己还没有测试)

  import {Directive, Injector, Attribute, ElementRef, DynamicComponentLoader} from 'angular2/core';
  import {Router, RouteData, RouterOutlet, RouteParams, Instruction, ComponentInstruction} from 'angular2/router';

  /*
    Example implementation

    Given a route:
    @RouteConfig([
    { path: '/thing/:id', component: ThingComponent, name: 'Thing', data: { public: false, roles:['thinger'] } }
    ])

    authorize(instruction: ComponentInstruction):boolean {
      // simplest case - route is public
      if (<boolean>instruction.routeData.data['public']) {
        return true;
      }

      // if not public then we at least need an authenticated user
      if (this.isAuthenticated()) {
        var routeRoles = <any[]>instruction.routeData.data['roles'];
        var userRoles = <string[]>this.roles();

        // no roles required for route = user just needs to be authenticated
        var authorized = routeRoles.length === 0 || routeRoles.some(routeRole => userRoles.indexOf(routeRole) >= 0);

        return authorized;
      }

      return false;
    }
  */
  export abstract class IAuthService {
    abstract isAuthenticated():boolean;
    authorize(instruction: ComponentInstruction, params:any):boolean {
      // authorized if route allows public access or user is authenticated
      return this.isAuthenticated() || <boolean>instruction.routeData.data['public']
    }
  }
@Directive({selector: 'secure-outlet'})
  export class SecureRouterOutlet extends RouterOutlet {
    signin:string;
    unauthorized:string;
    injector:Injector;

    private parentRouter: Router;
    private authService: IAuthService;

    constructor(_elementRef: ElementRef, _loader: DynamicComponentLoader,
                _parentRouter: Router, @Attribute('name') nameAttr: string,
                authService:IAuthService,
                injector:Injector,
                @Attribute('signin') signinAttr: string,
                @Attribute('unauthorized') unauthorizedAttr: string) {
      super(_elementRef, _loader, _parentRouter, nameAttr);
      this.parentRouter = _parentRouter;
      this.authService = authService;
      this.injector = injector;
      this.signin = signinAttr;
      this.unauthorized = unauthorizedAttr;
    }

    activate(nextInstruction: ComponentInstruction): Promise<any> {
      var params = this.getAllRouteParams(this.injector);
      var isAuthorized = this.authService.authorize(nextInstruction, params);

      if (isAuthorized) {
        return super.activate(nextInstruction);
      }

      if (this.authService.isAuthenticated()) {
        var ins = this.parentRouter.generate([this.unauthorized]);
        return super.activate(ins.component);
      } else {
        var ins = this.parentRouter.generate([this.signin,{url:location.pathname}]);
        return super.activate(ins.component);
      }
    }

    reuse(nextInstruction: ComponentInstruction): Promise<any> {
      return super.reuse(nextInstruction);
    }

    getAllRouteParams(injector) {
      let params = null;
      while(injector) {
        const routeParams = injector.getOptional(RouteParams);
        if (routeParams) {
          if (params === null) {
            params = {};
          } else {
            params = Object.create(params);
          }

          Object.assign(params, routeParams.params);
        }
        injector = injector.parent;
      }
      return params;
    }
  }

如果你使用路由(你说的似乎就是这样:"on every page change"),你可以利用几件事:

  • 创建一个自定义路由器出口(RouterOutlet 的子 class),它会调用其 activate 方法检查身份验证。在这种情况下,您可以拥有一些全球性的东西。类似的东西:

    @Directive({
      selector: 'auth-outlet'
    })
    export class AuthOutlet extends RouterOutlet {
      (...)
    
      activate(oldInstruction: ComponentInstruction) {
        var url = this.parentRouter.lastNavigationAttempt;
        if (isAuthenticated()) {
          return super.activate(oldInstruction);
        } else {
          (...)
        }
      }
    }
    

    有关详细信息,请参阅此问题:

  • 利用 CanActivate 装饰器检查组件是否可以激活。在您的情况下,您可以在此级别执行身份验证检查。

  • 也可以在 RouterLink 级别做一些事情来显示/隐藏路由链接。在这种情况下,您可以根据相关路由配置和当前用户提示在这些链接上应用角色。有关详细信息,请参阅此问题:

这也可以在 HTTP 拦截器(扩展 Http 的 class 中处理)。在这种情况下,当请求正在执行时,您可以插入一些身份验证检查:

@Injectable()
export class CustomHttp extends Http {
  constructor(backend: ConnectionBackend, defaultOptions: RequestOptions) {
    super(backend, defaultOptions);
  }

  request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
    console.log('request...');
    if (isAuthenticated()) {
      return super.request(url, options).catch(res => {
        // do something
      });        
    } else {
      // Redirect to login page
      // Or throw an exception: return Observable.throw(new Error(...));
    }
  }

  (...)
}

有关详细信息,请参阅此问题:

我正在向您展示 Angular2 的简单实现。您可以利用 @CanActivate 钩子,如下图所示,使用 isLoggedIn 函数 检查用户是否登录 returns 承诺.

注意:下面的实现是在访问任何组件之前检查用户是否loggedIn。希望通过一些修改可以达到你想要的效果。

Auth.ts

import {Observable} from 'rxjs/Observable';

export class Auth {
  constructor() {
    this.loggedIn = false;
  }

  login() {
    this.loggedIn = true;
  }

  logout() {
    this.loggedIn = false;
  }

  check() {
    return Observable.of(this.loggedIn);
  }
}

isLoggedIn.ts

import {Injector} from 'angular2/core';
import {appInjector} from './appInjector';
import {Auth} from './Auth';
import {Router, ComponentInstruction} from 'angular2/router';

export const isLoggedIn = (next: ComponentInstruction, previous: ComponentInstruction) => {
    let injector: Injector = appInjector(); // get the stored reference to the injector
    let auth: Auth = injector.get(Auth);
    let router: Router = injector.get(Router);

  // return a boolean or a promise that resolves a boolean
    return new Promise((resolve) => {
      auth.check()
          .subscribe((result) => {
                    if (result) {
                        resolve(true);
                    } else {
                        router.navigate(['/Login']);
                        resolve(false);
                    }
                });
  });
};

appInjector.ts

import {Injector} from 'angular2/core';

let appInjectorRef: Injector;
export const appInjector = (injector?: Injector):Injector => {
    if (injector) {
      appInjectorRef = injector;
    }

    return appInjectorRef;
};

somecomponent.ts

import {Component, View,ViewChild} from 'angular2/core';
import {CanActivate} from 'angular2/router';
import {isLoggedIn} from './isLoggedIn';

@Component({
  selector: 'some',
  template: 'some text'
})
@CanActivate((next: ComponentInstruction, previous: ComponentInstruction) => {
  return isLoggedIn(next, previous);  // this will tell whether user is loggedIn or not. 
})
export class Protected {
}

boot.ts

.
.
import { provide, ComponentRef } from 'angular2/core';
import { appInjector } from './app-injector';
.
.
bootstrap(AppComponent, [...]).then((appRef: ComponentRef) => {
  // store a reference to the application injector
  appInjector(appRef.injector);
});

在这篇很棒的文章 Authentication in Angular 2

中显示和实施了两种限制访问的方法 Custom Router OutletCanActivate Decorator

这就是我所做的,在app.routing.ts

中使用了canActive 属性
 {
  path: 'dashboard',
  loadChildren: './dashboard',
  canActivate:[AuthGuard]
}, 

请按照以下 5 分钟的视频教程进行操作

https://www.youtube.com/watch?v=0Qsg8fyKwO4

注意:此解决方案适用于 Angular 4