【问题标题】:how to refresh the access token using custom http in angular 2?如何在 Angular 2 中使用自定义 http 刷新访问令牌?
【发布时间】:2017-07-27 11:41:33
【问题描述】:

我在我的应用程序中使用基于令牌的身份验证。我的后端是使用 restful 服务(spring)开发的。后端代码很好地生成了所需的访问令牌和带有时间线的刷新令牌,所以我用以下内容覆盖了 http 类:

export class customHttp extends Http {
   headers: Headers = new Headers({ 'Something': 'Something' });
    options1: RequestOptions = new RequestOptions({ headers: this.headers });
    private refreshTokenUrl = AppSettings.REFRESH_TOKEN_URL;
    constructor(backend: ConnectionBackend,
        defaultOptions: RequestOptions,private refresh:OauthTokenService) {
        super(backend, defaultOptions);
    }
    request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
    console.log("custom http ");
        return super.request(url, options)
            .catch((err) => {
                if (err.status === 401) {
                    console.log(" custome http 401 ");
                    //   refresh the token
                    this.refresh.refresh().subscribe((tokenObj)=>{
                              console.log("tokenobj ");
                    })
                 } else {
                    console.log("err " + err);
                }
            }); } } 

我在 refresh() 方法中刷新令牌时遇到了循环依赖错误,因此我尝试在另一个模块中使用刷新服务,但没有运气。我使用的方法与Handling refresh tokens using rxjs 中提到的方法相同,任何帮助都会很棒!

