刷新令牌OAuth身份验证Angular 4+ [英] Refresh Token OAuth Authentication Angular 4+

查看:210
本文介绍了刷新令牌OAuth身份验证Angular 4+的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Angular的 Http 分组,但我决定进行迁移并使用新的 HttpClient ,我试图用拦截器创建一个解决方案来管理我需要刷新令牌的情况,以及何时需要修改标头以放置授权令牌。

I was working with the Http clase from Angular but I decide to make the migration and work with the new HttpClient, and I was trying to create a solution with Interceptors to manage the cases when I need to refresh the token and when I need to modify the header to put the Authorization Token.

推荐答案

首先我找到了这些帖子以及其他许多帖子:

First I found these post and so many others :

  • https://medium.com/@amcdnl/the-new-http-client-in-angular-4-3-754bd3ff83a8
  • https://medium.com/@ryanchenkie_40935/angular-authentication-using-the-http-client-and-http-interceptors-2f9d1540eb8

...但这些解决方案是p如果您只想处理授权标题的操作,则会出现错误。然后我想出了这个解决方案

... but those solutions are perfect if you just want to handle the action to put the Authorization Header. Then I come up with this solution

@Injectable()
export class RefreshTokenInterceptor implements HttpInterceptor {

  constructor(private injector: Injector, private authService: Auth) {
  }


  private getRequestWithAuthentication(request: HttpRequest<any>, next: HttpHandler, auth: OAuthService): Observable<HttpEvent<any>> {
    const  req = request.clone({
        headers: request.headers.set('Authorization', auth.getHeaderAuthorization())
      });
    return next.handle(req);
  }

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    // To avoid cyclic dependency
    const auth = this.injector.get(OAuthService);

    if (auth.hasAuthorization()) {
      return this.getRequestWithAuthentication(request, next, auth);
    } else if (auth.hasAuthorizationRefresh() && request.url !== AUTHORIZE_URL) {
      return auth.refreshToken().flatMap(
        (res: any) => {
          auth.saveTokens(res);
          return this.getRequestWithAuthentication(request, next, auth);
        }
      ).catch(() => {
        return next.handle(request);
      });
    } else if (request.url === AUTHORIZE_URL) {
      return next.handle(request);
    }

    return this.getRequestWithAuthentication(request, next, auth);
  }
}

这个主要想法很简单:


  • 首先,我正在注入一项服务,我有所有逻辑来确定我是否有令牌刷新令牌,当然还有保存并获取它的操作。

  • 如果我有授权​​(令牌可以放置在标题中)我只是用Authorization Header返回请求,如果不是我检查我是否有刷新令牌并且我试图从服务器获取它然后我等待直到我有令牌来传递请求。

  • 常量 AUTHORIZE_URL 这是一个字符串,其中包含我用来获取令牌或刷新它的服务器的路由。我检查这个的原因是因为我在 OAuthService 中使用 HttpClient 发出请求所以它会通过来自拦截器,如果我不检查就会产生无限循环。

  • First, I'm injecting a service where I have all the logic to determinate if I have the token or the refresh token and of course the action to save it and get it.
  • If I have the authorization (which is the token to put in the header) I am just return the request with the Authorization Header, if not I am check if I have the refresh token and I am trying to get it from the server and then I'm waitng until I have the token to pass the request.
  • The constant AUTHORIZE_URL it's a string with the route from the server that I am using to get the token or refresh it. The reason because I'm checking this is because I am making a request with HttpClient in the OAuthService so It's gonna pass from the interceptor too and It's gonna make a infinite loop if I don't check it.

解决方案在某些情况下工作正常,但事情是,例如令牌已过期并且您有多个请求,每个请求都会尝试刷新令牌。

This Solution work fine in some cases, but the thing is when for example the token expired and you have multiple request, every request is going to try to refresh the token.






在此之后我找到了这个解决方案,但我想知道你对代码和我正在做的方式的看法。


After this I found this solution but I wanna know what do you think about code and way that I'm doing it.

好的,首先我创建了一个服务来保存刷新令牌请求的状态和Observable以了解请求何时完成。

Ok, First I created a Service to save the state of the refresh token request and Observable to know when the request is done.

@Injectable()
export class RefreshTokenService {
  public processing: boolean = false;
  public storage: Subject<any> = new Subject<any>();

  public publish(value: any) {
    this.storage.next(value);
  }
}




我注意到它如果我有两个Interceptor一个刷新令牌并处理它,一个放置授权头(如果存在),那就更好了。

I noticed that It was better if I have two Interceptors one to refresh the token and handle that and one to put the Authorization Header if exist.



这个用于刷新令牌的拦截器:



This the Interceptor for Refresh the Token:

@Injectable()
  export class RefreshTokenInterceptor implements HttpInterceptor {

    constructor(private injector: Injector, private tokenService: RefreshTokenService) {
    }

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
      const auth = this.injector.get(OAuthService);
      if (!auth.hasAuthorization() && auth.hasAuthorizationRefresh() && !this.tokenService.processing && request.url !== AUTHORIZE_URL) {
        this.tokenService.processing = true;
        return auth.refreshToken().flatMap(
          (res: any) => {
            auth.saveTokens(res);
            this.tokenService.publish(res);
            this.tokenService.processing = false;
            return next.handle(request);
          }
        ).catch(() => {
          this.tokenService.publish({});
          this.tokenService.processing = false;
          return next.handle(request);
        });
      } else if (request.url === AUTHORIZE_URL) {
        return next.handle(request);
      }

      if (this.tokenService.processing) {
        return this.tokenService.storage.flatMap(
          () => {
            return next.handle(request);
          }
        );
      } else {
        return next.handle(request);
      }
    }
  }

所以我在这里等刷新令牌可用或失败,然后我发布需要授权标题的请求。

So here I'm waiting to the refresh token to be available or fails and then I release the request that needs the Authorization Header.

@Injectable()
  export class TokenInterceptor implements HttpInterceptor {
    constructor(private injector: Injector) {}

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
      const auth = this.injector.get(OAuthService);
      let req = request;
      if (auth.hasAuthorization()) {
        req = request.clone({
          headers: request.headers.set('Authorization', auth.getHeaderAuthorization())
        });
      }

      return next.handle(req).do(
        () => {},
        (error: any) => {
          if (error instanceof HttpErrorResponse) {
            if (error.status === 401) {
              auth.logOut();
            }
          }
        });
    }
  }



我的主模块是这样的:



And my main module is something like this:

@NgModule({
  imports: [
    ...,
    HttpClientModule
  ],
  declarations: [
    ...
  ],
  providers: [
    ...
    OAuthService,
    AuthService,
    RefreshTokenService,
    {
      provide: HTTP_INTERCEPTORS,
      useClass: RefreshTokenInterceptor,
      multi: true
    },
    {
      provide: HTTP_INTERCEPTORS,
      useClass: TokenInterceptor,
      multi: true
    }
  ],
  bootstrap: [AppComponent]
})
export class AppModule {
}

欢迎任何反馈,如果我有什么不对的话请告诉我。我正在测试 Angular 4.4.6 ,但我不知道它是否适用于角度5,我认为应该可行。

Please any feedback will be welcome and if I'm doning something wrong tell me. I'm testing with Angular 4.4.6 but I don't know if it work on angular 5, I think should work.

这篇关于刷新令牌OAuth身份验证Angular 4+的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