Angular 5可以激活重定向到浏览器刷新时登录

时间:2018-02-28 16:49:47

标签: angular authentication firebase-authentication

使用angularfire2和firebase的Angular 5身份验证应用。 该应用可以使用应用内链接进行精确导航,例如登录后重定向到仪表板或通过应用程序中的按钮/链接链接到另一个页面(组件)。 但是,如果在“http://localhost:4300/dashboard”页面上我点击浏览器刷新(Chrome),它会将我重定向回“登录”页面。 在浏览器上使用BACK / NEXT工作正常 - 但我猜是因为我并没有特别要求去特定路线。

我有一个NavBar,通过使用订阅,识别我是否登录(见右上方的截图...) - 这一切都正常。

enter image description here

我猜测在浏览器刷新或直接URL导航时,它会在确定我是否已经过身份验证之前尝试加载页面。 开发控制台从我插入导航栏组件的console.log语句中建议这一点,并且在Angular核心建议我们以开发模式运行之前它们是“未定义”的事实:

enter image description here

app.routes:

    import { Routes, RouterModule } from '@angular/router';

    import { LoginComponent } from './views/login/login.component';
    import { DashboardComponent } from './views/dashboard/dashboard.component';
    import { ProfileComponent } from './views/profile/profile.component';

    import { AuthGuard } from './services/auth-guard.service';

    const appRoutes: Routes = [
        {
            path: '',
            component: LoginComponent
        },
        {
            path: 'dashboard',
            canActivate: [AuthGuard],
            component: DashboardComponent
        },
        {
            path: 'profile',
            canActivate: [AuthGuard],
            component: ProfileComponent
        },
        {
            path: '**',
            redirectTo: ''
        }
    ];

    export const AppRoutes = RouterModule.forRoot(appRoutes);

AUTH-gaurd:

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

@Injectable()
export class AuthGuard implements CanActivate {

  status: string;

  constructor(private router: Router,
              private authService: AuthService) { }

  canActivate() {
    this.authService.authState.subscribe(state =>
      this.status = state.toString());
    console.log('Can Activate ' + this.authService.authState);
    console.log('Can Activate ' + this.authService.isLoggedIn());
    console.log('Can Activate ' + this.status);
    if(this.authService.isLoggedIn()) {
      return true;
    }

    this.router.navigate(['/']);
    return false;
  }

}

auth.service:

import { Injectable } from '@angular/core';
import { Router } from "@angular/router";

import { AngularFireAuth } from 'angularfire2/auth';
import * as firebase from 'firebase/app';
import { Observable } from 'rxjs/Observable';
import { GoogleAuthProvider, GoogleAuthProvider_Instance } from '@firebase/auth-types';
import { userInfo } from 'os';
import { Subject } from 'rxjs/Subject';

@Injectable()
export class AuthService {
  private user: Observable<firebase.User>;
  private userDetails: firebase.User = null;

  public authState = new Subject();

  constructor(private _firebaseAuth: AngularFireAuth, private router: Router) { 
      this.user = _firebaseAuth.authState;

      this.user.subscribe(
        (user) => {
          if (user) {
            this.userDetails = user;
            this.authState.next('Logged In');
            //console.log(this.userDetails);
          }
          else {
            this.userDetails = null;
            this.authState.next('Not Logged In');
          }
        }
      );
  }

  isLoggedIn() {
    if (this.userDetails == null ) {
        return false;
      } else {
        return true;
      }
  }

NAV-bar.component:

import { Component, OnInit } from '@angular/core';
import { AuthService } from '../../services/auth.service';

@Component({
  selector: 'app-nav-bar',
  templateUrl: './nav-bar.component.html',
  styleUrls: ['./nav-bar.component.css']
})
export class NavBarComponent implements OnInit {

  status: string;

  constructor(private authService: AuthService) {
    console.log('Constructor ' + this.status);
  }

  ngOnInit() {
    //this.authService.isLoggedIn().subscribe((state) => this.status = state.toString());
    this.authService.authState.subscribe(state =>
      this.status = state.toString());
    console.log('ngOnInit ' + this.status);
  }

}

1 个答案:

答案 0 :(得分:10)

在页面刷新时直接调用canActivate()方法。所以它总是返回false

canActivate() {
  this.authService.authState.subscribe(state => {
    this.status = state.toString(); // This is called async/delayed.
  });
  // so method execution proceeds

  // isLoggedIn() returns false since the login stuff in AuthService.constructor
  // is also async:    .subscribe((user) => { /* delayed login */ });
  if(this.authService.isLoggedIn()) {
    return true;
  }

  // so it comes here
  this.router.navigate(['/']); // navigating to LoginComponent
  return false;                // and canActivate returns false
}

解决方案:

import { CanActivate, Router, ActivatedRouteSnapshot,
         RouterStateSnapshot } from '@angular/router';

// ...

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
  // when the user is logged in and just navigated to another route...
  if (this.authService.isLoggedIn) { return true; } 

  // proceeds if not loggedIn or F5/page refresh 

  // Store the attempted URL for redirecting later
  this.authService.redirectUrl = state.url;

  // go login page
  this.router.navigate(['/']);
  return false;
}

现在,回到稍微改变的AuthService :(这里只更改了/相关代码)

export class AuthService {

  // new
  redirectUrl: string;

  // BehaviorSubjects have an initial value.
  // isLoggedIn is property (not function) now:
  isLoggedIn = new BehaviorSubject<boolean>(false);

  // params declared private and public in constructor become properties of the class
  constructor(private firebaseAuth: AngularFireAuth, private router: Router) {
    // so this.user is not required since it is reference to this.firebaseAuth
    this.firebaseAuth.authState.subscribe((user) => {
      if (user) {
        this.loggedIn.next(true);

        // NOW, when the callback from firebase came, and user is logged in,
        // we can navigate to the attempted URL (if exists)
        if(this.redirectUrl) {
          this.router.navigate([this.redirectUrl]);
        }
      } else {
        this.loggedIn.next(false);
      }
    }
  }

}

注意:我已在答案框中编写此代码并将其编译在我的大脑中。所以可能存在错误。此外,我不知道这是否是最好的做法。但这个想法应该清楚了吗?!

基于Angular Routing Guide

似乎有类似的问题/解决方案:Angular 2 AuthGuard + Firebase Auth