当前位置: 首页 > 知识库问答 >
问题:

从Spring Boot后端使用Angular 11拦截器刷新JWT过期令牌

翟渝
2023-03-14

我正在从事一个spring boot angular项目,其中用户从angular前端登录到spring boot上的身份验证api,该api返回一个JWT令牌。我还在Angular上设置了一个拦截器,该拦截器为所有请求附加带有JWT令牌的授权头。

我正在寻找一种拦截angualar请求的方法,这样当spring boot在JWT令牌过期后抛出401错误时,Angular前端将尝试使用过期的JWT和设置为true的新“isRefreshToken”头联系新的refreshtokenendpoint,以接收新的JWT。

这是我当前的AuthService

    @Injectable({
  providedIn: 'root',
})
export class AuthService {

  constructor(private http: HttpClient) {}

  login(username: string, password: string) {
    return this.http
      .post<iUser>('http://localhost:8080/authenticate', { username, password }).pipe(
        tap(res => this.setSession(res)),
        shareReplay()
      )      
  }

  refreshToken(){
    return this.http.post<iUser>('http://localhost:8080/refreshtoken', {responseType: 'text' as 'json'}).pipe(
      tap(res => this.setSession(res)),
      shareReplay()
    )
  }

  private setSession(authResult) {

    let tokenInfo = this.getDecodedAccessToken(authResult.token);   
    const expiresAt = moment(tokenInfo.exp);

    localStorage.setItem('id_token', authResult.token);
    localStorage.setItem('expires_at', JSON.stringify(expiresAt.valueOf()));
    localStorage.setItem('userId', tokenInfo.userId);

  }

  logout() {
    localStorage.removeItem('id_token');
    localStorage.removeItem('expires_at');
    localStorage.removeItem('userId');
  }

  public isLoggedIn() {
    return moment().isBefore(this.getExpiration());
  }

  isLoggedOut() {
    return !this.isLoggedIn();
  }

  getExpiration() {
    const expiration = localStorage.getItem('expires_at');
    const expiresAt = JSON.parse(expiration);
    return moment.unix(expiresAt);
  }

  getDecodedAccessToken(token: string): any {
    try{
        return jwt_decode(token);
    }
    catch(Error){
        return null;
    }
  }
}

这是我正在使用的拦截器:

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

  constructor(private router: Router, private authService: AuthService){}


  intercept(
    req: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {

    let url = req.url.includes('localhost');

    const idToken = localStorage.getItem('id_token');

    if (idToken && url) {  

      const cloned = req.clone({
        headers: req.headers.set('Authorization', 'Bearer ' + idToken),
      });

      console.log(cloned);

      return next.handle(cloned);

    } else {
      return next.handle(req);
    }
  }
}

共有1个答案

谢奕
2023-03-14

我可以向您建议另一种方法,我最近使用这种方法在令牌过期时注销用户。首先让我分享一下我的方法:

loginUser(email: string, password: string) {
    const authData: AuthData = { email: email, password: password };
    this.http.post<{ token: string, expiresIn: number }>('http://localhost:3000/api/users/login', authData).subscribe( response => {
      const token = response.token;
      this.token = token;
      if(token) {
        const expiresInDuration = response.expiresIn;
        this.tokenTimer = setTimeout(() => {
          this.logout();
        }, expiresInDuration*1000);
        this.isAuthenticated = true;
        this.authStatusListener.next(true);
        const now = new Date();
        const expirationDate = new Date(now.getTime() + (expiresInDuration * 1000));
        this.saveAuthData(token, expirationDate);
        this.router.navigate(['']);
      }
    });
  }

logout() {
    this.token = null;
    this.isAuthenticated = false;
    this.authStatusListener.next(false);
    clearTimeout(this.tokenTimer);
    this.clearAuthData();
    this.router.navigate(['']);
  }

  private saveAuthData(token: string, expirationDate: Date) {
    localStorage.setItem('token', token);
    localStorage.setItem('expirationDate', expirationDate.toISOString());
  }

所以我在这里所做的是,我收到了一个expireIn值,该值以令牌过期的秒为单位。然后我设置了一个timeout回调方法,当达到该时间时将调用该方法。在这里,我已经注销了,在你的情况下,你可以调用刷新令牌的应用编程接口

在Interceptor/Guard中,令牌过期时没有额外的事情要做

要控制报头中的注销按钮,只需这样做:

 userIsAuthenticated = false;
  private authListenerSubs: Subscription;
  constructor(private authService: AuthService) { }

  onLogout() {
    this.authService.logout();
  }

  ngOnInit() {
    this.authListenerSubs = this.authService.getAuthStatusListener().subscribe(isAuthenticated => {
      this.userIsAuthenticated = isAuthenticated;
    });
  }

  ngOnDestroy() {
    this.authListenerSubs.unsubscribe();
  }

在超文本标记语言的ngIF中使用userIsAuthentiated

对于一个真实的场景,你可以帮助这个github回购。

 类似资料:
  • 我需要(在拦截器类中)对403禁止的HTTP状态(获取/刷新)JWT令牌作出反应,并使用新令牌重试请求。 在下面的代码中,当服务器返回错误响应时,它将转到成功回调(而不是像我预期的那样进入错误回调),事件是typeof object(这在错误响应的反应中是无用的)。事件对象如下所示:{type:0}。 问题: -当我需要刷新accessToken并重试http请求时,如何在HttpIntercep

  • 我已经实现了 JWT 和刷新令牌流。当我过去实现这一点时,我的做法略有不同,主要是刷新令牌是在正文中发送的。 但是现在我做了不同的事情,我必须通过授权标头发送访问令牌,但是我的拦截器代码不想切换不记名令牌。如何修复,如果我想刷新,我实际上使用刷新令牌作为不记名令牌,而不是过期的访问令牌? 我还尝试在post请求中将HTTP标头设置为授权承载令牌

  • 我有一个使用express api的react应用程序。我正在尝试在访问令牌过期时刷新令牌。我正在使用axios拦截器来实现这一成就。 它卡在某个地方了。我使用console.log来调试它。从控制台; 发布http://localhost:5000/api/auth/token?null 401(未经授权) 之后什么都没发生。我该怎么办?谢谢你的帮助

  • 我有 401 拦截器,当access_token过期时,有一个请求的成功案例。拦截器重新加载令牌并返回 next.handle(customReq)。但是当同时发出 2 个或更多请求并且两个请求的令牌都已过期时,我遇到了问题,因为第二个请求尝试再次刷新,但现在刷新令牌无效。所以。。。。我尝试设置一个标志只执行一次,并使用自定义可观察量返回。问题是组件现在永远不会成功,我无法删除加载器。 HTTP

  • 问题内容: 我有一个有角度的应用程序,有时每个状态会执行多个$ http.get请求。该应用将JWT用于带有刷新令牌的用户身份验证。API服务器会发送由于身份验证错误而失败的每个请求。我做了一个请求,该请求在401错误时请求带有刷新令牌的新令牌,然后重新发送原始请求。 问题是,如果一个状态发出例如2个$ http.get请求,并且都获得401响应,那么我将访问令牌更新两次。显然,我只想刷新一次令牌