Angular2-jwt - AuthHttp,刷新令牌,把它们放在一起

Posted

技术标签:

【中文标题】Angular2-jwt - AuthHttp,刷新令牌,把它们放在一起【英文标题】:Angular2-jwt - AuthHttp, refresh tokens, putting it all together 【发布时间】:2017-06-02 00:24:20 【问题描述】:

我好像在这里转了一圈,也许是因为使用了这么多订阅并且不得不将它们链接在一起。

如果令牌过期,我希望能够使用刷新令牌刷新令牌。这就是我所拥有的,如果可能的话,我非常感谢一个简单的工作示例。

总之,我如何确保 AudienceService 首先检查令牌是否有效,如果无效,它会尝试使用刷新令牌对其进行刷新,然后使用适当的令牌调用端点?

app.module.ts:

import  BrowserModule  from '@angular/platform-browser';
import  NgModule  from '@angular/core';
import  FormsModule, ReactiveFormsModule  from '@angular/forms';
import  HttpModule  from '@angular/http';
import  RouterModule  from '@angular/router';
import  Http, RequestOptions  from '@angular/http';
import  ConfirmDialogModule, ListboxModule, PickListModule  from 'primeng/primeng';

import  AppComponent  from './app.component';
import  HeaderComponent  from './components/header/header.component';
import  HomeComponent  from './components/home/home.component';
import  ListAudiencesComponent  from './components/audiences/list-audiences/list-audiences.component';

import  AudienceService  from './services/audience.service';
import  LoggingService  from './services/logging.service';
import  RoleService  from './services/role.service';
import  AuthService  from './services/auth.service';
import  UserService  from './services/user.service';
import  AuthGuard  from './services/auth-guard.service'
import  AuthHttp, AuthConfig, provideAuth  from 'angular2-jwt';
import  ListRolesComponent  from './components/roles/list-roles/list-roles.component';
import  EditRoleAudiencesComponent  from './components/roles/edit-role-audiences/edit-role-audiences.component';
import  ModifyRoleComponent  from './components/roles/modify-role/modify-role.component';
import  LoginComponent  from './components/login/login.component';
import  UnauthorizedComponent  from './components/unauthorized/unauthorized.component';

export function authHttpServiceFactory(http: Http, options: RequestOptions) 
  return new AuthHttp(new AuthConfig(
    tokenName: 'token',
          tokenGetter: (() => sessionStorage.getItem('id_token')),
          globalHeaders: ['Content-Type':'application/json'],
     ), http, options);


@NgModule(
  declarations: [
    AppComponent,
    HeaderComponent,
    HomeComponent,
    ListAudiencesComponent,
    ListRolesComponent,
    EditRoleAudiencesComponent,
    ModifyRoleComponent,
    LoginComponent,
    UnauthorizedComponent
  ],
  imports: [
    BrowserModule,
    ConfirmDialogModule,
    FormsModule,
    HttpModule,
    ListboxModule,
    PickListModule,
    ReactiveFormsModule,
    RouterModule.forRoot([
             path: '', redirectTo: 'home', pathMatch: 'full' ,
             path: 'home', component: HomeComponent ,
             path: 'unauthorized', component: UnauthorizedComponent ,
             path: 'audiences', component: ListAudiencesComponent, canActivate: [AuthGuard] ,
             path: 'roles', component: ListRolesComponent, canActivate: [AuthGuard] ,
             path: 'roles/modify/:name', component: ModifyRoleComponent, canActivate: [AuthGuard] ,
             path: '**', redirectTo: 'home' 
        ]),
  ],
  providers: [
    
      provide: AuthHttp,
      useFactory: authHttpServiceFactory,
      deps: [Http, RequestOptions]
    ,
    AudienceService, AuthGuard, AuthService, LoggingService, RoleService, UserService
    ],
  bootstrap: [AppComponent]
)
export class AppModule  

auth.service.ts:

import  Injectable  from '@angular/core';
import  Http, Headers, RequestOptions, URLSearchParams  from '@angular/http';
import  environment  from '../../environments/environment';
import  tokenNotExpired  from 'angular2-jwt';

