angular - Angular JWT 刷新令牌
问题描述
我正在尝试使用基于外部 API 和 Angular 的刷新令牌来实现 JWT。我写了以下代码
令牌拦截器
import { Injectable } from '@angular/core';
import {
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor,
HttpErrorResponse
} from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError, first } from 'rxjs/operators';
import {AuthenticationService} from '../services/authentication.service'
@Injectable()
export class TokenInterceptor implements HttpInterceptor {
constructor(public authService : AuthenticationService ) {}
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
console.log(`AddTokenInterceptor - ${request.url}`);
return next.handle(this.addToken(request, localStorage.getItem('access_token')))
.pipe(catchError(error => {
if (error instanceof HttpErrorResponse && error.status === 401) {
this.refreshToken()
.pipe(first())
.subscribe(
data => {
return next.handle(this.addToken(request, localStorage.getItem('access_token')))
},
)
} else {
return throwError(error);
}
}));
}
private addToken(request: HttpRequest<any>, token: string) {
return request.clone({
setHeaders: {
'Authorization': `Bearer ${token}`
}
});
}
private refreshToken(){
return this.authService.refreshToken()
}
}
身份验证服务
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { BehaviorSubject, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class AuthenticationService {
public currentUser: string
constructor(
private http: HttpClient,
) { }
login(username:string, password:string){
return this.http.post<any>('http://localhost:8000/api/token/', {username: username, password: password})
.pipe(
map(data => {
localStorage.setItem('access_token', data.access)
localStorage.setItem('refresh_token', data.refresh)
})
)
}
logout(){
localStorage.removeItem('access_token')
localStorage.removeItem('refresh_token')
}
getJWToken(){
return localStorage.getItem('access_token')
}
getRefreshToken(){
return localStorage.getItem('refresh_token')
}
refreshToken(){
let refreshToken : string = localStorage.getItem('refresh_token');
return this.http.post<any>('http://localhost:8000/api/token/refresh/', {"refresh": refreshToken}).pipe(
map(data => {
localStorage.setItem('access_token', data.access)
})
)
}
}
主页组件
import { Component, OnInit } from '@angular/core';
import { TeamService} from '../../services/team.service'
import { first } from 'rxjs/operators';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {
teams;
constructor(private teamService : TeamService) { }
ngOnInit(): void {
this.teamService.getTeams().pipe(first()).subscribe(
data => {
this.teams = data.results
},
error => {
console.log(error.error)
}
)
}
login() : void {
console.log(this.teams)
}
}
当返回 401 响应时,我正在尝试刷新令牌,现在发生以下情况:
- 访问令牌已过期;
- 新的是请求;
- 使用 'teams' 变量渲染主组件时,控制台显示以下错误:
"You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable."
之后,当我刷新页面时,团队变量已正确加载并且可以使用。我的问题:如何在发出请求之前刷新令牌,以便始终可以使用有效的访问令牌发出请求?似乎错误在于,TokenInterceptor
但我似乎无法弄清楚如何解决这个问题
解决方案
一切看起来都很好,除了在拦截器中订阅而不是尝试在管道中映射响应。
更新
正如@ionut-t 在评论中指出的那样,必须有两个变化:
- 替换
subscription
为switchMap
运算符 catchError
返回运算符中的可观察对象
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
console.log(`AddTokenInterceptor - ${request.url}`);
return next.handle(this.addToken(request, localStorage.getItem('access_token')))
.pipe(catchError(error => {
if (error instanceof HttpErrorResponse && error.status === 401) {
return this.refreshToken()
.pipe(
first(),
switchMap( // <-- map the response instead of subscribing here
data => next.handle(this.addToken(request, localStorage.getItem('access_token')))
)
)
...
推荐阅读
- directed-acyclic-graphs - 你能让 Nextflow DAG 可视化变得漂亮吗?
- c++ - 当我使用计数功能时,Getline 不起作用
- angular - Angular NGX-SOAP 请求获取 ERR_RESPONSE_HEADERS_TRUNCATED
- python - 通过 pandas.cut() 函数创建 bin 后,如何有效地将每个值标记到 bin 中?
- flutter - MultiBlocProvider 中的 BlocProvider 事件不会在屏幕加载时发送。扑
- sql - 基于日期在 Postgres SQL 中添加计算列
- python-3.x - Python 3 将 ISO 8601 转换为毫秒
- python - 一段时间后,我的 Django 应用程序在 AWS Elastic Beanstalk 上使用 sigterm 失败
- c# - 有没有办法在不重播长时间运行的任务的情况下等待持久函数中的外部事件
- c - 当浮点数是次正规数时,frac 位会发生什么?