如何正确检查用户是否在Angular4中进行了身份验证?

时间:2017-05-16 15:05:57

标签: angular authentication auth0

我目前正在开发Angular 4应用程序。

应用程序使用Auth0进行身份验证,其语法与其他身份验证服务的语法非常相似。

身份验证代码如下所示:

// auth.services.ts

@Injectable()
export class Auth {
  public lock = new Auth0Lock(myConfig.clientID, myConfig.domain, myConfig.lock);
  public userProfile: any;
  public idToken: string;
  public signUpIncomplete: boolean;

  // Configure Auth0
  private auth0 = new Auth0.WebAuth({
    domain: myConfig.domain,
    clientID: myConfig.clientID,
    redirectUri: myConfig.redirectUri,
    responseType: myConfig.responseType
  });

  // Create a stream of logged in status to communicate throughout app
  private loggedIn: boolean;
  private loggedIn$ = new BehaviorSubject<boolean>(this.loggedIn);

  constructor(private router: Router, private http: Http) {
    // Set userProfile attribute of already saved profile
    this.userProfile = JSON.parse(localStorage.getItem('profile'));
  }

  public isAuthenticated(): boolean {
    // Check whether the id_token is expired or not
    console.log("isAuthenticated");
    return tokenNotExpired('id_token');
  }

  public login(username?: string, password?: string): Promise<any> {
    if (!username && !password) {
      return;
    }
    return this.processLogin(username, password);
  }

  public logout() {
    // Remove tokens and profile and update login status subject
    localStorage.removeItem('token');
    localStorage.removeItem('id_token');
    localStorage.removeItem('profile');
    this.idToken = '';
    this.userProfile = null;
    this.setLoggedIn(false);

    // Go back to the home rout
    this.router.navigate(['/']);
  }

  public loginWithWidget(): void {
    this.lock.show();
  }

  // Call this method in app.component
  // if using path-based routing <== WE ARE USING PATH BASED ROUTING
  public handleAuth(): void {
    // When Auth0 hash parsed, get profile
    this.auth0.parseHash({}, (err, authResult) => {
      if (authResult && authResult.accessToken && authResult.idToken) {
        // window.location.hash = '';
        this._getProfile(authResult);
        this.router.navigate(['/']);
      } else if (err) {
        this.router.navigate(['/']);
        console.error(`Error: ${err.error}`);
      }
    });
  }

  private setLoggedIn(value: boolean) {
    // Update login status subject
    this.loggedIn$.next(value);
    this.loggedIn = value;
  }

  private _getProfile(authResult) {
    // Use access token to retrieve user's profile and set session
    // const lock2 = new Auth0Lock(myConfig.clientID, myConfig.domain, myConfig.lock)
    const idToken = authResult.id_token || authResult.idToken;
    this.lock.getProfile(idToken, (error, profile) => {
      if (error) {
        // Handle error
        console.error(error.error);
        return;
      }
      // Save session data and update login status subject
      this._setSession(authResult, profile);
      if (!this.checkUserHasRole(profile)) {
        this.router.navigate(['/signup/complete']);
      }
    });
  }

  private _setSession(authResult, profile) {
    // Save session data and update login status subject
    localStorage.setItem('token', authResult.access_token || authResult.accessToken);
    localStorage.setItem('id_token', authResult.id_token || authResult.idToken);
    localStorage.setItem('profile', JSON.stringify(profile));
    this.idToken = authResult.id_token || authResult.idToken;
    this.setLoggedIn(true);
    this.userProfile = profile;
    this.checkUserHasRole(profile);
  }

  private processLogin(username?: string, password?: string): Promise<any> {
    const options = {
      client_id: myConfig.clientID,
      connection: postConfig.body.connection,
      grant_type: 'password',
      username,
      password,
      scope: myConfig.scope
    };
    const headers = new Headers();
    headers.append('content-type', 'application/json');
    const reqOpts = new RequestOptions({
      method: RequestMethod.Post,
      url: postConfig.urlLogin,
      headers,
      body: options
    });
    return this.http.post(postConfig.urlLogin, options, reqOpts)
      .toPromise()
      .then(this.extractData)
      .then((data) => { this._getProfile(data); })
      .catch(this.handleLoginError);
  }
  ...
}

我遇到的问题是在页面加载时调用isAuthenticated方法超过1000次。此外,每次在窗口对象中移动鼠标时都会调用它。

虽然我一步一步地遵循了Auth0的教程,但我认为这不是预期的行为,因为它会影响应用程序的性能。

经常被称为isAuthenticated的事实可能是什么原因?我是否必须实现一个定时器,它在指定时间后定期检查或者是否必须实现观察者?我的代码中是否有明显的错误?

2 个答案:

答案 0 :(得分:4)

调用isAuthenticated这么多次的原因取决于调用它的组件,这是你没有的。在此服务中永远不会调用isAuthenticated一次。

设置路由器防护,改为由Angular API调用CanActivate。这将在路由激活时调用,并且在路由组件甚至可以加载之前可能会在失败时发生重定向,并且只会被调用一次。用它来代替service.isAuthenticated

<强> login.guard.ts

import { Injectable } from '@angular/core';
import { Router, CanActivate } from '@angular/router';
import { Auth } from './auth.service';

@Injectable()
export class LoginGuard implements CanActivate {
    constructor(public auth: Auth, protected router: Router) { }

    canActivate() {
        if (!this.auth.isAuthenticated()) {
            this.router.navigate(['/']);
            return false;
        }
        return true;
    }

在您的路线定义中

export const routes: Routes = [
    { path: '', component: SomeComponent },
    { path: 'main', component: ProtectedComponent, canActivate: [LoginGuard] }
]

在任何情况下都不应该被调用1000次。我猜你的组件或注入树中有一些循环。

答案 1 :(得分:-1)

最后,我找到了原因。

我的导航组件使用主机侦听器@HostListener('mouseover', ['$event'])实现过渡效果。我不小心将主机监听器添加到了窗口对象中。出于这个原因,每次我移动鼠标时都会触发主机监听器。由于我的导航模板包含*ngIf="auth.isAuthenticated()"以显示一些导航项目以防用户进行身份验证,因此isAuthenticated被解雇了很多次。