@Injectable()
export class AuthService 

  tokenEndpoint = environment.token_endpoint;
  constructor(private http: Http )  

  login(username: string, password: string) 
    let headers = new Headers( 'Content-Type': 'application/x-www-form-urlencoded' );
    let options = new RequestOptions( headers: headers );
    let body = new URLSearchParams();
    body.set('username', username);
    body.set('password', password);
    body.set('client_id', '099153c2625149bc8ecb3e85e03f0022');
    body.set('grant_type', 'password');

    console.log("Got here");

    return this.http.post(this.tokenEndpoint, body, options)
    .map(res => res.json())
    .subscribe(
        data => 
          localStorage.setItem('id_token', data.access_token);
          localStorage.setItem('refresh_token', data.refresh_token);
        ,
        error => console.log(error)
      );
  

  loggedIn() 
    if (tokenNotExpired()) 
      return true;
     else 
      this.refreshToken()
      .subscribe(
          data => 
            if (data.error) 
              this.logout();
             else 
              localStorage.setItem('id_token', data.access_token);
              localStorage.setItem('refresh_token', data.refresh_token);
              console.log("Token was refreshed.");
            
          ,
          error => this.logout(),
          () =>  
            return tokenNotExpired();
          
        );
    
  

  refreshToken() 
    let refToken = localStorage.getItem('refresh_token');
    if (refToken) 
      let headers = new Headers( 'Content-Type': 'application/x-www-form-urlencoded' );
      let options = new RequestOptions( headers: headers );
      let body = new URLSearchParams();
      body.set('client_id', '099153c2625149bc8ecb3e85e03f0022');
      body.set('grant_type', 'refresh_token');
      body.set('refresh_token', refToken);

      return this.http.post(this.tokenEndpoint, body, options)
      .map(res => res.json());
     else 
      this.logout();
    
  

  tokenRequiresRefresh(): boolean 
    if (!this.loggedIn()) 
      console.log("Token refresh is required");
    

    return !this.loggedIn();
  

  logout() 
    localStorage.removeItem('id_token');
    localStorage.removeItem('refresh_token');
  

audience.service.ts:

import 'rxjs/Rx';
import  Observable  from 'rxjs/Observable';
import  environment  from '../../environments/environment';
import  AuthHttp  from 'angular2-jwt';
import  AuthService  from './auth.service';

import  AddDeleteAudienceModel  from './AddAudienceModel';

@Injectable()
export class AudienceService 

  baseApiUrl = environment.api_endpoint;

  constructor(private http: Http, private authHttp: AuthHttp, private authService: AuthService)  

  getAllAudiences()
  
    if (this.authService.tokenRequiresRefresh()) 
      this.authService.refreshToken();
    

    if (this.authService.loggedIn()) 
      return this.authHttp.get(this.baseApiUrl + 'audience/all').map(res => res.json());
    
  

【问题讨论】:

这段代码有什么问题?除非您想在每个请求中包含刷新令牌并在后端自动刷新,否则我认为没有其他方法。 你找到解决这个问题的方法了吗?我有同样的问题。 @VinayPandya 是的,我有。我会尽快发布我的答案。 【参考方案1】:

auth.service.ts

import  Injectable  from '@angular/core';
import  Router  from '@angular/router';
import  Http, Headers, RequestOptions, URLSearchParams  from '@angular/http';
import  environment  from '../../environments/environment';
import  tokenNotExpired, JwtHelper  from 'angular2-jwt';
import  Subject, Observable  from 'rxjs';

@Injectable()
export class AuthService 

  tokenEndpoint = environment.token_endpoint;
  requireLoginSubject: Subject<boolean>;
  tokenIsBeingRefreshed: Subject<boolean>;
  lastUrl: string;
  jwtHelper: JwtHelper = new JwtHelper();

  constructor(private http: Http, private router: Router)  
    this.requireLoginSubject = new Subject<boolean>();
    this.tokenIsBeingRefreshed = new Subject<boolean>();
    this.tokenIsBeingRefreshed.next(false);
    this.lastUrl = "/home";
  

  isUserAuthenticated() 

    if(this.loggedIn()) 
      this.requireLoginSubject.next(false);
      return true;
     else 
      return false;
    
  

  login(username: string, password: string) 
    let headers = new Headers( 'Content-Type': 'application/x-www-form-urlencoded' );
    let options = new RequestOptions( headers: headers );
    let body = new URLSearchParams();
    body.set('username', username);
    body.set('password', password);
    body.set('client_id', '099153c2625149bc8ecb3e85e03f0022');
    body.set('grant_type', 'password');

    return this.http.post(this.tokenEndpoint, body, options).map(res => res.json());
  

  loggedIn() 
    return tokenNotExpired();
  

  addTokens(accessToken: string, refreshToken: string) 
    localStorage.setItem('id_token', accessToken);
    localStorage.setItem('refresh_token', refreshToken);
  

  getRefreshTokenExpirationDate() 
    var token = localStorage.getItem('id_token');
    if (token) 
      let tokenExpDate = this.jwtHelper.getTokenExpirationDate(token);
      let sessionExpDate = new Date(tokenExpDate.getTime() + 4*60000);
      if (new Date() > sessionExpDate) 
        this.logout();
      
      return sessionExpDate;
    

    return null;
  

  hasRefreshToken() 
    let refToken = localStorage.getItem('refresh_token');

    if (refToken == null) 
      this.logout();
    

    return refToken != null;
  

  refreshTokenSuccessHandler(data) 
    if (data.error) 
        console.log("Removing tokens.");
        this.logout();
        this.requireLoginSubject.next(true);
        this.tokenIsBeingRefreshed.next(false);
        this.router.navigateByUrl('/unauthorized');
        return false;
     else 
        this.addTokens(data.access_token, data.refresh_token);
        this.requireLoginSubject.next(false);
        this.tokenIsBeingRefreshed.next(false);
        console.log("Refreshed user token");
    
  

  refreshTokenErrorHandler(error) 
    this.requireLoginSubject.next(true);
    this.logout();
    this.tokenIsBeingRefreshed.next(false);
    this.router.navigate(['/sessiontimeout']);
    console.log(error);
  

  refreshToken() 
    let refToken = localStorage.getItem('refresh_token');
    //let refTokenId = this.jwtHelper.decodeToken(refToken).refreshTokenId;
    let headers = new Headers( 'Content-Type': 'application/x-www-form-urlencoded' );
    let options = new RequestOptions( headers: headers );
    let body = new URLSearchParams();
    body.set('client_id', '099153c2625149bc8ecb3e85e03f0022');
    body.set('grant_type', 'refresh_token');
    body.set('refresh_token', refToken);

    return this.http.post(this.tokenEndpoint, body, options)
      .map(res => res.json());
  

  tokenRequiresRefresh(): boolean 
    if (!this.loggedIn()) 
      console.log("Token refresh is required");
    

    return !this.loggedIn();
  

  logout() 
    localStorage.removeItem('id_token');
    localStorage.removeItem('refresh_token');
    this.requireLoginSubject.next(true);
  

