【问题标题】:Force Angular app to redirect when session expired会话过期时强制 Angular 应用重定向
【发布时间】:2021-05-29 10:24:00
【问题描述】:

我有一个需要来自 OIDC 服务的会话授权的 Angular 应用程序。使用正常/快乐路径,单独的 NodeJS/Express 应用程序检查会话授权并重定向到 OIDC 授权/身份验证服务并附加相关标头。如果一切顺利,中间件将路由到 Angular 应用程序。

尽管如此,Angular 应用程序会使用过期的令牌运行。 Angular 应用程序获取用户名,此时我可以检查是否过期。但是,如果它已过期,我需要 Angular 的方式通过将整个 Angular 应用程序重新路由到相关的中间件页面来对错误情况做出反应。因为指示器将转到嵌套在应用程序中的组件,所以我不知道如何让小组件重定向更大/包含的应用程序。

我不是 Angular 人,所以我什至不知道要查找 Angular 的哪个部分。由于我缺乏知识,我将包含可能相关或不相关的源文件。

header.component.ts 是它获取用户名并可能返回错误令牌指示符的位置

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

@Component({
  selector: 'header-comp',
  templateUrl: './header.component.html',
  styleUrls: ['./header.component.css']
})
export class HeaderComponent implements OnInit {
  userName: string = 'No Session';

  constructor(private profileService: ProfileService) { }

  ngOnInit() {
    this.onDisplayUserLogged();
  }

  onDisplayUserLogged() {
    this.profileService.getUsername().subscribe(data => {
      this.userName = 'Welcome: ' + data;
    });
  }
}


app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { APP_BASE_HREF } from '@angular/common';
import { Router } from '@angular/router';

//Services
import { ProfileService } from './core/services/profile.service';
import { FinanceService } from './core/services/finance.service';
import { CmasService } from './core/services/cmas.service';
import { SoftlayerService } from './core/services/softlayer.service';
import { DashboardService } from './core/services/dashboard.service';
import { LowDollarExceptionService } from './core/services/lowDollarException.service';

//Components
import { AppComponent } from './app.component';
import { CmasComponent } from './modules/cmas/cmas.component';
import { SoftlayerComponent } from './modules/softlayer/softlayer.component';
import { FinanceComponent } from './modules/finance/finance.component';
import { ErrorComponent } from './modules/error/error/error.component';
import { LowDollarExceptionComponent } from './modules/lowDollarException/lowDollarException.component'
import { MenuComponent } from './shared/layout/menu/menu.component';
import { BasicComponent } from './shared/modals/basic/basic.component';
import { HeaderComponent } from './shared/layout/header/header.component';
import { PreProcessedPipe } from './shared/components/file-listing/file-listing.component';
import { ProcessedPipe } from './shared/components/softlayer-file-list/softlayer-file-list.component';

//Routing
import { AppRoutingModule } from './app-routing.module';

//Libs
import { AuthInterceptor } from 'auth-lib';
import { TermsComponent } from './modules/terms/terms.component';
import { FileListingComponent } from './shared/components/file-listing/file-listing.component';
import { ConfirmCancelComponent } from './shared/modals/confirm-cancel/confirm-cancel.component';
import { ErrorDisplayComponent } from './shared/components/error-display/error-display.component';
import { AboutComponent } from './modules/about/about.component';
import { ResultsComponent } from './modules/cmas/results/results.component';
import { SoftlayerFileListComponent } from './shared/components/softlayer-file-list/softlayer-file-list.component';
import { DashboardComponent } from './modules/dashboard/dashboard.component';
import { ReviewResultsComponent } from './modules/softlayer/review-results/review-results.component';
import { InvoiceDetailsComponent } from './modules/dashboard/invoice-details/invoice-details.component';
import { FormsModule } from '@angular/forms';
import { LowDollarNewRecordComponent } from './modules/lowDollarException/lowDollarNewRecord.component';

