Angular 如何使用拦截器自动刷新访问令牌+;路障?
我浏览了大量的文章和问题,但似乎没有一篇涉及到拦截器中的自动令牌刷新,同时还有一个路由保护,它等待服务器完成请求,以验证访问令牌是否有效 auth.guard.tsAngular 如何使用拦截器自动刷新访问令牌+;路障?,angular,jwt,Angular,Jwt,我浏览了大量的文章和问题,但似乎没有一篇涉及到拦截器中的自动令牌刷新,同时还有一个路由保护,它等待服务器完成请求,以验证访问令牌是否有效 auth.guard.ts canActivate( route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | Url
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
return this.authService.isAuthenticated()
.pipe(
map(
res => {
if (res.success === true) {
return true;
} else {
this.authService.deleteToken();
this.router.navigateByUrl('/login');
return false;
}
}
), catchError(err => {
// NOTE: If I put the code for refresh-token request here, it works -- user stays
// logged in after getting new access token
this.authService.deleteToken();
this.router.navigate(['/login']);
return of(false);
})
);
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (req.headers.get('noauth')) {
return next.handle(req.clone());
} else {
const clonedReq = req.clone({
headers: req.headers.set('Authorization', 'Bearer ' + this.authService.getToken())
});
return next.handle(clonedReq).pipe(
tap(
event => {},
err => {
if (err.error.code && err.error.code === 'EXPIRED') {
return this.handle401Error(clonedReq, next);
}
}
)
);
}
}
private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
if (!this.isRefreshing) {
this.isRefreshing = true;
this.refreshTokenSubject.next(null);
return this.authService.refreshToken().pipe(
switchMap((token: any) => {
this.isRefreshing = false;
this.refreshTokenSubject.next(token.new_access_token);
return next.handle(this.addToken(request, token.new_access_token));
}),
catchError(err => {
return throwError(err);
}))
.subscribe(r => r);
} else {
return this.refreshTokenSubject.pipe(
filter(token => token != null),
take(1),
switchMap(jwt => {
return next.handle(this.addToken(request, jwt));
}));
}
}
private addToken(request: HttpRequest<any>, token: string) {
return request.clone({
setHeaders: {
'Authorization': `Bearer ${token}`
}
});
}
refreshToken() {
return this.http.post<any>(`${environment.api}/refresh-token`, {
'refreshToken': this.getRefreshToken()
}, this.noAuthHeader).pipe(tap((res) => {
if (res.success === true) {
this.setToken(res.new_access_token);
return true;
} else {
this.deleteToken();
this.router.navigateByUrl('/login');
return false;
}
}));
}
return this.authService.isAuthenticated()
.pipe(
map(
res => {
if (res.success === true) {
return true;
} else {
this.authService.deleteToken();
this.router.navigateByUrl('/login');
return false;
}
}
), catchError(
err => {
// Absolutely needed this handler,
// but removed the code for redirection to login and deletion of tokens
return of(false);
}
)
);
return next.handle(clonedReq)
.pipe(
catchError((err: HttpErrorResponse) => {
if (err.error.code === 'EXPIRED') {
return this.handle401Error(clonedReq, next);
} else if (err.error.status === 401) {
this.router.navigateByUrl('/login');
return throwError(err.error.message);
}
// Redirect to an error landing page
return throwError(err.error.message);
})
);
private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
if (!this.isRefreshing) {
this.isRefreshing = true;
this.refreshTokenSubject.next(null);
return this.authService.refreshToken()
.pipe(
switchMap((token: any) => {
this.refreshTokenSubject.next(token.new_access_token);
this.authService.setToken(token.new_access_token);
return next.handle(this.addToken(request, token.new_access_token));
}), catchError(err => {
// .subscribe() not needed!Just an error handler
if (err.error.code === 'EXPIRED') {
this.authService.deleteToken();
this.router.navigateByUrl('/login');
return throwError(err.error.message);
}
// Redirect to error landing page
return throwError(err.error.message);
}),
finalize(() => {
this.isRefreshing = false;
})
);
...
canActivate(
路由:ActivatedRouteSnapshot,
状态:RouterStateSnashot):可观察的|承诺|布尔| UrlTree{
返回此.authService.isAuthenticated()
.烟斗(
地图(
res=>{
如果(res.success==true){
返回true;
}否则{
this.authService.deleteToken();
this.router.navigateByUrl('/login');
返回false;
}
}
),catchError(错误=>{
//注意:如果我把刷新令牌请求的代码放在这里,它就可以工作了——用户留下来
//获取新访问令牌后登录
this.authService.deleteToken();
this.router.navigate(['/login']);
归还(假);
})
);
}
auth.interceptor.ts
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
return this.authService.isAuthenticated()
.pipe(
map(
res => {
if (res.success === true) {
return true;
} else {
this.authService.deleteToken();
this.router.navigateByUrl('/login');
return false;
}
}
), catchError(err => {
// NOTE: If I put the code for refresh-token request here, it works -- user stays
// logged in after getting new access token
this.authService.deleteToken();
this.router.navigate(['/login']);
return of(false);
})
);
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (req.headers.get('noauth')) {
return next.handle(req.clone());
} else {
const clonedReq = req.clone({
headers: req.headers.set('Authorization', 'Bearer ' + this.authService.getToken())
});
return next.handle(clonedReq).pipe(
tap(
event => {},
err => {
if (err.error.code && err.error.code === 'EXPIRED') {
return this.handle401Error(clonedReq, next);
}
}
)
);
}
}
private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
if (!this.isRefreshing) {
this.isRefreshing = true;
this.refreshTokenSubject.next(null);
return this.authService.refreshToken().pipe(
switchMap((token: any) => {
this.isRefreshing = false;
this.refreshTokenSubject.next(token.new_access_token);
return next.handle(this.addToken(request, token.new_access_token));
}),
catchError(err => {
return throwError(err);
}))
.subscribe(r => r);
} else {
return this.refreshTokenSubject.pipe(
filter(token => token != null),
take(1),
switchMap(jwt => {
return next.handle(this.addToken(request, jwt));
}));
}
}
private addToken(request: HttpRequest<any>, token: string) {
return request.clone({
setHeaders: {
'Authorization': `Bearer ${token}`
}
});
}
refreshToken() {
return this.http.post<any>(`${environment.api}/refresh-token`, {
'refreshToken': this.getRefreshToken()
}, this.noAuthHeader).pipe(tap((res) => {
if (res.success === true) {
this.setToken(res.new_access_token);
return true;
} else {
this.deleteToken();
this.router.navigateByUrl('/login');
return false;
}
}));
}
return this.authService.isAuthenticated()
.pipe(
map(
res => {
if (res.success === true) {
return true;
} else {
this.authService.deleteToken();
this.router.navigateByUrl('/login');
return false;
}
}
), catchError(
err => {
// Absolutely needed this handler,
// but removed the code for redirection to login and deletion of tokens
return of(false);
}
)
);
return next.handle(clonedReq)
.pipe(
catchError((err: HttpErrorResponse) => {
if (err.error.code === 'EXPIRED') {
return this.handle401Error(clonedReq, next);
} else if (err.error.status === 401) {
this.router.navigateByUrl('/login');
return throwError(err.error.message);
}
// Redirect to an error landing page
return throwError(err.error.message);
})
);
private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
if (!this.isRefreshing) {
this.isRefreshing = true;
this.refreshTokenSubject.next(null);
return this.authService.refreshToken()
.pipe(
switchMap((token: any) => {
this.refreshTokenSubject.next(token.new_access_token);
this.authService.setToken(token.new_access_token);
return next.handle(this.addToken(request, token.new_access_token));
}), catchError(err => {
// .subscribe() not needed!Just an error handler
if (err.error.code === 'EXPIRED') {
this.authService.deleteToken();
this.router.navigateByUrl('/login');
return throwError(err.error.message);
}
// Redirect to error landing page
return throwError(err.error.message);
}),
finalize(() => {
this.isRefreshing = false;
})
);
...
intercept(req:HttpRequest,next:HttpHandler):可观察{
if(req.headers.get('noauth')){
返回next.handle(req.clone());
}否则{
const clonedReq=req.clone({
headers:req.headers.set('Authorization','Bearer'+this.authService.getToken())
});
返回next.handle(clonedReq).pipe(
水龙头(
事件=>{},
错误=>{
如果(err.error.code&&err.error.code==='EXPIRED'){
返回此.handle401错误(clonedReq,下一个);
}
}
)
);
}
}
私有句柄401错误(请求:HttpRequest,下一个:HttpHandler){
如果(!this.isRefreshing){
this.isRefreshing=true;
this.refreshTokenSubject.next(null);
返回此.authService.refreshToken()管道(
开关映射((令牌:任意)=>{
this.isRefreshing=false;
this.refreshTokenSubject.next(token.new\u access\u token);
返回next.handle(this.addToken(request,token.new_access_token));
}),
catchError(err=>{
回程抛掷器(err);
}))
.认购(r=>r);
}否则{
返回此.refreshTokenSubject.pipe(
过滤器(令牌=>令牌!=null),
以(1)为例,
开关映射(jwt=>{
返回next.handle(this.addToken(request,jwt));
}));
}
}
私有addToken(请求:HttpRequest,令牌:string){
return request.clone({
集合标题:{
'Authorization':'Bearer${token}`
}
});
}
auth.service.ts
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
return this.authService.isAuthenticated()
.pipe(
map(
res => {
if (res.success === true) {
return true;
} else {
this.authService.deleteToken();
this.router.navigateByUrl('/login');
return false;
}
}
), catchError(err => {
// NOTE: If I put the code for refresh-token request here, it works -- user stays
// logged in after getting new access token
this.authService.deleteToken();
this.router.navigate(['/login']);
return of(false);
})
);
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (req.headers.get('noauth')) {
return next.handle(req.clone());
} else {
const clonedReq = req.clone({
headers: req.headers.set('Authorization', 'Bearer ' + this.authService.getToken())
});
return next.handle(clonedReq).pipe(
tap(
event => {},
err => {
if (err.error.code && err.error.code === 'EXPIRED') {
return this.handle401Error(clonedReq, next);
}
}
)
);
}
}
private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
if (!this.isRefreshing) {
this.isRefreshing = true;
this.refreshTokenSubject.next(null);
return this.authService.refreshToken().pipe(
switchMap((token: any) => {
this.isRefreshing = false;
this.refreshTokenSubject.next(token.new_access_token);
return next.handle(this.addToken(request, token.new_access_token));
}),
catchError(err => {
return throwError(err);
}))
.subscribe(r => r);
} else {
return this.refreshTokenSubject.pipe(
filter(token => token != null),
take(1),
switchMap(jwt => {
return next.handle(this.addToken(request, jwt));
}));
}
}
private addToken(request: HttpRequest<any>, token: string) {
return request.clone({
setHeaders: {
'Authorization': `Bearer ${token}`
}
});
}
refreshToken() {
return this.http.post<any>(`${environment.api}/refresh-token`, {
'refreshToken': this.getRefreshToken()
}, this.noAuthHeader).pipe(tap((res) => {
if (res.success === true) {
this.setToken(res.new_access_token);
return true;
} else {
this.deleteToken();
this.router.navigateByUrl('/login');
return false;
}
}));
}
return this.authService.isAuthenticated()
.pipe(
map(
res => {
if (res.success === true) {
return true;
} else {
this.authService.deleteToken();
this.router.navigateByUrl('/login');
return false;
}
}
), catchError(
err => {
// Absolutely needed this handler,
// but removed the code for redirection to login and deletion of tokens
return of(false);
}
)
);
return next.handle(clonedReq)
.pipe(
catchError((err: HttpErrorResponse) => {
if (err.error.code === 'EXPIRED') {
return this.handle401Error(clonedReq, next);
} else if (err.error.status === 401) {
this.router.navigateByUrl('/login');
return throwError(err.error.message);
}
// Redirect to an error landing page
return throwError(err.error.message);
})
);
private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
if (!this.isRefreshing) {
this.isRefreshing = true;
this.refreshTokenSubject.next(null);
return this.authService.refreshToken()
.pipe(
switchMap((token: any) => {
this.refreshTokenSubject.next(token.new_access_token);
this.authService.setToken(token.new_access_token);
return next.handle(this.addToken(request, token.new_access_token));
}), catchError(err => {
// .subscribe() not needed!Just an error handler
if (err.error.code === 'EXPIRED') {
this.authService.deleteToken();
this.router.navigateByUrl('/login');
return throwError(err.error.message);
}
// Redirect to error landing page
return throwError(err.error.message);
}),
finalize(() => {
this.isRefreshing = false;
})
);
...
refreshToken(){
返回此.http.post(`${environment.api}/refresh-token`{
“refreshToken”:this.getRefreshToken()
}管道(水龙头((分辨率)=>{
如果(res.success==true){
this.setToken(res.new\u access\u token);
返回true;
}否则{
this.deleteToken();
this.router.navigateByUrl('/login');
返回false;
}
}));
}
发生的情况是:
我不确定这是怎么发生的,但我想的是,获取新访问令牌的请求应该首先完成,然后身份验证请求/auth.guard应该以此为基础?在被卡住一天半后,我想我终于自己解决了它: auth.guard.ts
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
return this.authService.isAuthenticated()
.pipe(
map(
res => {
if (res.success === true) {
return true;
} else {
this.authService.deleteToken();
this.router.navigateByUrl('/login');
return false;
}
}
), catchError(err => {
// NOTE: If I put the code for refresh-token request here, it works -- user stays
// logged in after getting new access token
this.authService.deleteToken();
this.router.navigate(['/login']);
return of(false);
})
);
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (req.headers.get('noauth')) {
return next.handle(req.clone());
} else {
const clonedReq = req.clone({
headers: req.headers.set('Authorization', 'Bearer ' + this.authService.getToken())
});
return next.handle(clonedReq).pipe(
tap(
event => {},
err => {
if (err.error.code && err.error.code === 'EXPIRED') {
return this.handle401Error(clonedReq, next);
}
}
)
);
}
}
private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
if (!this.isRefreshing) {
this.isRefreshing = true;
this.refreshTokenSubject.next(null);
return this.authService.refreshToken().pipe(
switchMap((token: any) => {
this.isRefreshing = false;
this.refreshTokenSubject.next(token.new_access_token);
return next.handle(this.addToken(request, token.new_access_token));
}),
catchError(err => {
return throwError(err);
}))
.subscribe(r => r);
} else {
return this.refreshTokenSubject.pipe(
filter(token => token != null),
take(1),
switchMap(jwt => {
return next.handle(this.addToken(request, jwt));
}));
}
}
private addToken(request: HttpRequest<any>, token: string) {
return request.clone({
setHeaders: {
'Authorization': `Bearer ${token}`
}
});
}
refreshToken() {
return this.http.post<any>(`${environment.api}/refresh-token`, {
'refreshToken': this.getRefreshToken()
}, this.noAuthHeader).pipe(tap((res) => {
if (res.success === true) {
this.setToken(res.new_access_token);
return true;
} else {
this.deleteToken();
this.router.navigateByUrl('/login');
return false;
}
}));
}
return this.authService.isAuthenticated()
.pipe(
map(
res => {
if (res.success === true) {
return true;
} else {
this.authService.deleteToken();
this.router.navigateByUrl('/login');
return false;
}
}
), catchError(
err => {
// Absolutely needed this handler,
// but removed the code for redirection to login and deletion of tokens
return of(false);
}
)
);
return next.handle(clonedReq)
.pipe(
catchError((err: HttpErrorResponse) => {
if (err.error.code === 'EXPIRED') {
return this.handle401Error(clonedReq, next);
} else if (err.error.status === 401) {
this.router.navigateByUrl('/login');
return throwError(err.error.message);
}
// Redirect to an error landing page
return throwError(err.error.message);
})
);
private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
if (!this.isRefreshing) {
this.isRefreshing = true;
this.refreshTokenSubject.next(null);
return this.authService.refreshToken()
.pipe(
switchMap((token: any) => {
this.refreshTokenSubject.next(token.new_access_token);
this.authService.setToken(token.new_access_token);
return next.handle(this.addToken(request, token.new_access_token));
}), catchError(err => {
// .subscribe() not needed!Just an error handler
if (err.error.code === 'EXPIRED') {
this.authService.deleteToken();
this.router.navigateByUrl('/login');
return throwError(err.error.message);
}
// Redirect to error landing page
return throwError(err.error.message);
}),
finalize(() => {
this.isRefreshing = false;
})
);
...
auth.interceptor.ts
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
return this.authService.isAuthenticated()
.pipe(
map(
res => {
if (res.success === true) {
return true;
} else {
this.authService.deleteToken();
this.router.navigateByUrl('/login');
return false;
}
}
), catchError(err => {
// NOTE: If I put the code for refresh-token request here, it works -- user stays
// logged in after getting new access token
this.authService.deleteToken();
this.router.navigate(['/login']);
return of(false);
})
);
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (req.headers.get('noauth')) {
return next.handle(req.clone());
} else {
const clonedReq = req.clone({
headers: req.headers.set('Authorization', 'Bearer ' + this.authService.getToken())
});
return next.handle(clonedReq).pipe(
tap(
event => {},
err => {
if (err.error.code && err.error.code === 'EXPIRED') {
return this.handle401Error(clonedReq, next);
}
}
)
);
}
}
private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
if (!this.isRefreshing) {
this.isRefreshing = true;
this.refreshTokenSubject.next(null);
return this.authService.refreshToken().pipe(
switchMap((token: any) => {
this.isRefreshing = false;
this.refreshTokenSubject.next(token.new_access_token);
return next.handle(this.addToken(request, token.new_access_token));
}),
catchError(err => {
return throwError(err);
}))
.subscribe(r => r);
} else {
return this.refreshTokenSubject.pipe(
filter(token => token != null),
take(1),
switchMap(jwt => {
return next.handle(this.addToken(request, jwt));
}));
}
}
private addToken(request: HttpRequest<any>, token: string) {
return request.clone({
setHeaders: {
'Authorization': `Bearer ${token}`
}
});
}
refreshToken() {
return this.http.post<any>(`${environment.api}/refresh-token`, {
'refreshToken': this.getRefreshToken()
}, this.noAuthHeader).pipe(tap((res) => {
if (res.success === true) {
this.setToken(res.new_access_token);
return true;
} else {
this.deleteToken();
this.router.navigateByUrl('/login');
return false;
}
}));
}
return this.authService.isAuthenticated()
.pipe(
map(
res => {
if (res.success === true) {
return true;
} else {
this.authService.deleteToken();
this.router.navigateByUrl('/login');
return false;
}
}
), catchError(
err => {
// Absolutely needed this handler,
// but removed the code for redirection to login and deletion of tokens
return of(false);
}
)
);
return next.handle(clonedReq)
.pipe(
catchError((err: HttpErrorResponse) => {
if (err.error.code === 'EXPIRED') {
return this.handle401Error(clonedReq, next);
} else if (err.error.status === 401) {
this.router.navigateByUrl('/login');
return throwError(err.error.message);
}
// Redirect to an error landing page
return throwError(err.error.message);
})
);
private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
if (!this.isRefreshing) {
this.isRefreshing = true;
this.refreshTokenSubject.next(null);
return this.authService.refreshToken()
.pipe(
switchMap((token: any) => {
this.refreshTokenSubject.next(token.new_access_token);
this.authService.setToken(token.new_access_token);
return next.handle(this.addToken(request, token.new_access_token));
}), catchError(err => {
// .subscribe() not needed!Just an error handler
if (err.error.code === 'EXPIRED') {
this.authService.deleteToken();
this.router.navigateByUrl('/login');
return throwError(err.error.message);
}
// Redirect to error landing page
return throwError(err.error.message);
}),
finalize(() => {
this.isRefreshing = false;
})
);
...
返回下一个.handle(clonedReq)
.烟斗(
catchError((错误:HttpErrorResponse)=>{
如果(err.error.code==‘过期’){
返回此.handle401错误(clonedReq,下一个);
}else if(err.error.status==401){
this.router.navigateByUrl('/login');
返回抛出器(err.error.message);
}
//重定向到错误登录页
返回抛出器(err.error.message);
})
);
私有句柄401错误(请求:HttpRequest,下一个:HttpHandler){
如果(!this.isRefreshing){
this.isRefreshing=true;
this.refreshTokenSubject.next(null);
返回此.authService.refreshToken()
.烟斗(
开关映射((令牌:任意)=>{
this.refreshTokenSubject.next(token.new\u access\u token);
this.authService.setToken(token.new\u access\u token);
返回next.handle(this.addToken(request,token.new_access_token));
}),catchError(err=>{
//.subscribe()不需要!只是一个错误处理程序
如果(err.error.code==‘过期’){
this.authService.deleteToken();
this.router.navigateByUrl('/login');
返回抛出器(err.error.message);
}
//重定向到错误登录页
返回抛出器(err.error.message);
}),
完成(()=>{
this.isRefreshing=false;
})
);
...
我希望这对您有所帮助!当您在
refreshttoken()
方法中删除旧令牌并导航到登录页面时,为什么在isAuthenticated()中出现完全catchError
)
又来了?嗨@roya,如果我没听错的话,你是说我不需要auth.guard
中的catchError
?我实际上刚刚试过,但我得到了一个错误:错误:未捕获(I