我正在尝试实现401响应的捕获,并尝试在令牌刷新后基于Angular 4 Intericetor重试请求获取刷新令牌。我试图实现同样的事情,但我始终无法重试该请求,我真的不确定这是否是应用刷新令牌策略的最佳方法。这是我的代码:
@Injectable()
export class AuthInterceptorService implements HttpInterceptor {
public authService;
refreshTokenInProgress = false;
tokenRefreshedSource = new Subject();
tokenRefreshed$ = this.tokenRefreshedSource.asObservable();
constructor(private router: Router, private injector: Injector) { }
authenticateRequest(req: HttpRequest<any>) {
const token = this.authService.getToken();
if (token != null) {
return req.clone({
headers: req.headers.set('Authorization', `Bearer ${token.access_token}`)
});
}
else {
return null;
}
}
refreshToken() {
if (this.refreshTokenInProgress) {
return new Observable(observer => {
this.tokenRefreshed$.subscribe(() => {
observer.next();
observer.complete();
});
});
} else {
this.refreshTokenInProgress = true;
return this.authService.refreshToken()
.do(() => {
this.refreshTokenInProgress = false;
this.tokenRefreshedSource.next();
}).catch(
(error) => {
console.log(error);
}
);
}
}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
this.authService = this.injector.get(AuthenticationService);
request = this.authenticateRequest(request);
return next.handle(request).do((event: HttpEvent<any>) => {
if (event instanceof HttpResponse) {
// do stuff with response if you want
}
}, (err: any) => {
if (err instanceof HttpErrorResponse) {
if (err.status === 401) {
return this.refreshToken()
.switchMap(() => {
request = this.authenticateRequest(request);
console.log('*Repeating httpRequest*', request);
return next.handle(request);
})
.catch(() => {
return Observable.empty();
});
}
}
});
}
}
问题是在中从未到达切换映射...
if (err.status === 401) {
return this.refreshToken()
.switchMap(() => {
还有do运算符…
return this.authService.refreshToken()
.do(() => {
这让我想到了我的authService refreshToken方法...
refreshToken() {
let refreshToken = this.getToken();
refreshToken.grant_type = 'refresh_token';
refreshToken.clientId = environment.appSettings.clientId;
return this.apiHelper.httpPost(url, refreshToken, null)
.map
(
response => {
this.setToken(response.data, refreshToken.email);
return this.getToken();
}
).catch(error => {
return Observable.throw('Please insert credentials');
});
}
}
它返回一个映射的可观察值,我知道如果我替换了do in,它需要订阅。。。
return this.authService.refreshToken()
.do(() => {
通过订阅,我想我会打破可观察到的链条。我迷路了,我已经玩了很长时间没有解决办法。: D
下面的拦截器为您完成此任务
import {
throwError as observableThrowError,
Observable,
Subject,
EMPTY,
} from 'rxjs';
import { catchError, switchMap, tap, finalize } from 'rxjs/operators';
import { Injectable } from '@angular/core';
import {
HttpInterceptor,
HttpRequest,
HttpHandler,
HttpSentEvent,
HttpHeaderResponse,
HttpProgressEvent,
HttpResponse,
HttpUserEvent,
HttpErrorResponse,
} from '@angular/common/http';
import { StoreService } from './store.service';
import { ApiService } from './api.service';
export const tokenURL = '/315cfb2a-3fdf-48c3-921f-1d5209cb7861'; //copied from api service
@Injectable()
export class SessionInterceptorService implements HttpInterceptor {
isRefreshingToken: boolean = false;
cachedRequests = [];
tokenSubject: Subject<string> = new Subject<string>();
constructor(
private readonly store: StoreService,
private readonly ApiService: ApiService
) {}
intercept(
req: HttpRequest<any>,
next: HttpHandler
): Observable<
| HttpSentEvent
| HttpHeaderResponse
| HttpProgressEvent
| HttpResponse<any>
| HttpUserEvent<any>
> {
let urlPresentIndex = this.cachedRequests.findIndex(
(httpRequest) => httpRequest.url == req.url
);
if (this.isRefreshingToken && !req.url.endsWith(tokenURL)) {
// check if unique url to be added in cachedRequest
if (urlPresentIndex == -1) {
this.cachedRequests.push(req);
return this.tokenSubject.pipe(
switchMap(() => next.handle(req)),
tap((v) => {
// delete request from catchedRequest if api gets called
this.cachedRequests.splice(
this.cachedRequests.findIndex(
(httpRequest) => httpRequest.url == req.url
),
1
);
return EMPTY;
})
);
} else {
//already in cached request array
return EMPTY;
}
}
return next.handle(this.updateHeader(req)).pipe(
catchError((error) => {
console.log(error);
if (error instanceof HttpErrorResponse) {
switch ((<HttpErrorResponse>error).status) {
case 400:
return this.handle400Error(error);
case 403 || 401:
if (req.url.endsWith(tokenURL)) {
return observableThrowError(error);
} else {
this.cachedRequests.push(req);
return this.handle401Error(req, next);
}
default:
return observableThrowError(error);
}
} else {
return observableThrowError(error);
}
})
);
}
handle400Error(error) {
if (
error &&
error.status === 400 &&
error.error &&
error.error.error === 'invalid_grant'
) {
// If we get a 400 and the error message is 'invalid_grant', the token is no longer valid so logout.
return this.logout();
}
return observableThrowError(error);
}
handle401Error(req: HttpRequest<any>, next: HttpHandler) {
if (!this.isRefreshingToken) {
this.isRefreshingToken = true;
return this.ApiService.refreshToken().pipe(
switchMap((newToken: string) => {
if (newToken) {
this.store.updateAccessToken(newToken);
this.tokenSubject.next(newToken);
return next.handle(this.updateHeader(this.cachedRequests[0]));
}
// If we don't get a new token, we are in trouble so logout.
return this.logout();
}),
catchError((error) => {
// If there is an exception calling 'refreshToken', bad news so logout.
return this.logout();
}),
finalize(() => {
this.isRefreshingToken = false;
})
);
}
}
logout() {
console.log('logging it out');
// Route to the login page (implementation up to you)
return observableThrowError('');
}
/*
This method is append token in HTTP request'.
*/
updateHeader(req) {
const authToken = this.store.getAccessToken();
console.log(authToken);
req = req.clone({
headers: req.headers.set('X-RapidAPI-Key', `${authToken}`),
});
return req;
}
}
有关更多详细信息,您可以阅读我的中型文章令牌刷新侦听器重试失败的请求
看看它的工作原理
我很高兴你喜欢我的解决方案。我将把最终的解决方案放在这里,但是如果有人想知道我放弃的过程,请访问这里:刷新令牌OAuth身份验证Angular 4
好的,首先我创建了一个服务来保存刷新令牌请求的状态,并观察以知道请求何时完成。
@Injectable()
export class RefreshTokenService {
public processing: boolean = false;
public storage: Subject<any> = new Subject<any>();
public publish(value: any) {
this.storage.next(value);
}
}
我注意到,如果我有两个拦截器,一个用于刷新令牌并处理它,另一个用于放置授权标头(如果存在),那就更好了。
@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);
}
}
}
因此,我在这里等待刷新令牌可用或失败,然后释放需要授权头的请求。
@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();
}
}
});
}
}
@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进行测试,但我不知道它是否适用于Angular 5,我认为应该有效。
您好,我正试图通过刷新令牌并重试请求,找出如何实现新的角度拦截器和处理错误。这是我一直遵循的指南:https://ryanchenkie.com/angular-authentication-using-the-http-client-and-http-interceptors 我成功缓存失败的请求,并可以刷新令牌,但我不知道如何重新发送以前失败的请求。我还想让它与我目前使用的解析器一起工作。 t
正如标题所说,我正在使用OAuth身份验证进行Angular 4项目。 每当http请求以状态代码401响应时,我将拦截该请求,续订访问令牌并重试失败的请求。 当我收到401时,请求被正确截获,访问令牌被刷新。失败的请求也会再次执行,但不再将其响应传递给组件。 因此,问题在于,我的组件(应该在请求响应上进行观察)在刷新令牌和重试请求之前,会抱怨视图的日志“接收属性时出错”。 我的拦截器: 我的服务
我正在构建一个移动应用程序,并且正在使用JWT进行身份验证。 最好的方法似乎是将JWT访问令牌与刷新令牌配对,这样我就可以根据需要频繁地使访问令牌过期。 刷新令牌是什么样子的?是随机字符串吗?那串加密了吗?是另一个JWT吗? 刷新令牌将存储在用户模型的数据库中以便访问,对吗?在这种情况下似乎应该加密 在用户登录后,我是否会将刷新令牌发送回,然后让客户端访问单独的路由来检索访问令牌?
我在刷新访问令牌时得到这个错误:访问令牌无法刷新。请重新验证 这一错误此前曾在2017年4月报告过。OneLogin文档声明刷新令牌可以使用45天左右。我的刷新令牌已经有20个小时了。是文档正确还是刷新令牌的寿命更短?我可以做获取访问令牌和撤销令牌罚款。 public RootObject RefreshToken(HttpRequesterDM rDM){restsharp.deserializ
我如何从第一次授权代码中获得刷新令牌和访问令牌?并且,我如何重用这个刷新令牌来获得一个新的访问令牌,以便使用Java API上传到Google Drive?这不是一个web应用程序。它在Java Swing代码中。
我正在构建一个使用JWT进行身份验证的应用程序。我开始做一些研究,但对于诸如刷新令牌和令牌存储之类的主题缺乏共识,我感到惊讶。 据我所知,JWT和OAuth是两个不同的协议,它们遵循不同的规范。 但我的问题是,对于一个没有通过第三方资源服务器如Google、Facebook等认证的应用程序,有一个刷新令牌真的有用吗?为什么不让JWT令牌像刷新令牌一样持续时间长。 另一方面,我可以看到,如本文所述,