@NgModule({
  declarations: [
    AppComponent,
    CmasComponent,
    FinanceComponent,
    ErrorComponent,
    MenuComponent,
    BasicComponent,
    HeaderComponent,
    TermsComponent,
    FileListingComponent,
    PreProcessedPipe,
    ProcessedPipe,
    ConfirmCancelComponent,
    ErrorDisplayComponent,
    AboutComponent,
    ResultsComponent,
    SoftlayerComponent,
    SoftlayerFileListComponent,
    DashboardComponent,
    ReviewResultsComponent,
    InvoiceDetailsComponent,
    LowDollarExceptionComponent,
    LowDollarNewRecordComponent
  ],
  imports: [BrowserModule, HttpClientModule, AppRoutingModule, FormsModule],
  providers: [
    ProfileService,
    FinanceService,
    CmasService,
    SoftlayerService,
    DashboardService,
    LowDollarExceptionService,
    {
      provide: HTTP_INTERCEPTORS,
      useClass: AuthInterceptor,
      deps: [Router],
      multi: true,
    },
    { provide: APP_BASE_HREF, useValue: '/sprint-cost-recovery' },
  ],
  bootstrap: [AppComponent],
})
export class AppModule {}

app.component.ts

import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import listadeTerms from '../assets/config/properties.json';

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

  ngOnInit() {
    this.openOverlay();
  }

  openOverlay() {
    var sheet = document.createElement('style');
    sheet.innerHTML =
      '.ds-full-width {visibility: hidden;} .usabilla_live_button_container{visibility: hidden;}';
    document.body.appendChild(sheet);
    if (document.getElementById('termsDialog')) {
      var overlayElement = document.querySelector('#termsDialog');
      overlayElement.classList.add('ds-open');
      document
        .querySelector('#termsDialogCloseBtn')
        .addEventListener('click', load);
    }
    function load() {
      var div = document.getElementById('termsDialog');
      sheet.innerHTML = '.usabilla_live_button_container{visibility: true;}';
      var parent = div.parentElement;
      parent.removeChild(div);
      var node = sheet.parentNode;
      node.removeChild(sheet);
    }
  }
}

app-routing.module.ts

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

// Components
import { CmasComponent } from './modules/cmas/cmas.component';
import { FinanceComponent } from './modules/finance/finance.component';
import { ErrorComponent } from './modules/error/error/error.component';
import { AboutComponent } from './modules/about/about.component';
import { SoftlayerComponent } from './modules/softlayer/softlayer.component';
import { DashboardComponent } from './modules/dashboard/dashboard.component';
import { LowDollarExceptionComponent } from './modules/lowDollarException/lowDollarException.component';
import { LowDollarNewRecordComponent } from './modules/lowDollarException/lowDollarNewRecord.component';

const appRoutes: Routes = [
  { path: 'finance', component: FinanceComponent },
  { path: 'cmas-process', component: CmasComponent },
  { path: 'about', component: AboutComponent },
  { path: 'softlayer-process', component: SoftlayerComponent },
  { path: 'cost-dashboard', component: DashboardComponent },
  { path: 'low-dollar', component: LowDollarExceptionComponent },
  { path: 'low-dollar/create', component: LowDollarNewRecordComponent },
  { path: 'error/auth', component: ErrorComponent, data: { forbidden: true } },
  { path: '', redirectTo: '/finance', pathMatch: 'full' },
  {
    path: 'error/badgateway',
    component: ErrorComponent,
    data: { badgateway: true },
  },
];

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

【问题讨论】:

    标签: node.js angular express openid-connect


    【解决方案1】:

    在 Angular 中实现这一点的方法是通过 Route Guards。在高层次上,它是这样的:

    1. 创建route guards。它根据检查的条件返回真或假。

    2. 将canActivate参数添加到需要检查身份验证的路由中,例如:

    const appRoutes: Routes = [
      { path: 'finance', component: FinanceComponent, canActivate: [AuthGuard] },
      { path: 'cmas-process', component: CmasComponent, canActivate: [AuthGuard] },
    
    1. 如果用户未通过身份验证,则在路由保护中指定重定向(例如到登录页面):
    @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;
          })
        );
      }
    }
    

    【讨论】:

    • 我将上面的答案标记为正确。我得到了我希望的粗略想法。但我必须问一个后续问题。如果我在单独的 Node/Express 服务器上使用 Passport 进行身份验证,我应该包含哪个 Angular 包来检查 isAuthenticated() 然后重定向到登录页面?
    • 为此,在较高级别上,您将创建一个服务,该服务进行 HTTP 调用以从快速服务器获取响应,并相应地返回 isAuthenticated() 中的值。有许多文章可用于“角度登录示例”@Woodsman
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-10-16
    • 1970-01-01
    • 1970-01-01
    • 2015-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多