【问题标题】:How to correctly check if user is authenticated in Angular4?如何正确检查用户是否在 Angular4 中通过身份验证?
【发布时间】:2017-10-15 18:05:39
【问题描述】:

我目前正在开发一个 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 经常被调用的原因可能是什么?我是否必须实现一个计时器,该计时器在指定时间后定期检查,还是我必须实现一个观察者?我的代码中是否有任何明显的错误?

【问题讨论】:

  • 我知道这是旧的,但你从哪里得到 tokenNotExpired('id_token') ?它来自一个包裹吗?
  • @Enrico 是的,它来自 @auth0/angular-jwttokenNotExpired 包,并且像 import { tokenNotExpired } from 'angular2-jwt'; 那样集成

标签: angular authentication auth0


【解决方案1】:

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 次。我猜你的组件或注入树中有一些循环。

【讨论】:

    【解决方案2】:

    终于找到原因了。

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

    【讨论】:

      猜你喜欢
      • 2017-07-15
      • 2018-12-28
      • 2013-02-22
      • 2017-09-11
      • 1970-01-01
      • 1970-01-01
      • 2023-03-04
      • 2017-07-10
      相关资源
      最近更新 更多