繁体   English   中英

将 Bearer 令牌传递给不同的 APP_INITIALIZER 以从 Angular App 中的服务器加载配置

[英]Passing Bearer token to a different APP_INITIALIZER to load config from server in Angular App

我经历了几个问题,例如12 ,但我不知道如何使我的应用程序正常工作。

问题:当我第一次登录时,我没有得到Bearer token ,因此我的SettingConfigService失败并显示401 ,如果我刷新页面,我会从this.oauth.getAccessToken()获取令牌,因为现在令牌在localstorage中。

我正在使用oauth 库进行登录。 这是我创建的模块和库。

应用模块


export function AppConfigurationFactory(config: SettingConfigService) {
  return async () => await config.ensureInit(APP_NAME);
}

export class AppConfig {
  baseUrl: string;
  production: boolean;
}

export const appConfig: AppConfig = {
  baseUrl: environment.baseUrl,
  production: environment.production,
};

@NgModule({
  exports: [],
  declarations: [

  ],
  imports: [
     ....
    CustomAuthModule.forRoot(environment.keycloak),
    CustomInfrastructureModule.forRoot({ appConfig }),
    SharedModule,
  ],
  providers: [
    { provide: AppConfig, useValue: appConfig }, 
    ...
    {
      provide: APP_INITIALIZER,
      useFactory: AppConfigurationFactory,
      deps: [ SettingConfigService, HttpClient, TranslateService, OAuthService],
      multi: true,
    },
  ],
})
export class AppModule {}

CustomAuthModule.ts

import { NgModule, APP_INITIALIZER, Optional, SkipSelf, ModuleWithProviders, InjectionToken } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { OAuthModule, AuthConfig } from 'angular-oauth2-oidc';
import { OAuthModuleConfig,CustomAuthConfigParams } from './auth.config';
import { AuthConfigService } from './auth.service';

export function init_app(authConfigService: AuthConfigService) {
  return () => authConfigService.initAuth();
}

@NgModule({
  imports: [HttpClientModule, OAuthModule.forRoot()]
})
export classCustomAuthModule {
  constructor(@Optional() @SkipSelf() parentModule:CustomAuthModule){
    if(parentModule){
        throw new Error('QontrolAuthModule is already loaded.');
    }
  }

  static forRoot(keycloakParams): ModuleWithProviders<QontrolAuthModule> {
    return {
      ngModule:CustomAuthModule,      
      providers: [ 
        AuthConfigService,
        OAuthModuleConfig,
        { 
          provide: AuthConfig, 
          useValue: keycloakParams
        },
        {
          provide: APP_INITIALIZER,
          useFactory: init_app,
          deps: [AuthConfigService],
          multi: true,
        }, ]
    }
  }
}

CustomInfrastructureModule.ts

@NgModule({
  declarations: [],
  imports: [CommonModule],
  exports: [],
  providers: [],
})
export class CustomInfrastructureModule {

  static forRoot(conf?: {
    appConfig: SharedInfrastructureAppConfig;
  }): ModuleWithProviders<CustomInfrastructureModule> {
    return {
      ngModule: CustomInfrastructureModule,
      providers: [
        { provide: APP_CONFIG, useValue: conf.appConfig },
        {
          provide: LOCALE_ID,
          deps: [SettingConfigService], // some service handling global settings
          useFactory: (config: SettingConfigService) => config.culture
        },
      ],
    };
  }
}

设置配置服务


@Injectable({providedIn: 'root'})
export class SettingConfigService {
  culture: string;
  config: any;

  constructor(
    private httpClient: HttpClient,
    @Inject(APP_CONFIG) protected appConfig: SharedInfrastructureAppConfig,
    private oauth: OAuthService
  ) { }

  async ensureInit(clientPrefix: string): Promise<void>{
    console.log(this.oauth.getAccessToken());  //<-- comes as null when 1st login    
   // putting a wait time of 1 sec as per https://stackoverflow.com/questions/951021/what-is-the-javascript-version-of-sleep makes it work,
     // because by that time, we have the token in localStorage
    const response = await this.httpClient.get<any>(`${this.appConfig.baseUrl}/configs`).toPromise();
    this.config = response;
  }
}

这是我的代码,它使用调用的oauth2-oidc获取令牌

AuthConfigService

  async initAuth(): Promise<any> {
    return new Promise((resolveFn, rejectFn) => {
      this.oauthService.configure(this.authConfig);
      // Redirect to path, if there is one
      if (window && window.location && window.location.pathname) {
        this.oauthService.redirectUri = window.location.origin + window.location.pathname;
      }

      this.oauthService.setStorage(localStorage);
      this.oauthService.tokenValidationHandler = new NullValidationHandler();

      this.oauthService.events
        .pipe(
          filter((e: any) => {
            return e.type === 'token_received';
          })
        )
        .subscribe(() => 
           this.handleNewToken() // <-- this takes time to get triggered and meanwhile
                       // the call to SettingConfigService is made
        );

      this.oauthService.loadDiscoveryDocumentAndLogin().then((isLoggedIn) => {
        if (isLoggedIn) {
          this.oauthService.setupAutomaticSilentRefresh();
          resolveFn(() => {});
        } else {
          console.log(this.oauthService.getAccessToken())
          this.oauthService.initImplicitFlow();
          console.log(this.oauthService.getAccessToken())
          rejectFn();
        }
      });
    });
  }

简而言之,

我需要同步APP_INITIALIZERapp.module.ts以等待CustomAuthModuleAPP_INITIALIZER令牌,然后它会有一个不记名令牌(由拦截器添加)。 我的理解正确吗? 请帮忙

您需要按照正确的顺序使用令牌加载配置。 尝试:

应用程序模块

@NgModule({
  exports: [],
  declarations: [

  ],
  imports: [
     ....
    CustomAuthModule.forRoot(environment.keycloak, clientPrefixConstant),
    CustomInfrastructureModule.forRoot({ appConfig }),
    SharedModule,
  ],
  providers: [
    { provide: AppConfig, useValue: appConfig }, 
    ...
  ],
})
export class AppModule {}

在 AuthModule 中


export const CLIENT_NAME = new InjectionToken<string>('CLIENT_PARAM');

export function init_app(authConfigService: AuthConfigService,
    settingSvc: SettingConfigService,
    clientPrefix: string 
   ) {
  return () => authConfigService.initAuth().then(async () => {
      await settingSvc.ensureInit(clientName);
    });
}

@NgModule({
  imports: [HttpClientModule, OAuthModule.forRoot()]
})
export classCustomAuthModule {
  ....

  static forRoot(keycloakParams, clientPrefix): ModuleWithProviders<QontrolAuthModule> {
    return {
      ngModule:CustomAuthModule,      
      providers: [ 
        AuthConfigService,
        OAuthModuleConfig,
        { 
          provide: AuthConfig, 
          useValue: keycloakParams
        },
        {
          provide: CLIENT_NAME,
          useValue: clientPrefix
        },
        {
          provide: APP_INITIALIZER,
          useFactory: init_app,
          deps: [AuthConfigService,SettingConfigService,CLIENT_NAME],
          multi: true,
        }, ]
    }
  }
}

您的代码中发生的情况是两个APP_INITIALIZER都被并行调用,这导致令牌不可用。 使用我建议的答案,您首先解析令牌,然后调用ensureInit 使用InjectionToken ,我接受了调用 function 所需的string值。

暂无
暂无

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

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