javascript - Angular2: oauth2 med token-headere


Jeg er ny på Angular2. I 1.* var alt fint med interceptorer, bare legg dem til: og du har overalt dine headere, og du kan håndtere forespørslene dine når tokenen blir ugyldig...

I angular2 bruker jeg RxJs. Så jeg får tokenet mitt:

  getToken(login: string, pwd: string): Observable<boolean> {

    let bodyParams = {
      grant_type: 'password',
      client_id: 'admin',
      scope: AppConst.CLIENT_SCOPE,
      username: login,
      password: pwd
    };

    let params = new URLSearchParams();
    for (let key in bodyParams) {
      params.set(key, bodyParams[key])
    }

    let headers = new Headers({'Content-Type': 'application/x-www-form-urlencoded'});
    let options = new RequestOptions({headers: headers});

    return this.http.post(AppConst.IDENTITY_BASE_URI + '/connect/token', params.toString(), options)
      .map((response: Response) => {
        let data = response.json();

        if (data) {
          this.data = data;
          localStorage.setItem('auth', JSON.stringify({
            access_token: data.access_token,
            refresh_token: data.refresh_token
          }));
          return true;
        } else {
          return false;
        }
      });
  }

og hvordan kan jeg bruke denne tokenen i hver forespørsel? Jeg vil ikke sette .header i hver forespørsel. Det er en dårlig praksis.

Og så: for eksempel når jeg gjør en forespørsel, og får 401-feil, hvordan kan jeg avbryte, og få en ny token, og deretter gjenoppta alle forespørsler, som det var i angular 1?

jeg prøvde å bruke JWT herfra jwt, men det oppfyller ikke kravene mine, forresten i første Angular brukte jeg Restangular - og alt var fint der (også med manualen for tokens:https://github.com/mgonto/restangular#seterrorinterceptor)

Svar #1

Du kan enten utvide standard http-tjenesten og bruke den utvidede versjonen, eller du kan lage en metode som tar noen parametere (om nødvendig) og returnerer et RequestOptions-objekt for å sende til standard http-tjeneste.

Alternativ 1

Du kan opprette en tjeneste:

@Injectable()
export class HttpUtils {
  constructor(private _cookieService: CookieService) { }

  public optionsWithAuth(method: RequestMethod, searchParams?: URLSearchParams): RequestOptionsArgs {
    let headers = new Headers();
    let token = 'fancyToken';
    if (token) {
      headers.append('Auth', token);
    }
    return this.options(method, searchParams, headers);
  }

  public options(method: RequestMethod, searchParams?: URLSearchParams, header?: Headers): RequestOptionsArgs {
    let headers = header || new Headers();
    if (!headers.has('Content-Type')) {
      headers.append('Content-Type', 'application/json');
    }
    let options = new RequestOptions({headers: headers});
    if (method === RequestMethod.Get || method === RequestMethod.Delete) {
      options.body = '';
    }
    if (searchParams) {
      options.params = searchParams;
    }
    return options;
  }

  public handleError(error: Response) {
    return (res: Response) => {
      if (res.status === 401) {
        // do something
      }
      return Observable.throw(res);
    };
  }
}

Brukseksempel:

this._http
  .get('/api/customers', this._httpUtils.optionsWithAuth(RequestMethod.Get))
  .map(res => <Customer[]>res.json())
  .catch(err => this._httpUtils.handleError(err));

Dette eksemplet bruker informasjonskapsler for å lagre og få tilgang til tokenet. Du kan også bruke en parameter.

Alternativ 2

Det andre alternativet er å utvide HTTP-tjenesten, for eksempel slik:

import { Injectable } from '@angular/core';
import { Http, XHRBackend, RequestOptions, Request, RequestOptionsArgs, Response, Headers } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';

@Injectable()
export class MyHttp extends Http {

  constructor (backend: XHRBackend, options: RequestOptions) {
    let token = 'fancyToken';
    options.headers.set('Auth', token);
    super(backend, options);
  }

  request(url: string|Request, options?: RequestOptionsArgs): Observable<Response> {
    let token = 'fancyToken';
    if (typeof url === 'string') {
      if (!options) {
        options = {headers: new Headers()};
      }
      options.headers.append('Auth', token);
    } else {
      url.headers.append('Auth', token);
    }
    return super.request(url, options).catch(this.handleError(this));
  }

  private handleError (self: MyHttp) {
    return (res: Response) => {
      if (res.status === 401) {
        // do something
      }
      return Observable.throw(res);
    };
  }
}

Og i din @NgModule:

@NgModule({
  // other stuff ...
  providers: [
    {
      provide: MyHttp,
      useFactory: (backend: XHRBackend, options: RequestOptions) => {
        return new MyHttp(backend, options);
      },
      deps: [XHRBackend, RequestOptions]
    }
  ]
  // a little bit more other stuff ...
})

Bruk:

@Injectable()
class CustomerService {

  constructor(private _http: MyHttp) {
  }

  query(): Observable<Customer[]> {
    return this._http
      .get('/api/customers')
      .map(res => <Customer[]>res.json())
      .catch(err => console.log('error', err));
  }
}

Ekstra:

Hvis du vil bruke oppfriskingstoken for å skaffe et nytt token, kan du gjøre noe som dette:

private handleError (self: MyHttp, url?: string|Request, options?: RequestOptionsArgs) {
  return (res: Response) => {
    if (res.status === 401 || res.status === 403) {
      let refreshToken:string = 'fancyRefreshToken';
      let body:any = JSON.stringify({refreshToken: refreshToken});
      return super.post('/api/token/refresh', body)
        .map(res => {
          // set new token
        }) 
        .catch(err => Observable.throw(err))
        .subscribe(res => this.request(url, options), err => Observable.throw(err));
    }
    return Observable.throw(res);
  };
}

For å være ærlig har jeg ikke testet dette, men det kan gi deg i det minste et utgangspunkt.

Svar #2

Vi løste problemet med utvidelsen av AuthHttp. Vi la til en metode på AuthHttp for å sette en ny header dynamisk på denne måten (X-RoleId er en tilpasset header)

declare module 'angular2-jwt' {
  interface AuthHttp {
    setRoleId(config: {});
  }
}

AuthHttp.prototype.setRoleId = function (roleId) {
  let jsThis = <any>(this);
  jsThis.config.globalHeaders = [
          {'Content-Type': 'application/json'}, 
          {'X-RoleId': roleId}
          ];
};

Relaterte artikler