我正在使用adal-angular4库在角度4单页面应用程序中进行隐式流程实现 . 这个应用程序调用web api来显示结果 . 这两个应用程序都托管在azure(在特定租户中)并正确注册并且配置有效 .
我面临的问题是,当第一次调用API时,getstoken不会立即返回令牌,它会出错 . 作为错误处理的一部分,这会导致页面错误(我有意识地做出决定) . 但是在几秒钟内,acquiretoken返回令牌 . 因此,如果我在几秒钟后刷新页面,则api调用成功,一切都按预期工作 .
以下是各种组件的相关代码
1. AuthenticationGuard.ts
import { 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()以在将请求发送到api endpoints 之前获取RequestOptions(标头)以与http请求关联 .
-
getRequestOptionsWithHeadersForHTTP()调用attemptAcquireToken()从adal服务获取令牌 .
-
attemptAcquireToken()调用acquiretoken() - 该方法首先检查是否存在缓存的令牌,如果没有,则调用acquiretoken来检索一个 . 在这里,调用立即执行“错误”lambda中的代码块 - 从而引起UI中的错误 . 在几秒钟之后,当getstoken已经检索到令牌时,它执行“令牌”lambda中的代码 .
这仅在用户第一次登录时才会发生,并且缓存中没有令牌 . 对于后续尝试,这可以正常工作(检索缓存的令牌) .
1 回答
您可以像这样使用Promise或chain observable:
等等 ...