我是第一次使用角度拦截器,我几乎拥有了我想要的东西,但是即使在谷歌搜索了一会儿之后,我仍然无法弄清某些东西。我在本地存储刷新令牌,访问令牌每15分钟失效一次;我希望能够使用刷新令牌在到期时自动刷新其身份验证令牌。
我的第一次尝试是这样的:
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (req.url.toLowerCase().includes('/auth')) {
// It's an auth request, don't get a token
return next.handle(req);
}
// Not an auth endpoint, should have a token
this.authService.GetCurrentToken().subscribe(token => {
// Make sure we got something
if (token == null || token === '') {
return next.handle(req);
}
// Have a token, add it
const request = req.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
});
return next.handle(request);
});
}
这似乎没有用,我也不知道为什么(我是Angular的新手,而JS也是新手,所以如果对其他人来说很明显,对不起。)直觉上我想知道这是否是可观察的东西弄乱了,它不喜欢等待可观察的东西返回,所以我尝试了这个:
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (req.url.toLowerCase().includes('/auth')) {
// It's an auth request, don't get a token
return next.handle(req);
}
const token = this.authService.GetAccessTokenWithoutRefresh();
const request = req.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
});
return next.handle(request);
}
现在看来可行!这表明我可能预感到是正确的(或者是我未看到的其他代码中的其他内容)。无论如何,工作还是不错的,但这给我留下了如何刷新的问题。我使用auth服务中的可观察对象的最初原因是如果需要刷新。基本上,身份验证服务将查看它的当前令牌,并查看它是否已过期。如果不是,它将仅返回of(token)
,但是如果它过期了,它将通过可观察到的http帖子返回到服务器,因此只要服务器响应,字符串就将到达。
所以我想我的问题有两个:
编辑
这是auth令牌方法中的逻辑:
GetCurrentToken(): Observable<string> {
if (this.AccessToken == null) {
return null;
}
if (this.Expiry > new Date()) {
return of(this.AccessToken);
}
// Need to refresh
return this.RefreshToken().pipe(
map<LoginResult, string>(result => {
return result.Success ? result.AccessToken : null;
})
);
}
和刷新方法:
private RefreshToken(): Observable<LoginResult> {
const refreshToken = localStorage.getItem('rt');
if (refreshToken == null || refreshToken === '') {
const result = new LoginResult();
// Set other stuff on result object
return of(result);
}
const refresh = new RefreshTokenDto();
refresh.MachineId = 'WebPortal';
refresh.TokenId = refreshToken;
return this.http.post(ApiData.baseUrl + '/auth/refresh', refresh)
.pipe(
tap<AuthResultDto>(authObject => {
this.SetLocalData(authObject);
}),
map<AuthResultDto, LoginResult>(authObject => {
const result = new LoginResult();
// Set other stuff on the result object
return result;
}),
catchError(this.handleError<LoginResult>('Refresh'))
);
}
答案 0 :(得分:0)
您可以尝试以下方法。我可能会夸大其中的FP:
export class AuthInterceptor {
ctor(private authService: AuthService){}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return of(req.url.toLowerCase().includes('/auth')).pipe(
mergeMap(isAuthRequest => !isAuthRequest
// Missing: handle error when accessing the access token
? this.authService.accessToken$.pipe(map(addAuthHeader(req)))
: of(req)
),
mergeMap(nextReq => next.handle(nextReq))
);
}
}
function addAuthHeader(req: HttpRequest<any>): (token:string)=> HttpRequest<any> {
return token => req.clone({setHeaders: {Authorization: `Bearer ${token}`}})
}
以及身份验证服务:
export class AuthService {
ctor(private http: HttpClient){}
get accessToken$(): Observable<string> {
return of(this.AccessToken).pipe(
mergeMap(token => token === null
? throwError("Access token is missing")
: of(this.Expiry > new Date())
),
mergeMap(accessTokenValid => accessTokenValid
? of(this.AccessToken)
: this.refreshToken()
)
);
}
refreshToken(): Observable<string> {
return of(localStorage.getItem('rt')).pipe(
mergeMap(refreshToken => !refreshToken
? of(extractAccessTokenFromLogin(createLoginResult())
: this.requestAccessToken(this.createRefreshToken(refreshToken))
)
);
}
private requestAccessToken(refreshToken: RefreshTokenDto): Observable<string> {
return this.http.post<AuthResultDto>(ApiData.baseUrl + '/auth/refresh', refreshToken)
.pipe(
tap(auth => this.SetLocalData(auth )),
map(auth => this.mapAuthObjToLoginRes(auth)),
map(extractAccessTokenFromLogin)
catchError(this.handleError<string>('Refresh'))
)
}
private createRefreshToken(tokenId: string): RefreshTokenDto{...}
private createLoginRes(): LoginResult {...}
private mapAuthObjToLoginRes(val: AuthResultDto): LoginResult{...}
}
function extractAccessTokenFromLogin(login: LoginResult): string
=> login.Success ? login.AccessToken : null;