路由配置正在使用isAuthenticated
服务方法:
canActivate(route: ActivatedRouteSnapshot): boolean {
const expectedRole = route.data.expectedRole ? route.data.expectedRole : null;
const tokenPayload = this.authService.getDecodedAccessToken();
const role = tokenPayload.role ? tokenPayload.role : null;
if (!this.authService.isAuthenticated()) {
this.router.navigate(['login']);
return false;
} else if (role != null && role !== expectedRole) {
this.router.navigate(['login']);
return false;
} else {
return true;
}
}
此方法正在检查浏览器本地存储中的访问令牌有效性,而尚未尝试使用刷新令牌:
public isAuthenticated(): boolean {
const token = this.getAccessTokenFromLocalStorage();
return (token && !this.jwtHelperService.isTokenExpired(token));
}
我想知道如何使用刷新令牌。
我希望我的拦截器能够完成这项工作:
return this.refreshToken()
.pipe(
switchMap(() => {
request = this.addAccessToken(request);
return next.handle(request);
})
)
.pipe(
catchError(
(refreshError) => {
this.authService.logout();
return empty();
// return throwError(refreshError); TODO
})
);
然后在请求中发送刷新令牌:
private refreshToken() {
if (this.refreshTokenInProgress) {
return new Observable(observer => {
this.tokenRefreshed$.subscribe(() => {
observer.next();
observer.complete();
});
});
} else {
this.refreshTokenInProgress = true;
console.log('Sending a refresh token request...');
return this.authService.refreshAccessToken()
.pipe(
tap(() => {
console.log('The refresh token has been received');
this.refreshTokenInProgress = false;
this.tokenRefreshedSource.next();
})
);
}
}
然后将更新的访问令牌添加到下一个请求:
private addAccessToken(request): HttpRequest<any> {
if (!this.tokenService.getAccessTokenFromLocalStorage()) {
return request;
}
// The original request is immutable and cannot be changed
return this.authService.addAccessTokenToClonedRequest(request);
}
但是现在,我的isAuthenticated
方法完全忽略了这一点。
我应该修改isAuthenticated
方法以使其称为refreshToken
方法吗?还是有办法将拦截器插入路由配置?
更新:
我修改了isAuthenticated
方法:
public isAuthenticated(): boolean {
let isAuthenticated = false;
if (this.tokenService.accessTokenIsNotExpired()) {
isAuthenticated = true;
} else {
if (this.tokenService.refreshTokenIsNotExpired()) {
this.refreshAccessToken()
.pipe(
map(() => {
console.log('The access token has been refreshed');
// TODO How to resend this unauthorized request ?
})
);
}
}
return isAuthenticated;
}
但是刷新令牌响应是异步的,而canActivate
属性是同步的。所以我想我在上面的更新方法中丢失了未经授权的请求。有什么办法可以重新发送此未经授权的请求?
还有,如何处理我漂亮的拦截器?访问令牌刷新部分是否仍不使用它?更新:我现在可以回答一个问题:当客户端在isAuthenticated
方法中查找访问令牌时,当访问令牌仍然有效时,将使用拦截器访问令牌刷新,但是在请求到达时不再有效到REST令牌刷新端点,服务器将检查令牌。我会这么说。
更新: 我也尝试过这种方法,但没有帮助:
public isAuthenticated(): boolean {
let isAuthenticated = true;
if (this.tokenService.accessTokenExpired()) {
isAuthenticated = false;
if (this.tokenService.refreshTokenExpired()) {
isAuthenticated = false;
} else {
this.refreshAccessToken()
.pipe(
map((response: HttpResponse<any>) => {
console.log('The access token has been refreshed');
}),
catchError((error, caught) => {
console.log('The access token has not been refresh');
console.log(error);
return empty();
})
);
}
}
return isAuthenticated;
}
UPDATE:现在可以正常工作了,刷新令牌确实可以更新访问令牌。而且路由行为符合预期。
我更改了canActivate
方法,因为它实际上也可以使用Observable
:
canActivate(route: ActivatedRouteSnapshot): Observable<boolean> {
const expectedRole = route.data.expectedRole ? route.data.expectedRole : null;
const tokenPayload = this.tokenService.getDecodedAccessToken();
return this.authService.isAuthenticated()
.pipe(
map(isAuth => {
console.log('A response was returned');
console.log(isAuth);
if (!isAuth) {
this.router.navigate(['login']);
return false;
} else {
return true;
}
}),
catchError((error, caught) => {
console.log('An error was returned');
console.log(error);
return of(false);
})
);
}
使用isAuthenticated
方法现在看起来像:
public isAuthenticated(): Observable<boolean> {
if (this.tokenService.accessTokenExpired()) {
console.log('The access token expired.');
if (this.tokenService.refreshTokenExpired()) {
console.log('The refresh token expired.');
return of(false);
} else {
return this.refreshAccessToken()
.pipe(
map(response => {
if (response) {
console.log('The access token has been refreshed');
return true;
}
}),
catchError((error, caught) => {
console.log('The access token could not be refreshed');
console.log(error);
return of(false);
})
);
}
}
return of(true);
}
public refreshAccessToken(): Observable<any> {
console.log('Sending the refresh token to obtain a new access token');
let httpHeaders: HttpHeaders = this.httpService.buildHeader(null);
httpHeaders = this.addRefreshTokenHeader(httpHeaders);
httpHeaders = this.addClientIdHeader(httpHeaders);
return this.httpService.postWithHeadersInResponse(URI_REFRESH_TOKEN, {}, httpHeaders)
.pipe(
map((response: HttpResponse<any>) => {
// Only the access token is refreshed
// Refresing the refresh token would be like giving a never expiring refresh token
this.storeAccessTokenInLocalStorage(response);
console.log('Stored the refreshed access token in the local storage');
return true;
})
);
}
答案 0 :(得分:1)
为避免在每个HTTP请求上刷新令牌,我使用了rxjs Scheduler在到期前刷新会话。
不刷新每个请求。用户可能正在使用该网页(可能正在编写消息),但没有发送请求,因此该用户处于活动状态,但会话仍将终止。
不信任JWT中的到期时间字段来计算何时刷新令牌,因为服务器的时间可能与客户端的时间不同。如果需要,请调整计算中的时差。
此外,使用刷新令牌时,请记住设置刷新的时间限制。否则,通过反复刷新,用户可能会遇到无限会话。
这些是我面临的问题。客户端在闲置8分钟后请求会话期满。
答案 1 :(得分:0)
我认为你是对的。
在我的情况下,刷新令牌是JWT,在您的情况下,我将刷新令牌和访问令牌都保存在本地存储中。
在路由保护程序中,我查看刷新令牌是否已过期,并且我正在使用Auth0中的angular2-jwt软件包。
此外,如果打开的页面不会调用任何http请求,则应该执行一个http请求以检查访问令牌是否已过期并刷新它。您也可以警惕,这取决于应用程序。