简体   繁体   English

带重定向的 Angular 2 AuthGuard 服务?

[英]Angular 2 AuthGuard Service with redirect?

I have an application that I am building that implements CanActivate on the dashboard route.我有一个正在构建的应用程序,它在仪表板路由上实现了 CanActivate。 It works fine except on page reload, I check a flag in the user service to see if a user is logged in or not.除了页面重新加载外,它工作正常,我检查用户服务中的标志以查看用户是否已登录。 By default this flag is false which kicks the user out to login.默认情况下,此标志为 false,它会将用户踢出登录。 Also on page reload I am trying to fetch user data with a token in localStorage, if fetch is successful, I want them to be able to stay on the dashboard.同样在页面重新加载时,我尝试使用 localStorage 中的令牌获取用户数据,如果获取成功,我希望它们能够留在仪表板上。 The problem is that I am seeing a glimpse of login and having to manually redirect them to the dashboard.问题是我看到了登录的一瞥,不得不手动将它们重定向到仪表板。 Is there any way to fix this to where the authGuard doesn't do anything until after it checks the API?有什么方法可以解决这个问题,直到 authGuard 在检查 API 之后才执行任何操作? Code is here: https://github.com/judsonmusic/tfl代码在这里: https : //github.com/judsonmusic/tfl

dashboard:仪表盘:

import { Component, ViewChild } from '@angular/core';
import { LoginComponent } from "../login.component";
import { UserService } from "../user.service";
import { SimpleChartComponent } from "../charts/simpleChart.component";
import { AppleChartComponent } from "../charts/appleChart.component";
import { BarChartComponent } from "../charts/barChart.component";
import { DonutChartComponent } from "../charts/donutChart.component";
import { AlertComponent } from 'ng2-bootstrap/ng2-bootstrap';
import { ModalDemoComponent } from "../modals/modalDemoComponent";
import { NgInitHelperComponent } from "../helpers/nginit.helper.component";
import { ModalDirective } from "ng2-bootstrap/ng2-bootstrap";
import { MODAL_DIRECTIVES, BS_VIEW_PROVIDERS } from 'ng2-bootstrap/ng2-bootstrap';


@Component({
  selector: 'dashboard',
  templateUrl: '/app/components/dashboard/dashboard.component.html',
  providers: [UserService, BS_VIEW_PROVIDERS],
  directives: [SimpleChartComponent, AppleChartComponent, BarChartComponent, DonutChartComponent, AlertComponent, ModalDemoComponent, NgInitHelperComponent, ModalDirective]
})
export class DashboardComponent  {

  public areas: any;

  constructor() {

    this.areas = [
      "Spiritual",
      "Habits",
      "Relationships",
      "Emotional",
      "Eating Habits",
      "Relaxation",
      "Exercise",
      "Medical",
      "Financial",
      "Play",
      "Work/ Life Balance",
      "Home Environment",
      "Intellectual Well-being",
      "Self Image",
      "Work Satisfaction"
    ]

  }
}

Routes:路线:

import { Routes, RouterModule } from '@angular/router';
import { AboutComponent } from './components/about.component';
import { PageNotFoundComponent } from "./components/pageNotFound.component";
import { HomeComponent } from "./components/home.component";
import { DashboardComponent } from "./components/dashboard/dashboard.component";
import { SurveyComponent } from "./components/survey/survey.component";
import { ResourcesComponent } from "./components/resources.component";
import { LogoutComponent } from "./components/logout.component";
import { AuthGuard } from "./components/auth-guard.service";
import { loginRoutes, authProviders } from './login.routing';
import { LoginComponent } from "./components/login.component";

const appRoutes:Routes = [
  { path: '', component: HomeComponent },
  { path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuard] },
  { path: 'logout', component: LogoutComponent },
  { path: 'resources', component: ResourcesComponent },
  { path: 'survey', component: SurveyComponent },
  { path: 'about', component: AboutComponent },
  { path: 'login', component: LoginComponent },
  { path: '**', component: PageNotFoundComponent }
];

export const appRoutingProviders: any[] = [
  authProviders
];
export const routing = RouterModule.forRoot(appRoutes);

login route:登录路径:

import { Routes }         from '@angular/router';
import { AuthGuard }      from './components/auth-guard.service';
import { AuthService }    from './components/auth.service';
import { LoginComponent } from './components/login.component';
export const loginRoutes: Routes = [
  { path: 'login', component: LoginComponent }
];
export const authProviders = [
  AuthGuard,
  AuthService
];

In AuthGuard do the following:AuthGuard执行以下操作:

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private authService: AuthService, private router: Router) {}

  canActivate() {
    if (/*user is logged in*/) {
      this.router.navigate(['/dashboard']);
      return true;
    } else {
      this.router.navigate(['/Login']);
    }
    return false;
  }
}

Here's how to correctly handle redirects in a guard by using an UrlTree以下是如何使用 UrlTree 正确处理警卫中的重定向

@Injectable({
  providedIn: 'root'
})
export class AuthGuard implements CanActivateChild {
  constructor(
    private authService: AuthService,
    private logger: NGXLogger,
    private router: Router
  ) {}

  canActivateChild(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean | UrlTree> {

    return this.authService.isLoggedIn().pipe(
      map(isLoggedIn => {
        if (!isLoggedIn) {
          return this.router.parseUrl('/login');
        }

        return true;
      })
    );
  }
}

Big thanks to Angular In Depth for the explanation!非常感谢Angular In Depth的解释!

You can now return a UrlTree from an AuthGuard, or a boolean true / false.您现在可以从 AuthGuard 或布尔值 true / false 返回UrlTree

Kind of amazed nobody has mentioned this yet!有点惊讶还没有人提到这个! Sorry no example right now, but the idea is pretty simple.对不起,现在没有例子,但这个想法很简单。

I actually changed my service to this and it works:我实际上将我的服务更改为这个并且它有效:

import { Injectable }             from '@angular/core';
import { CanActivate, Router,
ActivatedRouteSnapshot,
RouterStateSnapshot }    from '@angular/router';
import { AuthService }            from './auth.service';
import {UserService} from "./user.service";

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private authService: AuthService, private router: Router, private userService: UserService) {}

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {



    if (this.authService.isLoggedIn){
      console.log('ATUH GUARD SAYD THEY ARE ALREADY LOGGED IN!');
      return true;


    }else {


      this.userService.getUser().subscribe((user) => {

        console.log('AUTH GUARD GETTING USER', user);

        if (user._id) {
        this.authService.isLoggedIn = true;
        // Store the attempted URL for redirecting
        this.authService.redirectUrl = state.url;
        this.router.navigate(['/dashboard']);
        return true;
        }else{
          console.log('Validation Failed.');
          localStorage.clear();
          this.router.navigate(['/login']);
          return false;
        }


      }, (error) => {
        console.log('There was an error.');
        this.router.navigate(['/login']);
        return false

      });

    }


  }
}

I solved it like this and used it in my AuthGuard我是这样解决的,并在我的 AuthGuard 中使用它

isLoggedIn(): Observable<boolean> {
return this.afAuth.authState
  .pipe(
    take(1),
    map(user => {
        return !!user;
      },
      () => {
        return false;
      }
    ),
    tap(loggedIn => {
        if (!loggedIn) {
          this.router.navigate(['/']);
        }
      }
    ));
}

This is what I did for canActivate这就是我为canActivate

  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    // ensure the user is properly logged in and the url he's requesting is within his right
    if (this.authSvc.getRole().trim().length > 0 && this.authSvc.getToken().trim().length > 0
      && state.url.includes(this.authSvc.getRole().trim())) {
      let url: string;
      // base on user's role send to different url to check
      if (this.authSvc.getRole().trim() === 'client') {
        url = ClientAccessRequestUrl;
      } else {
        url = AdminAccessRequestUrl;
      }
      return this.http.post<AccessRequestResponse>(url, {
        token: this.authSvc.getToken(),
      }).pipe(map(response => {
        console.log('response is:', response);
        // check successful then let go
        if (response.reply === true) {
          return true;
          // unless go to visitor site
        } else {
          return this.router.createUrlTree(['/visitor']);
        }
      }));
    } else {
      return this.router.createUrlTree(['/visitor']);
    }
  }

The best way to do redirects after authentication is structuring the logic as shown below;身份验证后进行重定向的最佳方法是构建如下所示的逻辑;

  1. in the AuthGuard,在 AuthGuard 中,
canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree 
    {

      // keep the requsted url for redirect after login
      let url: string = state.url;

      // call the authentication function
      var authenticated = this.http.isAuthenticated();
      
      var subject = new Subject<boolean>();

      authenticated.subscribe(

      (res) => {
        //if not authenticated redirect to the login route with the initial route attached as an query param 'returnUrl'
        if(!res.successState) {
          this.router.navigate(['/login'], {queryParams: {returnUrl: url}});
        }else{

        // the user is authenticated just go to the requested route

          subject.next(res.successState);

        }

      });

      return subject.asObservable();

    }


  1. in the login route在登录路径中
  loginAction(data: any){
    // if the auth works fine the go the route requested before the inconviniences :)
    if(data.successState){

    // get the query params to see if there was a route requested earlier or they just typed in the login route directly
    this.route.queryParams.subscribe(params => {
      // console.log(params); //returnUrl

      if(params.returnUrl){

        // pearse the url to get the path and query params
        let urlTree: UrlTree = this.router.parseUrl(params.returnUrl);
        let thePathArray : any[] = [];
        // populate it with urlTree.root.children.primary.segments[i].path;
        for(let i = 0; i < urlTree.root.children.primary.segments.length; i++){
          thePathArray.push(urlTree.root.children.primary.segments[i].path);
        }
        let the_params = urlTree.queryParams;

        this.router.navigate(thePathArray, {queryParams: the_params});

      }else{

        this.router.navigate(['']);

      }
    });

    }else{
    // tell them about it and let them try again or reset the password 
    }

 }

That should work perfectly.那应该可以完美运行。 it will even preserve query params for the initial request.它甚至会保留初始请求的查询参数。

THANKS谢谢

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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