Angular 保护路由以使用刷新令牌
路线配置正在使用Angular 保护路由以使用刷新令牌,angular,Angular,路线配置正在使用isAuthenticated服务方法: canActivate(route: ActivatedRouteSnapshot): boolean { const expectedRole = route.data.expectedRole ? route.data.expectedRole : null; const tokenPayload = this.authService.getDecodedAccessToken(); const role = tokenP
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);
}
然后将更新的访问令牌添加到下一个请求:
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);
}
但是刷新令牌响应是异步的,而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;
}
通过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;
})
);
}
public isAuthenticated():可观察{
if(this.tokenService.accessTokenExpired()){
log('访问令牌已过期');
if(this.tokenService.refreshtTokenExpired()){
log('刷新令牌已过期');
归还(假);
}否则{
返回此。refreshAccessToken()
.烟斗(
映射(响应=>{
如果(答复){
log('访问令牌已刷新');
返回true;
}
}),
catchError((错误,捕获)=>{
log('无法刷新访问令牌');
console.log(错误);
归还(假);
})
);
}
}
返回(真);
}
public refreshAccessToken():可观察{
log('发送刷新令牌以获取新的访问令牌');
让httpHeaders:httpHeaders=this.httpService.buildHeader(null);
httpHeaders=this.addRefreshTokenHeader(httpHeaders);
httpHeaders=this.addClientHeader(httpHeaders);
返回此.httpService.postWithHeadersInResponse(URI\u REFRESH\u令牌,{},httpHeaders)
.烟斗(
map((响应:HttpResponse)=>{
//仅刷新访问令牌
//重新设计刷新令牌就像给出一个永不过期的刷新令牌
此.storeAccessTokenInLocalStorage(响应);
log('将刷新的访问令牌存储在本地存储器中');
返回true;
})
);
}
我认为你是对的
在我的例子中,刷新令牌是JWT,与您的例子一样,我将刷新和访问令牌都保存在本地存储中。切勿将其保存在本地存储中。身份验证令牌只能保存在带有httpOnly标志的Cookie中
在route guard中,我观察刷新令牌是否过期,为此我使用Auth0的angular2 jwt包
此外,如果您打开的页面不会调用任何http请求,您应该执行一个http请求来检查访问令牌是否过期并刷新它。您也可以在guard中执行此操作,这取决于应用程序。以避免在每次HTTP请求上刷新令牌,我用于在过期之前刷新会话 不要每次请求都刷新。用户可能正在使用网页(可能正在编写消息),但没有发送请求,因此用户处于活动状态,但会话仍将过期 不要信任JWT中的过期时间字段来计算何时刷新令牌,因为服务器的时间可能与客户端的时间不同。如果需要,调整计算中的时间差 另外,在使用刷新令牌时,请记住设置刷新的时间限制。否则,用户可能通过反复刷新而拥有无限会话
这些就是我面临的问题。客户端在8分钟不活动后请求会话过期。总之,您理解我的问题:-)我计划让访问令牌在一小时后过期,刷新令牌在一周后过期。我遇到的问题是,由于令牌刷新异步进程将
isAuthenticated
设置为false,我得到了未经授权的令牌,将用户重定向到登录页面。那么您没有使用canActivate
功能?那么你如何确保你的路线安全呢?或者,如果您确实使用了它,那么如何让它与刷新令牌一起更新访问令牌服务器端?我认为您不应该自动刷新令牌。若用户正在观看视频或写书(即使可能是一周),对于类似的不同情况,您应该逐个刷新令牌。