首页 文章

返回令牌之前的AcquireToken Observable错误

提问于
浏览
0

我正在使用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 回答

  • 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);
            }
        });
    }
    

    等等 ...

相关问题