我正在尝试使用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);
}
答案 0 :(得分:0)
作为一种解决方法(虽然它不是很理想),您可以直接在KeycloakService
export class KeycloakService {
static auth: any = {};
constructor(private injector: Injector) {}
然后,当您需要访问HttpClient
或JwtHelperService
时,您可以在服务中调用resolve(而不是在构造函数中!)
let httpClient = this.injector.get<HttpClient>(HttpClient);
修改:从您对该问题的评论看起来是JwtHelperService
导致该问题,因此您只能在服务的构造函数中注销Injector
和HttpClient
并解析{{1}在需要时