Angular 6 AngularFireAuth检查用户是不是在页面呈现之前登录

Posted

技术标签:

【中文标题】Angular 6 AngularFireAuth检查用户是不是在页面呈现之前登录【英文标题】:Angular 6 AngularFireAuth check if user is logged in before page renderedAngular 6 AngularFireAuth检查用户是否在页面呈现之前登录 【发布时间】:2018-11-07 10:04:51 【问题描述】:

我已经使用 Firebase auth 和 Cloud Fire Store 建立了一个新的 Angular 6 项目。有一个登录页面,您可以通过 google 登录,用户数据保存在 Firestore 中(代码如下)。我唯一的问题是如何检查用户是否已经登录,有什么最佳做法吗?

目前我正在异步获取用户数据,但导航正在闪烁。一秒钟有登录按钮,然后切换到注销按钮。有没有在呈现页面之前检查登录状态的好方法?

用户.ts

export interface User 
  uid: string;
  email: string;

auth.service.ts

import  Injectable  from '@angular/core';
import  Router  from '@angular/router';
import * as firebase from 'firebase/app';
import  AngularFireAuth  from 'angularfire2/auth';
import  AngularFirestore, AngularFirestoreDocument  from 'angularfire2/firestore';
import  Observable, of  from 'rxjs';
import  switchMap  from 'rxjs/operators';
import  User  from './user';

@Injectable(
  providedIn: 'root'
)
export class AuthService 

  user: Observable<User>;

  constructor(private afAuth: AngularFireAuth, private afs: AngularFirestore, private router: Router) 
    this.user = this.afAuth.authState.pipe(
        switchMap(user => 
          if (user) 
            return this.afs.doc<User>(`users/$user.uid`).valueChanges();
           else 
            return of(null);
          
        ))
  

  public googleLogin() 
    const provider = new firebase.auth.GoogleAuthProvider()
    return this.oAuthLogin(provider);
  

  public signOut() 
    this.afAuth.auth.signOut();
  

  private oAuthLogin(provider) 
    return this.afAuth.auth.signInWithPopup(provider)
      .then((credential) => 
        this.updateUserData(credential.user)
      )
  

  private updateUserData(user) 
    const userRef: AngularFirestoreDocument<any> = this.afs.doc(`users/$user.uid`);
    const data: User = 
      uid: user.uid,
      email: user.email
    
    return userRef.set(data,  merge: true )
  


navigation.component.ts

import  Component, OnInit  from '@angular/core';
import  AuthService  from '../core/auth.service';
import  AngularFireAuth  from 'angularfire2/auth';

@Component(
  selector: 'app-navigation',
  templateUrl: './navigation.component.html',
  styleUrls: ['./navigation.component.scss']
)
export class NavigationComponent implements OnInit 

  constructor(public auth: AuthService)  

  ngOnInit() 

auth.service.ts

<div *ngIf="auth.user | async; then loggedIn else loggedOut"></div>
                    
<ng-template #loggedOut>
    <li  class="nav-item d-none d-md-inline-block pl-2 pr-0">
        <a class="btn btn-sm btn-primary u-btn-primary u-btn-pill transition-3d-hover" href="/login">
            Login
        </a>
    </li>
</ng-template>

<ng-template #loggedIn>
    <li  class="nav-item d-none d-md-inline-block pl-2 pr-0">
        <a class="btn btn-sm btn-primary u-btn-primary u-btn-pill transition-3d-hover" (click)="auth.signOut()">
            Logout
        </a>
    </li>
</ng-template>

【问题讨论】:

【参考方案1】:

您可能很久以前就想到了这一点,但其他人可能会想知道。我选择对 angularFireAuth 的 authState 进行检查。如果它为空,则您已注销。然后您可以按照Ryan Chenkie's Medium article中的说明使用路由保护

@Injectable()
export class FirebaseAuthService 
private authState: Observable<firebase.User>
private currentUser: firebase.User = null;

constructor(
  public afAuth: AngularFireAuth,
  private http: HttpClient,
  private localStorage: LocalStorageService,
  private router: Router,
  private snackBar: MatSnackBar) 
  this.authState = this.afAuth.authState;

this.authState.subscribe(user => 
  if (user) 
    this.currentUser = user;
    this.localStorage.storeSimple('userData', user)
    this.openSnackBar('Successfully authenticated');
    console.log('AUTHSTATE USER', user)
    this.router.navigate(['home']);
   else 
    console.log('AUTHSTATE USER EMPTY', user)
    this.currentUser = null;
  
,
  err => 
    this.openSnackBar(`$err.status $err.statusText ($err.error.message)`, 'Please try again')
  );
 

 isAuthenticated(): boolean 
   return this.authState !== null;
 

 loginEmail(email, password, route) 
   this.afAuth.auth.signInWithEmailAndPassword(email, password).catch(error => 
   let errorCode = error.code;
   let errorMessage = error.message;
   this.openSnackBar(error, 'OK')
 );


logout() 
  this.afAuth.auth.signOut()
  .then(response => this.openSnackBar('Signed out'))
  .catch(error => this.openSnackBar('Error signing out: ' + error));


...

【讨论】:

您好,您的解决方案对我有用,请问,在主要组件中您如何获取用户 ID? 在上面的服务中我有一个返回 this.currentuser 的函数,所以在我的组件中,我注入了身份验证服务并可以访问 currentUser 变量 我已经尝试了很多次,但是如果我在我的主要组件中调用 currentUser,它会返回 null,如果你可以在这里查看我的答案 [***.com/q/54961071/10158519] alessandro 也许您正在运行多个登录服务实例? 请注意,仅仅因为 authState 不为空,并不意味着您在所有情况下都“登录”。这意味着您的身份已通过身份验证,例如。使用谷歌登录,然后谷歌验证你就是你所说的那个人。对于您的应用程序,您可能会添加额外的身份验证,例如“是否允许此 google 用户查看此数据?”【参考方案2】:

您可以使用可以激活 Angular 路由模块中存在的路由概念.....在这个概念中,它只是在每次页面的路由或导航发生时调用可注入服务......您还可以检查用户是否有权限查看页面

【讨论】:

angular.io/api/router/CanActivate 使用这个URL作为它的引用......我只是简单......你需要添加'canActivate'每个对象中的路由数组中的数组,在 ''canActivate 的数组中将有一个或多个可注射类。该类可用于检查用户是否登录...... 如何读取导航模板中的值?有没有像这样使用的演示项目?我需要在某个地方决定显示登录或注销按钮...感谢您的帮助:) alligator.io/angular/route-guards 用这个例子来做。这是一个路由保护示例。可注入类“canActivate”方法中有一个方法,带有 2 个参数。在这些参数上,这两个参数中包含有关您的路由的所有信息 但这会限制对整个页面的访问,但我只想更改一个按钮。 我也在寻找一个解决方案。直到 observable 获得一些值(因为从一开始就为空,然后获得当前用户值)你有一些条件逻辑的按钮异步管道需要一些时间来决定它是否被渲染。这么久你解决了吗?

以上是关于Angular 6 AngularFireAuth检查用户是不是在页面呈现之前登录的主要内容,如果未能解决你的问题,请参考以下文章

无法在 AngularFireAuth 中捕获异常

未捕获(承诺):错误:没有 AngularFireAuth 的提供者

离子 2 应用程序中的“AngularFireAuth”类型不存在属性“authState”

Angular Firestore 映射文档到对象

angularFireAuth 不适用于谷歌

AngularFire 不会触发更改检测