【问题讨论】:

    标签: javascript angular typescript oauth-2.0 refresh-token


    【解决方案1】:

    这对我有用:

     request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
        //adding access token to each http request before calling super(..,..)
        let token = this.authenticationService.token;
        if (typeof url === 'string') {
            if (!options) {
                options = { headers: new Headers() };
            }
            options.headers.set('Authorization', `Bearer ${token}`);
        }
        else {
            url.headers.set('Authorization', `Bearer ${token}`);
        }
        return super.request(url, options)
          .catch((error) => {
                //if got authorization error - try to update access token
                if (error.status = 401) {
                    return this.authenticationService.updateToken()
                        .flatMap((result: boolean) => {
                            //if got new access token - retry request
                            if (result) {
                                return this.request(url, options);
                            }
                            //otherwise - throw error
                            else {
                                return Observable.throw(new Error('Can\'t refresh the token'));
                            }
    
                        })
                }
                else {
                    Observable.throw(error);
                }
            })
    }
    

    更新:authenticationService.updateToken() 实现应该取决于您使用的授权提供者/授权机制。在我的情况下,它是 OAuth Athorization Server,因此实现基本上将带有刷新令牌的发布请求发送到配置的令牌 url 并返回更新的访问和刷新令牌。 tokenEndPointUrl 由 OAuth 配置并发出访问和刷新令牌(取决于发送的 grant_type)。因为我需要刷新令牌,所以我将 grant_type 设置为 refresh_token。代码类似于:

    updateToken(): Observable<boolean> {
        let body: string = 'refresh_token=' + this.refreshToken + '&grant_type=refresh_token';
    
        return this.http.post(tokenEndPointUrl, body, this.options)
            .map((response: Response) => {
                var returnedBody: any = response.json();
                if (typeof returnedBody.access_token !== 'undefined'){
                  localStorage.setItem(this.tokenKey, returnedBody.access_token);
                  localStorage.setItem(this.refreshTokenKey, returnedBody.refresh_token);
                return true;
            }
            else {
                return false;
            }
            })
    }
    

    希望对你有帮助

    【讨论】:

    • 但 401 并不一定意味着令牌无效,令牌有效但不适用于该请求,这意味着 401 将继续触发,您将进入循环重新创建刷新令牌...我说的对吗?
    • 我猜这里是认证和授权的混淆。 401 UNAUTHORIZED - 请求尚未应用,因为它缺少目标资源的有效身份验证凭据。 403 FORBIDDEN - 服务器理解请求但拒绝授权。因此,如果您从服务器获得 401 - 您的凭据已过时。如果您收到 403 - 服务器对您进行了身份验证,但由于缺乏权限而无法授权您的访问。所以 403 更多的是关于你在系统中的权限而不是你的令牌。在您拥有权限之前,您永远无法使用令牌获得访问权限。
    • 当然这取决于您的服务器授权提供商,但理想情况下应该是 401 - 过期令牌和 403 - 缺少权限。
    【解决方案2】:

    感谢回复@dragonfly,这对我有用

        post(url: string, body: any, options?: RequestOptionsArgs): Observable<Response> {
            //check if the token is expired then set the latest token
                    if (this.isTokenExpired) {
                        options.headers.set('Authorization', 'Bearer ' + localStorage.getItem("accessToken"));
       }
    
      return super.post(url, body, options)
             .catch((err) => {
      //if authentication error
          if (err.status === 401) {
           this.isTokenExpired = true;
          options.headers.set('Authorization', 'Bearer ' + localStorage.getItem("accessToken"));
        //refresh the token
     let refreshUrl = this.refreshTokenUrl;
        //pass the refresh token 
     refreshUrl = refreshUrl.replace(':refreshToken', localStorage.getItem("refreshToken"));
     //rest the access token
      return super.get(refreshUrl).mergeMap((tokenObj) => {
      localStorage.setItem("accessToken", tokenObj.json().value);
       // reset the headers
        options.headers.set('Authorization', 'Bearer ' + localStorage.getItem("accessToken"));
    //retry the request with the new access token
    return this.post(url, body, options)
     })
      .catch((refreshErr) => {
        if (refreshErr.status == 400) {
               console.log("refesh err");
        window.location.href = 'request=logout';
       }
     return Observable.throw(refreshErr);
      })
      } else {
      return err;
      }
     })
    }
    

    你能告诉你如何更新令牌(this.authenticationService.updateToken())吗?

    【讨论】:

      【解决方案3】:

      对于那些访问此页面但对其中一无所知的人。

      通俗易懂:

      创建您的刷新令牌方法: //以某种方式检索刷新令牌

      refreshToken(){
      
          let refreshToken = sessionStorage.getItem('refresh_token');
      
          let body = 'grant_type=refresh_token&refresh_token=' + refreshToken;
      
          var headers = new Headers();
          headers.append('Content-Type', 'application/x-www-form-urlencoded');
          headers.append('Authorization','Basic ' + btoa('something:something'));
      
          return this.http.post('your auth url',body,{headers: headers})
      
        }
      

      比你的http请求(我的使用angular-jwt authHttp而不是http)

      testService(){
          this.authHttp.get('your secured service url')
          .map(res => {
                  return res;
              })
          .catch(error=> {
                  if (error.status === 401) {
                      return this.refreshToken().flatMap((newToken) => {
                        let newAccessToken = newToken.json();
                        sessionStorage.setItem('id_token', newAccessToken['access_token']);
                        sessionStorage.setItem('refresh_token', newAccessToken['refresh_token']);
      
                        return this.authHttp.request('your secured service url');
                      })
                  } else {
                      return Observable.throw(error);
                  }
              })
          .subscribe(res => console.log(res));
      
        }
      

      别忘了导入你需要的东西:

      import { AuthHttp } from 'angular2-jwt';
      import { Observable } from "rxjs/Observable";
      import 'rxjs/add/operator/map';
      import 'rxjs/add/operator/catch';
      import 'rxjs/add/observable/throw';
      

      不要订阅您的刷新令牌方法。如果你这样做了,你会在服务调用方法的平面图中看到一个很大的错误。

      【讨论】:

      • 嗨,如果多个 https 请求被近距离发送,这不会导致多个刷新请求,或者 flatmap 会阻止这种情况。我问的原因是因为我有类似的设置,但我使用的是订阅而不是平面图。虽然刷新成功,但用户体验很糟糕,因为它最终重定向到登录,然后又返回。我想采用您的解决方案。谢谢
      猜你喜欢
      • 2020-07-12
      • 2013-09-05
      • 2014-09-13
      • 2019-06-29
      • 2019-11-15
      • 2015-06-19
      • 2016-09-23
      • 2016-08-13
      • 1970-01-01
      相关资源
      最近更新 更多