获取循环依赖项错误 [英] Getting a cyclic dependency error

查看:117
本文介绍了获取循环依赖项错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 angular-jwt ,但我得到了关注者循环依赖错误:

I'm trying to make use of the angular-jwt but I get the follower cyclic dependency error:

Cannot instantiate cyclic dependency! InjectionToken_JWT_OPTIONS
  at NgModuleProviderAnalyzer.parse (compiler.js:19550)
    at NgModuleCompiler.compile (compiler.js:20139)
    at JitCompiler._compileModule (compiler.js:34437)
    at eval (compiler.js:34368)
    at Object.then (compiler.js:474)
    at JitCompiler._compileModuleAndComponents (compiler.js:34366)
    at JitCompiler.compileModuleAsync (compiler.js:34260)
    at CompilerImpl.compileModuleAsync (platform-browser-dynamic.js:239)
    at PlatformRef.bootstrapModule (core.js:5567)
    at eval (main.ts:13)

在我的第一个演示Angular 5应用程序中,这是我的设置:

In my first demo Angular 5 application, here is my setup:

import {NgModule} from '@angular/core';
import {HttpClientModule} from '@angular/common/http';
import {JwtModule, JWT_OPTIONS} from '@auth0/angular-jwt';

import {KeycloakService} from './keycloak.service';

// See https://github.com/auth0/angular2-jwt

export function jwtOptionsFactory(keycloakService) {
  return {
    whitelistedDomains: ['localhost:3001'],
    blacklistedRoutes: ['localhost:3001/auth/'],
    tokenGetter: () => {
      return keycloakService.getJwtTokenFromLocalStorage();
    }
  };
}

@NgModule({
  imports: [
    HttpClientModule,
    JwtModule.forRoot({
      jwtOptionsProvider: {
        provide: JWT_OPTIONS,
        useFactory: jwtOptionsFactory,
        deps: [KeycloakService]
      }
    })
  ],
  providers: [
    KeycloakService
  ]
})
export class AuthModule {}

使用空的deps:属性可以解决此问题.当然,没有服务注入也不是解决方案.

Having an empty deps: property removes the issue. Of course, no service being injected is no solution either.

我的服务是:

import {Injectable} from '@angular/core';
import {environment} from '../environments/environment';
import {Observable} from 'rxjs';
import {JwtHelperService} from '@auth0/angular-jwt';
import {HttpClient, HttpHeaders} from '@angular/common/http';

declare let Keycloak: any;

const JWT_TOKEN_NAME: string = 'token';

@Injectable()
export class KeycloakService {

  static auth: any = {};

  constructor(private httpClient: HttpClient, private jwtHelperService: JwtHelperService) {}

  static init(): Promise<any> {
    const keycloakAuth: any = Keycloak({
      url: environment.KEYCLOAK_URL,
      realm: environment.KEYCLOAK_REALM,
      clientId: environment.KEYCLOAK_CLIENTID,
      'ssl-required': 'external',
      'public-client': true
    });

    KeycloakService.auth.loggedIn = false;

    return new Promise((resolve, reject) => {
      keycloakAuth.init({onLoad: 'check-sso'})
        .success(() => {
          console.log('The keycloak client has been initiated successfully');
          KeycloakService.auth.loggedIn = true;
          KeycloakService.auth.authz = keycloakAuth;
          KeycloakService.auth.logoutUrl = environment.KEYCLOAK_URL
            + '/realms/' + environment.KEYCLOAK_REALM + '/protocol/openid-connect/logout?redirect_uri='
            + document.baseURI;
          resolve();
        })
        .error(() => {
          reject();
        });
    });
  }

  static hasRole(role: string): boolean {
    return KeycloakService.auth.authz.tokenParsed.realm_access.roles.indexOf(role) > -1;
  }

  static getUsername(): string {
    return KeycloakService.auth.authz.tokenParsed.preferred_username;
  }

  static getFullName(): string {
    return KeycloakService.auth.authz.tokenParsed.name;
  }

  public login(ussername: string, password: string): Observable<any> {
    console.log('Sending the login credentials to obtain a token');
    const credentials = {username: ussername, password: password};
    const url: string = environment.KEYCLOAK_URL + '/realms/' + environment.KEYCLOAK_REALM
      + '/protocol/openid-connect/token/generate-token';
    return this.httpClient.post(url, credentials);
  }

