401 令牌刷新 API 调用期间出现未经授权的错误

问题描述 投票:0回答:1

我是 Angular 18 的新手,我无法解决刷新令牌的实现,我的后端在 PostMan 工作正常,使用curl命令也可以,我发送带有刷新令牌的标头和后端响应带有 accessToken 的对象也可以正常工作,但是当我使用前端时,错误是 401!请帮助我,非常感谢!

这是拦截器

export const customInterceptor: HttpInterceptorFn = (req, next) => {
    const excludedRoutes = ['/login']; // Customizable array
    const shouldExclude = excludedRoutes.some(route => req.url.includes(route));
    const authService = inject(AuthService);

    if (shouldExclude) {
        return next(req);
    }

    const token = authService.getAuthToken();
    const cloneRequest = req.clone({
        setHeaders: {
            Authorization: `Bearer ${token}`
        }
    });

    return next(cloneRequest).pipe(
        catchError((error) => {
            if (error.status === 401) {
                console.log("From the interceptor, here is the error: " + error.status);
                return authService.refreshToken().pipe(
                    switchMap((res) => {
                        localStorage.setItem('authToken', res.accessToken);
                        console.log("From the interceptor, the new authToken is: " + res.accessToken);
                        const newReq = req.clone({
                            setHeaders: {
                                Authorization: `Bearer ${res.accessToken}`
                            }
                        });

                        return next(newReq);
                    }),
                    catchError((refreshErr) => {
                        const finalError = new Error(refreshErr);
                        console.log("Error refreshing the token", finalError);
                        authService.logout();
                        return throwError(() => new Error('Session expired, please log in again'));
                    })
                );
            } else {
                // Handle other errors (e.g., network errors)
                return throwError(() => error);
            }
        })
    );
};

这是服务方法:

refreshToken() {
    const refreshToken = localStorage.getItem('refreshToken');
    console.log("The refreshToken inside the method is: " + refreshToken);

    if (!refreshToken) {
        throw new Error('Refresh token missing');
    }
    
    console.log("Sending refresh token to backend: " + refreshToken);
    
    return this._httpClient.post<any>(`${this.baseURL}/refresh`, {}, {
        headers: {
            Authorization: `Bearer ${refreshToken}`
        }
    }).pipe(
        catchError((error) => {
            console.error("Refresh token error:", error);
            return throwError(() => new Error("Refresh error"));
        })
    );
}
angular jwt refresh-token angular-http-interceptors
1个回答
0
投票

您的 customInterceptor 似乎大部分设置正确,但可能有几个原因导致您在 Angular 前端收到 401 错误:

令牌同步问题:可能存在拦截器在获取新令牌后仍尝试使用旧令牌的问题。 拦截器链接问题:如果注册了多个拦截器,它们可能无法正确处理 401 错误,从而导致令牌刷新无法正确传播。 API 请求计时:在发出新请求之前,令牌刷新可能尚未完成。 Angular 的 HttpInterceptorFn 语法:如果您将 Angular 16+ 与 HttpInterceptorFn 函数类型一起使用,请确保正确实现设置。

尝试过这段代码:

export const customInterceptor: HttpInterceptorFn = (req, next) => {
    const excludedRoutes = ['/login']; // Exclude login endpoint from interception
    const shouldExclude = excludedRoutes.some(route => req.url.includes(route));
    const authService = inject(AuthService);

    if (shouldExclude) {
        return next(req);
    }

    const token = authService.getAuthToken();
    let cloneRequest = req;

    if (token) {
        cloneRequest = req.clone({
            setHeaders: {
                Authorization: `Bearer ${token}`
            }
        });
    }

    return next(cloneRequest).pipe(
        catchError((error) => {
            if (error.status === 401) {
                // Token is expired or invalid, try refreshing the token
                return authService.refreshToken().pipe(
                    switchMap((res) => {
                        // Store new access token
                        localStorage.setItem('authToken', res.accessToken);

                        // Clone the request with new token and resend it
                        const newReq = req.clone({
                            setHeaders: {
                                Authorization: `Bearer ${res.accessToken}`
                            }
                        });

                        // Continue the request with the new token
                        return next(newReq);
                    }),
                    catchError((refreshErr) => {
                        // Refresh token failed, handle logout or other error
                        console.log("Error refreshing the token:", refreshErr);
                        authService.logout(); // Redirect to login or handle logout
                        return throwError(() => new Error('Session expired, please log in again'));
                    })
                );
            } else {
                // For any other errors
                return throwError(() => error);
            }
        })
    );
};

在 authService 中:

refreshToken() {
    const refreshToken = localStorage.getItem('refreshToken');
    console.log("The refreshToken inside the method is: " + refreshToken);

    if (!refreshToken) {
        throw new Error('Refresh token missing');
    }
    
    return this._httpClient.post<any>(`${this.baseURL}/refresh`, {}, {
        headers: {
            Authorization: `Bearer ${refreshToken}`
        }
    }).pipe(
        catchError((error) => {
            console.error("Refresh token error:", error);
            return throwError(() => new Error("Refresh error"));
        })
    );
}
© www.soinside.com 2019 - 2024. All rights reserved.