【发布时间】:2018-08-30 08:26:06
【问题描述】:
我正在使用 adal-angular4 库在 Angular 4 单页应用程序中实现隐式流程。这个应用程序调用一个 web api 来显示结果。这两个应用程序都托管在 azure 中(在特定租户中)并进行了适当的注册,并且配置工作正常。
我面临的问题是当第一次调用 API 时,acquiretoken 不会立即返回令牌,它会出错。作为错误处理的一部分,这会导致页面错误(我有意识的决定)。但在几秒钟内,acquiretoken 会返回该令牌。因此,如果我在几秒钟后刷新页面,则 api 调用成功并且一切都按预期工作。
这里是各个组件的相关代码
1. AuthenticationGuard.ts从 '@angular/core' 导入 { Injectable }; 从 'rxjs/Observable' 导入 { Observable }; 从'@angular/router'导入{路由器、CanActivate、CanActivateChild、ActivatedRouteSnapshot、RouterStateSnapshot、NavigationExtras}; 从“adal-angular4”导入 { AdalService };
@Injectable() 导出类 AuthenticationGuard 实现 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
从'@angular/core'导入{组件,OnInit}; 从'adal-angular4'导入{AdalService}; 从'@angular/router'导入{路由器}; 从'../environments/environment'导入{环境};
@组件({ 选择器:'应用程序根', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) 导出类 AppComponent 实现 OnInit { title = '我的门户'; isLoggedIn = 假; 登录用户:字符串; 私人身体标签:任何; 构造函数(私有 adalService:AdalService,私有路由器:路由器) { this.adalService.init(environment.azureConfig); this.bodyTag = document.getElementsByTagName("body") }
ngOnInit() { console.log("AppComponent 初始化"); this.adalService.handleWindowCallback(); 如果(this.adalService.userInfo.authenticated){ this.isLoggedIn = this.adalService.userInfo.authenticated; this.loggedInUser = this.adalService.userInfo.profile.name; } document.addEventListener("keydown", () => this.handleEvt); }
注销():无效{ this.adalService.logOut(); }}
-
LoginComponent.ts
从'@angular/core'导入{组件,OnInit,Injectable,Inject}; 从'@angular/router'导入{ActivatedRoute,路由器}; 从'adal-angular4'导入{AdalService}; 从 '../app.constants' 导入 { APP_CONFIG, AppConfig };
@组件({ 选择器:'应用程序登录', templateUrl: './login.component.html', styleUrls: ['./login.component.css'] }) 导出类 LoginComponent 实现 OnInit {
isLoggedIn = false; 登录用户:字符串; localLoggedInUser:任何; tokenNew: 字符串;
构造函数( 私有路由:ActivatedRoute, 私有路由器:路由器, 私人 adalService:AdalService, @Inject(APP_CONFIG) 私有appConfig:AppConfig ) { }
ngOnInit() { this.adalService.handleWindowCallback(); 如果(this.adalService.userInfo.authenticated){ this.isLoggedIn = this.adalService.userInfo.authenticated; this.loggedInUser = this.adalService.userInfo.profile.name ; } }
登录():无效{ 常量 returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/'; 如果(this.adalService.userInfo.authenticated){ this.router.navigate([returnUrl]); } 别的 { this.adalService.login(); } } }
-
ListService.ts(负责通过 getAll() 方法进行第一次 API 调用。
从 '@angular/core' 导入 { Injectable, OnInit, Inject }; 从'@angular/http'导入{Headers, Http, Response, RequestOptions}; 从 '@angular/common/http' 导入 { HttpClient, HttpHeaders }; 从 'rxjs/Observable' 导入 { Observable }; 导入'../rxjs-extensions'; 从 '../app.constants' 导入 { APP_CONFIG, AppConfig }; @Injectable() 导出类 ProductService 实现 OnInit { 标头:标头; 令牌新:字符串; 项目:产品[]; 登录用户名:字符串; 登录用户电子邮件:字符串; baseUrl = 'https://myproductsapi/'; productListEndpoint = this.baseUrl + '/products'; 构造函数(私有http:HttpClient,私有httpBase:Http, @Inject(APP_CONFIG) 私有 appConfig:AppConfig,私有 adalService:AdalService) { this.adalService.handleWindowCallback(); this.setLoggedInUserDetails(); }
public getAllProducts(): Observable> { 返回 this.httpBase.get(this.productListEndpoint, this.getRequestOptionsWithHeadersForHTTP()) .map(响应 => { 常量 tmp = response.json(); 返回 tmp; }); }
私有 getRequestOptionsWithHeadersForHTTP(): RequestOptions { this.attemptAcquireToken(); this.tokenNew = this.adalService.getCachedToken(this.appConfig.resourceApplicationId); const headersNew = new Headers({ 'Content-Type': 'application/json' }); headersNew.append('授权', '承载' + this.tokenNew); 常量选项=新的请求选项(); 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 (headers) 与之前的 http 请求相关联 向 api 端点发出请求。
getRequestOptionsWithHeadersForHTTP() 调用尝试AcquireToken() 以 从 adal 服务获取令牌。
attemptAcquireToken() 调用 acquiretoken() - 该方法首先检查是否 有一个缓存的令牌,如果没有,它调用acquiretoken 取回一个。正是在这里,调用会立即执行“错误”lambda 中的代码块——从而在 UI 中引发错误。经过几次 当acquiretoken 检索到它的token 时的秒数 执行“令牌”lambda 中的代码。
这只发生在用户第一次登录并且缓存中没有令牌时。对于后续尝试,这可以正常工作(检索缓存的令牌)。
【问题讨论】:
标签: angular azure oauth azure-active-directory adal