我在角度4单页面应用程序中使用adal-angular4库进行隐式流程实现。此应用程序调用web api来显示结果。这两个应用程序都托管在azure(在特定租户中)并正确注册并且配置有效。
我面临的问题是,当第一次调用API时,getstoken不会立即返回令牌,它会出错。作为错误处理的一部分,这会导致页面错误(我自觉的决定)。但是在几秒钟内,acquiretoken返回令牌。因此,如果我在几秒钟后刷新页面,则api调用成功,一切都按预期工作。
以下是各种组件的相关代码
1. AuthenticationGuard.tsimport { Injectable } from '@angular/core'; import { Observable } from 'rxjs/Observable'; import { Router, CanActivate, CanActivateChild, ActivatedRouteSnapshot, RouterStateSnapshot, NavigationExtras } from '@angular/router'; import { AdalService } from 'adal-angular4';
@Injectable() export class AuthenticationGuard implements CanActivate, CanActivateChild {
constructor( private router: Router, private adalSvc: AdalService ) { } canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { if (this.adalSvc.userInfo.authenticated) { return true; } else { this.router.navigate(['/login'], { queryParams: { returnUrl: state.url } }); return false; } } canActivateChild(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { return this.canActivate(childRoute, state); }}
appcomponent.ts
import { Component, OnInit } from '@angular/core'; import { AdalService } from 'adal-angular4'; import { Router } from '@angular/router'; import { environment } from '../environments/environment';
@Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit { title = 'My portal'; isLoggedIn = false; loggedInUser: string; private bodyTag: any; constructor(private adalService: AdalService, private router: Router) { this.adalService.init(environment.azureConfig); this.bodyTag = document.getElementsByTagName("body") }
ngOnInit() { console.log("AppComponent Init"); this.adalService.handleWindowCallback(); if (this.adalService.userInfo.authenticated) { this.isLoggedIn = this.adalService.userInfo.authenticated; this.loggedInUser = this.adalService.userInfo.profile.name; } document.addEventListener("keydown", () => this.handleEvt); }
logout(): void { this.adalService.logOut(); }}
LoginComponent.ts
import { Component, OnInit, Injectable, Inject } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { AdalService } from 'adal-angular4'; import { APP_CONFIG, AppConfig } from '../app.constants';
@Component({ selector: 'app-login', templateUrl: './login.component.html', styleUrls: ['./login.component.css'] }) export class LoginComponent implements OnInit {
isLoggedIn = false; loggedInUser: string; localLoggedInUser: any; tokenNew: string;
constructor( private route: ActivatedRoute, private router: Router, private adalService: AdalService, @Inject(APP_CONFIG) private appConfig: AppConfig ) { }
ngOnInit() { this.adalService.handleWindowCallback(); if (this.adalService.userInfo.authenticated) { this.isLoggedIn = this.adalService.userInfo.authenticated; this.loggedInUser = this.adalService.userInfo.profile.name ; } }
login(): void { const returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/'; if (this.adalService.userInfo.authenticated) { this.router.navigate([returnUrl]); } else { this.adalService.login(); } } }
ListService.ts (responsible for making the first API call via the getAll() method.
import { Injectable, OnInit, Inject } from '@angular/core'; import {Headers, Http, Response, RequestOptions} from '@angular/http'; import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Observable } from 'rxjs/Observable'; import '../rxjs-extensions'; import { APP_CONFIG, AppConfig } from '../app.constants'; @Injectable() export class ProductService implements OnInit { headers: Headers; tokenNew: string; items: Products[]; loggedInUserName: string; loggedInUserEmail: string; baseUrl = 'https://myproductsapi/'; productListEndpoint = this.baseUrl + '/products'; constructor( private http: HttpClient, private httpBase: Http, @Inject(APP_CONFIG) private appConfig: AppConfig, private adalService: AdalService) { this.adalService.handleWindowCallback(); this.setLoggedInUserDetails(); }
public getAllProducts(): Observable> { return this.httpBase.get(this.productListEndpoint, this.getRequestOptionsWithHeadersForHTTP()) .map(response => { const tmp = response.json(); return tmp; }); }
private getRequestOptionsWithHeadersForHTTP(): RequestOptions { this.attemptAcquireToken(); this.tokenNew = this.adalService.getCachedToken(this.appConfig.resourceApplicationId); const headersNew = new Headers({ 'Content-Type': 'application/json' }); headersNew.append('Authorization', 'Bearer ' + this.tokenNew); const options = new RequestOptions(); options.headers = headersNew;
return options; } private attemptAcquireToken() { this.tokenNew = this.adalService.getCachedToken(this.appConfig.resourceApplicationId); console.log('1. token from attemptacquiretoken ' + this.tokenNew); // this.tokenNew = null; if (this.tokenNew == null) { console.log('2. token new is null - trying to acquiretoken using adal'); this.adalService.acquireToken(this.appConfig.resourceApplicationId).subscribe( token => { console.log('3. token acquired ' + token); //this happens but only a few seconds later this.tokenNew = token; }, // never comes error => { console.log('4. Error when acquiring token'); //this is called straight away console.log(error); }); } }
如何在调用API之前确保令牌存在 - 就目前而言,检索产品列表的方法调用顺序如下:
调用getAllProducts() - 这是负责的主要方法 调用API。这会启动对其他方法的嵌套调用。
getAllProducts()调用getRequestOptionsWithHeadersForHTTP()来获取 RequestOptions(标头)之前与http请求关联 将请求发送到api端点。
getRequestOptionsWithHeadersForHTTP()调用attemptAcquireToken() 从adal服务获取令牌。
attemptAcquireToken()调用acquiretoken() - 该方法首先检查是否 有一个缓存的令牌,如果没有,它会调用acquiretoken 找回一个。在这里,调用立即执行“错误”lambda中的代码块 - 从而在UI中引发错误。经过几次 在getstoken收回令牌时的秒数 执行“token”lambda中的代码。
这仅在用户首次登录且缓存中没有令牌时才会发生。对于后续尝试,这可以正常工作(检索缓存的令牌)。
答案 0 :(得分:0)
你可以像这样使用Promise或chain observable:
private attemptAcquireToken():Observable<boolean> {
this.tokenNew = this.adalService.getCachedToken(this.appConfig.resourceApplicationId);
console.log('1. token from attemptacquiretoken ' + this.tokenNew);
// this.tokenNew = null;
if (this.tokenNew == null) {
console.log('2. token new is null - trying to acquiretoken using adal');
this.adalService.acquireToken(this.appConfig.resourceApplicationId).subscribe(
token => {
...
return Observable.of(true);
}, // never comes
error => {
....
return Observable.of(false);
}
}
private getRequestOptionsWithHeadersForHTTP(): Observable<RequestOptions> {
this.attemptAcquireToken().subsribe(r => {
if (r !== false) {
...
options.headers = headersNew;
return Observable.of(options);
} else {
return Observable.of(undefined);
}
});
}
依旧......