auth-http.service.ts

import  Injectable  from '@angular/core';
import  Router  from '@angular/router';
import 'rxjs/Rx';
import  Observable  from 'rxjs/Observable';
import  environment  from '../../environments/environment';
import  AuthHttp  from 'angular2-jwt';
import  AuthService  from './auth.service';

@Injectable()
export class AuthHttpService 

  constructor(private authHttp: AuthHttp, private authService: AuthService, private router: Router)  

  get(endpoint: string) 
    if (this.authService.tokenRequiresRefresh()) 
      this.authService.tokenIsBeingRefreshed.next(true);
      return this.authService.refreshToken().switchMap(
        (data) => 
          this.authService.refreshTokenSuccessHandler(data);
          if (this.authService.loggedIn()) 
            this.authService.tokenIsBeingRefreshed.next(false);
            return this.getInternal(endpoint);
           else 
            this.authService.tokenIsBeingRefreshed.next(false);
            this.router.navigate(['/sessiontimeout']);
            return Observable.throw(data);
          
        
      ).catch((e) => 
        this.authService.refreshTokenErrorHandler(e);
        return Observable.throw(e);
      );
    
    else 
      return this.getInternal(endpoint);
    
  

  post(endpoint: string, body: string) : Observable<any> 
    if (this.authService.tokenRequiresRefresh()) 
      this.authService.tokenIsBeingRefreshed.next(true);
      return this.authService.refreshToken().switchMap(
        (data) => 
          this.authService.refreshTokenSuccessHandler(data);
          if (this.authService.loggedIn()) 
            this.authService.tokenIsBeingRefreshed.next(false);
            return this.postInternal(endpoint, body);
           else 
            this.authService.tokenIsBeingRefreshed.next(false);
            this.router.navigate(['/sessiontimeout']);
            return Observable.throw(data);
          
        
      ).catch((e) => 
        this.authService.refreshTokenErrorHandler(e);
        return Observable.throw(e);
      );
    
    else 
      return this.postInternal(endpoint, body);
    
  

  private getInternal(endpoint: string) 
    return this.authHttp.get(endpoint);
  

  private postInternal(endpoint: string, body: string) 
    return this.authHttp.post(endpoint, body);
  


audience.service.ts

import  Injectable  from '@angular/core';
import 'rxjs/Rx';
import  Observable  from 'rxjs/Observable';
import  environment  from '../../environments/environment';
import  AuthHttpService  from './auth-http.service';

import  AddDeleteAudienceModel  from './AddAudienceModel';

@Injectable()
export class AudienceService 

  baseApiUrl = environment.api_endpoint;

  constructor(private authHttpService: AuthHttpService)  

  getAllAudiences()
  
    return this.authHttpService.get(this.baseApiUrl + 'audience/all').map(res => res.json());
  

  addAudience(model: AddDeleteAudienceModel) 
    return this.authHttpService.post(this.baseApiUrl + 'audience', JSON.stringify(model)).map(res => res.json());
  

  deleteAudience(model: AddDeleteAudienceModel) 
    return this.authHttpService.post(this.baseApiUrl + 'audience/delete', JSON.stringify(model)).map(res => res.json());
  


【讨论】:

localStorage.setItem('refresh_token', refreshToken);。不确定这是推荐的。根据this 文章,单页应用程序(通常实现隐式授予)在任何情况下都不应该获得刷新令牌。原因是这条信息的敏感性。您可以将其视为用户凭据,因为刷新令牌允许用户基本上永远保持身份验证。因此,您无法在浏览器中获取此信息,它必须安全存储。

以上是关于Angular2-jwt - AuthHttp,刷新令牌,把它们放在一起的主要内容,如果未能解决你的问题,请参考以下文章

angular2-jwt 编译元数据解析器错误

Angular 6 中的 AuthHttp(从 Angular2 迁移到 Angular6)

带有jwt的Angular 2 AuthHttp未连接

Angular 2 JWT AuthHttp 错误 401 未捕获

angular2-jwt 令牌一直无效

令牌未过期模块不起作用,Angular 6