  public logout(): void {
    KeycloakService.auth.loggedIn = false;
    KeycloakService.auth.authz = null;
    window.location.href = KeycloakService.auth.logoutUrl;
  }

  public getToken(): Promise<string> {
    return new Promise<string>((resolve, reject) => {
      if (KeycloakService.auth.authz && KeycloakService.auth.authz.token) {
        KeycloakService.auth.authz
          .updateToken(5) // Refresh the token if it will expire in n seconds or less
          .success(() => {
            resolve(<string>KeycloakService.auth.authz.token);
          })
          .error(() => {
            reject('Failed to refresh the auth token');
          });
      } else {
        reject('The auth token could not be retrieved because the user was not logged in');
      }
    });
  }

  public isAuthenticated(): boolean {
    const token = this.getJwtTokenFromLocalStorage();
    return (token && !this.jwtHelperService.isTokenExpired(token));
  }

  public getTokenExpirationDate() {
    const token = this.getJwtTokenFromLocalStorage();
    return this.jwtHelperService.getTokenExpirationDate(token);
  }

  public getDecodedToken() {
    const token = this.getJwtTokenFromLocalStorage();
    return this.jwtHelperService.decodeToken(token);
  }

  public getJwtTokenFromLocalStorage(): string {
    return localStorage.getItem(JWT_TOKEN_NAME);
  }

  public setJwtTokenToLocalStorage(token: string): void {
    localStorage.setItem(JWT_TOKEN_NAME, token);
  }

  public getJwtTokenName(): string {
    return JWT_TOKEN_NAME;
  }

}

我所有的依赖项是:

"@angular/animations": "^5.2.0",
"@angular/common": "^5.2.0",
"@angular/compiler": "^5.2.0",
"@angular/core": "^5.2.0",
"@angular/forms": "^5.2.0",
"@angular/http": "^5.2.0",
"@angular/platform-browser": "^5.2.0",
"@angular/platform-browser-dynamic": "^5.2.0",
"@angular/router": "^5.2.0",
"@auth0/angular-jwt": "^1.0.0",
"angular-in-memory-web-api": "^0.5.3",
"core-js": "^2.4.1",
"jwt-decode": "^2.2.0",
"keycloak-js": "^3.4.3",
"npm": "^5.6.0",
"rxjs": "^5.5.6",
"zone.js": "^0.8.19"

更新:我将服务分为两个服务,一个KeycloakService处理Keycloak服务器,另一种AuthService封装它,以便具有较小的服务.但是问题仍然存在,我不得不寻求David的解决方案和他提到的解决方法.我的新AuthService具有以下构造函数:

UPDATE: I split the service into two services, one KeycloakService dealing with the Keycloak server and an AuthService sort of encapsulating it, so as to have smaller sized services. The issue remained though, and I had to go for David's solution and the workaround he mentioned. And my new AuthService has the following constructor:

@Injectable()
export class AuthService {

  //  constructor(private jwtHelperService: JwtHelperService) {} TODO
  jwtHelperService: JwtHelperService;
  constructor(private injector: Injector) {
    let jwtHelperService = this.injector.get(JwtHelperService);
  }

推荐答案

作为一种解决方法(虽然这不是很理想),您可以将注入器直接注入KeycloakService

As a workaround (it's not really ideal though), you could directly inject the injector in your KeycloakService

export class KeycloakService {

  static auth: any = {};

  constructor(private injector: Injector) {}

然后,当您需要访问HttpClientJwtHelperService时,只需在服务中调用resolve(而不是在构造函数中!)

Then when you need access to HttpClient or JwtHelperService, then you can just call resolve in your service (not in the constructor !)

let httpClient = this.injector.get<HttpClient>(HttpClient);

从您对问题的评论看来,这是引起问题的JwtHelperService,因此您只能在服务的构造函数中注入InjectorHttpClient,并在需要时解决JwtHelperService

From your comment on the question it looks like it's JwtHelperService causing the issue, so you could only onject Injector and HttpClient in the service's constructor and resolve JwtHelperService when needed

这篇关于获取循环依赖项错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