获得循环依赖性错误

时间:2018-03-12 16:37:06

标签: angular keycloak

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

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应用程序中,这是我的设置:

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:属性可以解决问题。当然,没有注入任何服务也不是解决方案。

我的服务是:

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封装它,以便拥有更小的服务。问题仍然存在,我不得不采取大卫的解决方案和他提到的解决方法。我的新AuthService有以下构造函数:

@Injectable()
export class AuthService {

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

1 个答案:

答案 0 :(得分:0)

作为一种解决方法(虽然它不是很理想),您可以直接在KeycloakService

中注入注射器
export class KeycloakService {

  static auth: any = {};

  constructor(private injector: Injector) {}

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

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

修改:从您对该问题的评论看起来是JwtHelperService导致该问题,因此您只能在服务的构造函数中注销InjectorHttpClient并解析{{1}在需要时