Angular2:带标记头的oauth2

时间:2017-03-13 19:55:35

标签: javascript angular typescript

我是angular2的新手。在1. *拦截器的一切都很好,只需添加它们:你的标头到处都有,你可以处理你的请求,当令牌变得无效时......

angular2我使用RxJs。 所以我得到了我的令牌:

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

然后如何在每个请求中使用此令牌?我不想在每个请求中设置.header。这是一种不好的做法。

然后:例如,当我做任何请求,并获得401 - 错误时,我如何拦截,获取新令牌,然后恢复所有请求,就像在angular 1中一样?

我尝试使用JWT来自jwt,但它并不符合我的要求,顺便说一下,我正在使用Restangular - 并且一切都很好(还有手动令牌:https://github.com/mgonto/restangular#seterrorinterceptor

2 个答案:

答案 0 :(得分:5)

您可以扩展默认的http服务并使用扩展版本,也可以创建一个获取某些参数的方法(如果需要)并返回RequestOptions对象以传递默认的http服务。

选项1

您可以创建服务:

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

用法示例:

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

此示例使用Cookie来存储和访问令牌。您也可以使用参数。

选项2

第二个选项是扩展http服务,例如:

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

在您的@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 ...
})

用法:

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

<强>附加

如果您想使用刷新令牌获取新令牌,您可以执行以下操作:

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

老实说,我没有测试过这个,但它至少可以为你提供一个起点。

答案 1 :(得分:0)

我们通过扩展AuthHttp解决了这个问题。我们在AuthHttp上添加了一个方法来动态设置一个新的头文件(X-RoleId是一个自定义头文件)

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}
          ];
};