简体   繁体   中英

canActivate AuthGuard not redirecting to login page

Current behaviour: AuthGuard seems to not trigger at all and navigating to /dashboard works but leaves a blank screen, does not even console.log the Observable and does not trigger a redirect.

Expected Behaviour: Navigating to /dashboard should trigger the AuthGuard and redirect to /login.

auth.service.ts

import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { catchError, tap } from 'rxjs/operators';
import { Subject, throwError } from 'rxjs';
import { User } from './user.model';

interface AuthResponseData {
  data: {
    firstName: string;
    lastName: string;
    userId: string;
    userToken: string;
  };
}

@Injectable({
  providedIn: 'root',
})
export class AuthService {
  user = new Subject<User>();

  constructor(private http: HttpClient) {}

  login(email: string, password: string) {
    const headers = new HttpHeaders({
      'Content-Type': 'application/x-www-form-urlencoded',
    });

    let body = new HttpParams()
      .set('username', email)
      .set('password', password);

    return this.http
      .post<AuthResponseData>('${apiUrl}/Login', body, {
        headers: headers,
        withCredentials: false,
      })
      .pipe(
        catchError((errorRes) => {
          const meta = errorRes.error.meta;
          let errorMessage = 'UNKNOWN';

          if (!meta.errorName) {
            return throwError(errorMessage);
          }

          switch (meta.errorName) {
            case 'PASSWORD_DOES_NOT_MATCH':
            case 'NO_SUCH_USER':
              errorMessage = meta.errorName;
              break;
            default:
              errorMessage = errorMessage;
          }
          return throwError(errorMessage);
        }),
        tap((resData) => {
          const user = new User(
            resData.data.firstName,
            resData.data.lastName,
            resData.data.userId,
            resData.data.userToken,
          );
          this.user.next(user);
        })
      );
  }
}

Router:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { AuthComponent } from './auth/auth.component';
import { AuthGuard } from './auth/auth.guard';
import { DashboardComponent } from './dashboard/dashboard.component';
import { LayoutComponent } from './layout/layout.component';

const routes: Routes = [
  { path: '', redirectTo: '/login', pathMatch: 'full' },
  {
    path: '',
    component: LayoutComponent,
    canActivate: [AuthGuard],
    children: [
      { path: '', redirectTo: '/dashboard', pathMatch: 'full' },
      { path: 'dashboard', component: DashboardComponent },
    ],
  },
  { path: 'login', component: AuthComponent },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
})
export class AppRoutingModule {}

auth.guard.ts:

import { Injectable } from '@angular/core';
import {
  CanActivate,
  ActivatedRouteSnapshot,
  RouterStateSnapshot,
  Router,
  UrlTree,
} from '@angular/router';
import { Observable } from 'rxjs';
import { map, take } from 'rxjs/operators';
import { AuthService } from './auth.service';

@Injectable({
  providedIn: 'root',
})
export class AuthGuard implements CanActivate {
  constructor(private authService: AuthService, private router: Router) {}
  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> {
    return this.authService.user.pipe(
      take(1),
      map((user) => {
        const isAuth = !!user;
        if (isAuth) {
          return true;
        }
        console.log(user);
        return this.router.createUrlTree(['/login']);
      })
    );
  }
}

How about modifing your login method as below?

  login(email: string, password: string) {
    const headers = new HttpHeaders({
      'Content-Type': 'application/x-www-form-urlencoded',
    });

    let body = new HttpParams()
      .set('username', email)
      .set('password', password);

    this.http
      .post<AuthResponseData>('${apiUrl}/Login', body, {
        headers: headers,
        withCredentials: false,
      })
      .subscribe(resData => {
        const user = new User(
              resData.data.firstName,
              resData.data.lastName,
              resData.data.userId,
              resData.data.userToken,
            );
        this.user.next(user);
      }, (errorRes) => {
        const meta = errorRes.error.meta;
        let errorMessage = 'UNKNOWN';

        if (!meta.errorName) {
          return throwError(errorMessage);
        }

        switch (meta.errorName) {
          case 'PASSWORD_DOES_NOT_MATCH':
          case 'NO_SUCH_USER':
            errorMessage = meta.errorName;
            break;
          default:
            errorMessage = errorMessage;
        }
        return throwError(errorMessage);
      });
  }

I think you need to change your guard

export class AuthGuard implements CanActivate {
  constructor(private authService: AuthService, private router: Router) {}
  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> {
    return this.authService.user.pipe(
      take(1),
      map((user) => {
        const isAuth = !!user;
        if (isAuth) {
          return true;
        } esle {
          this.router.navigate(['/login']) <==== HERE
          return false
        }
      })
    );
  }
}

And add in your routes

...
canActivate: [AuthGuard],    
canActivateChild: [AuthGuard]
...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM