【问题标题】:Angular 2 AuthGuard Service with redirect?带有重定向的Angular 2 AuthGuard服务?
【发布时间】:2016-08-17 16:52:02
【问题描述】:

我正在构建一个在仪表板路由上实现 CanActivate 的应用程序。它工作正常,除了页面重新加载,我检查用户服务中的标志以查看用户是否登录。默认情况下,此标志为 false,将用户踢出登录。同样在页面重新加载时,我试图在 localStorage 中使用令牌获取用户数据,如果获取成功,我希望他们能够留在仪表板上。问题是我看到了登录的一瞥,并且不得不手动将它们重定向到仪表板。有没有办法解决这个问题,直到它检查 API 之后 authGuard 才做任何事情?代码在这里:https://github.com/judsonmusic/tfl

仪表板:

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"
    ]

  }
}

路线:

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);

登录路径:

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
];

【问题讨论】:

    标签: javascript angular


    【解决方案1】:

    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;
      }
    }
    

    【讨论】:

    • 有没有办法从路由器中提取预期的路由,而不是硬编码'/dashboard'?
    • @Kirby 返回真,不重定向
    • @Kirby 为此,请参阅question
    • 当守卫解析为真时,最好让重定向逻辑远离。这是因为如果您在仪表板以外的其他路线上也应用了身份验证保护,那么刷新页面会将您重定向到仪表板。
    【解决方案2】:

    以下是如何使用 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;
          })
        );
      }
    }
    

    非常感谢Angular In Depth 的解释!

    【讨论】:

      【解决方案3】:

      您现在可以从 AuthGuard 返回 UrlTree,或布尔值 true / false。

      有点惊讶还没有人提到这个!抱歉,目前没有示例,但想法很简单。

      【讨论】:

      • 一个更完整的例子会很棒。链接的文章有一个简单的“authGuard 作为重定向”示例,但试图将其与现有的 Angular 项目 (8) 集成,我的根 url 只是无限旋转,显然是在某个循环中。显然问题出在我头上了。
      • CanActivate 的例子有很多,除了你返回一个 URL 树之外没有什么不同。当你说旋转时,你认为你得到了一个无限循环吗?如果你在路由守卫中放置一个 console.log 语句,它会被打印多少次?
      • 是的,它进入了一个无限循环,您对控制台语句的建议通过帮助我了解正在发生的事情得到了回报,但我无法让警卫正常工作,所以目前我绕过它。我会检查一些最新版本的 canActivate 示例。
      • 听起来你可能把 CanActivate 放在了错误的地方或两个地方。每次访问该 URL 时它都会运行,如果您重定向到原始 URL 的子 URL,它将再次运行(它们是继承的)。我认为仅在授权失败时才使用返回树,因此根据定义,如果您重定向到触发相同的 CanActivate,它将再次失败。
      • 这是有道理的。我正在使用它来取得成功,昨晚我突然想到它可能被用作某种子路由器。听从你的建议,我离得更近了。 Success 现在只返回 true,并且无限循环消失了,但它也不会继续到 authGaurd 作为 canActivate 处理程序的路径。我现在可以通过检查用户令牌的目标并在不存在时重新路由到登录路径来解决这个问题,所以它不是一个显示停止器。可能我有一些其他代码导致了这个问题。
      【解决方案4】:

      我实际上将我的服务更改为这个并且它有效:

      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
      
            });
      
          }
      
      
        }
      }
      

      【讨论】:

      • 这一点都不好抱歉!您必须返回布尔值或可观察值。您不能订阅某些内容,然后在该订阅中返回 true/false,因为它不会去任何地方。这可能有点用,但您没有正确使用 AuthGuard。相反,您应该 return userService.getUser().pipe(...) 并且可以在内部使用 tapswitchMap 来运行您的逻辑。
      【解决方案5】:

      我这样解决了它并在我的 AuthGuard 中使用它

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

      【讨论】:

        【解决方案6】:

        这就是我为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']);
            }
          }
        

        【讨论】:

          【解决方案7】:

          身份验证后进行重定向的最佳方法是构造如下所示的逻辑;

          1. 在 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. 在登录路径中
            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 
              }
          
           }
          
          

          这应该可以完美运行。它甚至会保留初始请求的查询参数。

          谢谢

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2020-06-20
            • 1970-01-01
            • 2021-03-19
            • 1970-01-01
            • 2017-03-17
            • 1970-01-01
            • 2017-01-22
            • 1970-01-01
            相关资源
            最近更新 更多