将承载令牌传递给不同的 APP_INITIALIZER 以从 Angular 应用程序中的服务器加载配置

2023-12-07

我经历了几个问题,比如1 , 2但我不知道如何让我的应用程序运行。

Problem:当我第一次登录时,我没有得到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,
        }, ]
    }
  }
}

自定义基础设施模块.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这是从调用的

认证配置服务

  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_INITIALIZER of app.module.ts等待令牌APP_INITIALIZER of CustomAuthModule,然后它将有一个不记名令牌(由拦截器添加)。我的理解正确吗?请帮忙


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

应用程序模块

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

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

在 Auth 模块中


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并行调用,导致token不可用。根据我建议的答案,您首先解析令牌,然后调用ensureInit. With InjectionToken,我接受了string调用该函数所需的值。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

将承载令牌传递给不同的 APP_INITIALIZER 以从 Angular 应用程序中的服务器加载配置 的相关文章

随机推荐

  • Flutter 在本机应用程序中打开本地资源(PDF)

    我正在尝试将我的应用程序与 PDF 捆绑在一起 并让用户在本机查看器中打开它 我努力了 将 PDF 的数据复制到 临时目录 或 文档目录 来自 path provider 并从那里打开 要打开 我使用 url launcher 打开文件 我
  • 使用 angular2 限制输入字段的长度

    我已经实现了使用 angular2 限制输入字段的指令 它在桌面浏览器中工作正常 但在 Android 移动设备中无法工作 成分 import LimitToDirective from directives limitedvalidati
  • 按两个变量组进行汇总

    考虑一个简化的数据集 真实的数据集有更多的列和行 df tp tf weight 1 FWD RF 78 86166 2 MF LF 81 04566 3 DEF LF 80 70527 4 DEF LF 82 96071 5 DEF RF
  • 如何将position_dodge()和geom_line()与重叠分组结合起来?

    当 x 轴上的分组变量 颜色和线条重叠但总是不同时 是否可以使用 geom line 在躲避点之间绘制一条线 下图中的灰线应该始终连接两个具有相同名称的数据点 它们位于相同的分组 2 x 轴 内 但位于不同的分组 2 颜色 中 可重现的例子
  • 如何在silverstripe数据扩展中自动发布图像

    我试图将上传字段添加到自定义数据扩展并让图像字段正常工作 但是 我上传的图像仍处于概念模式 我必须转到 文件 选项卡才能发布它 我尝试使用 Silverstripe 文档中提供的代码 但这似乎只适用于常规页面 我发现了一个和我类似的问题 如
  • 在 .htaccess 中使用自定义环境变量

    我需要这样的东西 SetEnv foo bar baz RewriteEngine On RewriteCond HTTP HOST foo RewriteRule http www foo 1 L UPD 我做了如下 SetEnv HOS
  • Struts 2 文件上传 - 空指针异常

    我正在尝试结合使用 Struts2 和 Spring 来上传文件 但是 不知怎的 在到达我的动作课 我的文件之后 filename和文件内容类型都出来了null 我尝试寻找问题所在 但没有结果 下面是我的文件上传的代码 index jsp
  • 如何使用 Mockito 和 JUnit 检查方法中的 if 语句?

    我有我应该测试的方法 代码 当然有些部分被删掉了 public class FilterDataController public static final String DATE FORMAT yyyy MM dd Autowired p
  • 在docker下安装时可以回答对话框问题吗?

    是否可以以某种方式回答使用 apt get 安装某些软件包时以对话框形式出现的问题 例如 我正在尝试设置一个包含以下内容的容器mail stack delivery包装内含 FROM ubuntu RUN apt get install y
  • 如何在android中使用sax解析器从xml读取imageUrl在网格视图中显示图像

    我是安卓新手 我想创建一个应用程序来从 URL 读取 XML 文件并使用图像的 ImageUrl 在网格视图中显示图像 感谢您的回答 但我可以从 url 读取 xml 文件 但我需要 xml imageUrl 是否存在 以便在网格视图中显示
  • 计算原始输入中的元音数

    我有一个家庭作业问题 要求通过原始输入读取字符串并计算字符串中有多少个元音 这是我到目前为止所拥有的 但我遇到了一个问题 def vowels vowels a e i o u count 0 string raw input Enter
  • 运行“sudo pip”有哪些风险?

    偶尔我会遇到评论或回应该状态强调运行pip under sudo是 错误 或 坏 但在某些情况下 包括我设置一堆工具的方式 它要么更简单 要么甚至有必要以这种方式运行 跑步有哪些风险pip under sudo 请注意 这与以下问题不同th
  • 在定义整个映射之前,如何引用映射中的变量?

    我将从我的代码开始 因为它应该更容易理解我想要做什么 function get color color lightness return map get map get colors color lightness colors green
  • Thread.MemoryBarrier 和简单属性的锁区别

    对于以下场景 有什么区别吗关于使用之间的线程安全性 结果和性能MemoryBarrier private SomeType field public SomeType Property get Thread MemoryBarrier So
  • 在 Python 中使用 pynput 检查特定键

    dpressed 0 def on press key if key d global dpressed dpressed 1 logging info D s dpressed 当我运行此代码并按 d 时 没有任何反应 我怀疑这是因为在检
  • Python - isnull().sum() 与 isnull().count()

    所以我目前正在完成泰坦尼克号数据集的教程 https www kaggle com c titanic data 现在我正在尝试一些可能相关的新事物 The info for it is There are 891 entries red
  • 如何在java中进行链式检查

    考虑一个像下面这样的类 public class MyClass private Integer myField private Result result more global variables public MyResult che
  • 尝试使用 Nunit 运行多个 Selenium Webdriver 测试失败

    我想知道是否有人可以在这里帮助我 我将 selenium Webdriver 与 C 和 Nunit 结合使用 当我尝试运行多个测试时 出现以下错误 OpenQA Selenium WebDriverException 意外错误 Syste
  • 实时更新 MySQL 数据

    我有以下代码 它工作得很好 我只是想将其转换为实时状态 以便它每 10 秒左右更新一次 而无需刷新页面 我猜我需要使用 AJAX 或 Jquery 但我缺乏这方面的知识如何做到这一点 VIA ON THE PAGE
  • 将承载令牌传递给不同的 APP_INITIALIZER 以从 Angular 应用程序中的服务器加载配置

    我经历了几个问题 比如1 2但我不知道如何让我的应用程序运行 Problem 当我第一次登录时 我没有得到Bearer token因此我的SettingConfigService失败了401 如果我刷新页面 我会从以下位置获取令牌this