【问题标题】:how to get automatically new JWT Token and Refresh Token in angular如何以角度自动获取新的 JWT 令牌和刷新令牌
【发布时间】:2021-10-31 04:14:03
【问题描述】:

我正在使用 angular 和 asp.net 核心我已经在
this artical 的帮助下实现了 JWT 令牌和刷新令牌我已经编写了代码来检查 Jwt 在 Authguard 中是否有效,如果 jwt 无效,那么使用刷新令牌将调用 api 并获取新的 Jwt 和刷新令牌。仅当我执行刷新页面和导航到另一个页面之类的任何事件时,才会发生这种情况。 我想知道如果我们的 jwt 令牌过期而没有执行任何事件角度检测它并使用referh 令牌自动调用 api 并获取新的 jwt 令牌

这里是代码

authguard

在 authguard 中 isUserAuthenticated() 将检查 jwt 是否已过期,如果已过期,它将使用刷新令牌进行 api 调用并获取新的 jwt 和刷新令牌

async canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Promise<boolean> {
    if((await this._authService.isUserAuthenticated()) === true){
      return true;
    }
    else{
      this._router.navigate([RouteConstant.SIGN_IN]);
      return false;
    }
  }

public async isUserAuthenticated(){
    if(this._storageService.getToken() !== null){
      if(!this._jwtHelperService.isTokenExpired(this._storageService.getToken()?.toString())){
        return true;
      }
      else{
        if(!this._storageService.refreshTokenExists()){
          return false;
        }
        else{
          let authTokenClient: AuthTokenClient = {
            token: this._storageService.getToken() as string,
            refreshToken: this._storageService.getRefreshToken() as string
          };
          let response: AuthToken = await this._serverService.refreshAuthToken(authTokenClient).toPromise<AuthToken>();
          console.log(response);
          this.setAuth(response);
          return !this._jwtHelperService.isTokenExpired(this._storageService.getToken()?.toString());
        }
      }
    }
    else{
      return false;
    }
  }

  }

  public setAuth(authToken: AuthToken){
    this._storageService.saveToken(authToken.token?.toString());
    this._storageService.saveRefreshToken(authToken.refreshToken);
  }
  public async refreshAuthToken(authTokenClient: AuthTokenClient) {
    const response = await this._http.post<AuthToken>(environment.apiHost + UrlConstant.ACCOUNT_REFRESH, authTokenClient,{observe: 'response'}).toPromise();
    console.log(response);
    const newToken = (<any>response).body.token;
            console.log(newToken);
            const newRefreshToken = (<any>response).body.refreshToken;
            console.log(newRefreshToken);
            localStorage.setItem("token", newToken);
            localStorage.setItem("refreshToken",newRefreshToken);
            if (newToken && newRefreshToken == null){
              return false
            }
            else {
              return true;
            }
  }

Startup.cs

 services.AddAuthentication(opt =>
            {
                opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                opt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            }).AddJwtBearer(options =>
            {
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuer = true,
                    ValidateAudience = true,
                    ValidateLifetime = true,
                    ValidateIssuerSigningKey = true,

                    ValidIssuer = jwtSettings.ValidIssuer,
                    ValidAudience = jwtSettings.ValidAudience,
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration[ConfigurationKeyConstant.JWT_SECRET_KEY])),
                    ClockSkew = TimeSpan.Zero
                };
            });

Controller.cs

[Route("refresh")]
        public IActionResult Refresh(AuthTokenBase authTokenBase)
        {
            if (authTokenBase is null)
            {
                return BadRequest("Invalid client request");
            }
            string accessToken = authTokenBase.Token;
            string refreshToken = authTokenBase.RefreshToken;
            var principal = this._jwtHandler.GetPrincipalFromExpiredToken(accessToken);
            var username = principal.Identity.Name; 
            string newAccessToken = this._jwtHandler.GenerateToken(); 
            string newRefreshToken = this._jwtHandler.GenerateRefreshToken();
            return Ok(new AuthToken()
            {
                Token = newAccessToken,
                RefreshToken = newRefreshToken,
                IsAuthSuccessful = true
            });
        }

【问题讨论】:

  • 我们使用一个可观察的计时器在令牌过期之前进行刷新,但是在刷新期间有调用出去时会变得复杂。你为什么要这样做?为什么默认实现不好?
  • @MotKohn 正如你在网上提到的可观察计时器并发现有时间间隔,所以我在我的代码中使用它import { interval, Observable, Subscription } from "rxjs"; timer: any; constructor(private _authservice: AuthService, private _storageService: StorageService) { } ngOnInit() { this.timer = interval(60000).subscribe(x =&gt; { this._authservice.isUserAuthenticated(); }); 这是正确的方式吗??
  • 这就是我的意思,但再说一遍:当您使旧令牌无效且尚未取回新令牌时发生的调用,您将如何处理?这就是 authguard 以这种方式实现的原因。
  • @MotKohn 因为我没有在 Startup.cs 中设置 ClockSkew=TimeSpan.Zero => 在 asp.net-core 中配置,所以即使在我的令牌过期后,服务器也会允许直到 5 分钟,这是默认的 @987654327 @。因为我将 JWT 令牌的到期时间保持为 5 分钟,并保持检查有效性的时间间隔为 4 分钟。所以我认为这样我的通话将永远成功

标签: c# angular jwt refresh-token


【解决方案1】:

您可以使用角度拦截器。 拦截http请求中的每一个错误

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        var modifiedRequest = this.normalizeRequestHeaders(request);
        return next.handle(modifiedRequest)
            .pipe(
                catchError(error => {
                    if (error instanceof HttpErrorResponse && error.status === 401) {
                        return this.tryGetRefreshTokenService(request, next, error);
                    } else {
                        return this.handleErrorResponse(error);
                    }
                }),
                switchMap((event) => {
                    return this.handleSuccessResponse(event);
                })
            );
}

【讨论】:

  • 请注意,在 Stack Overflow 上强烈不鼓励仅链接的答案,最终会被删除。应编辑答案以总结答案本身中链接的关键点。
【解决方案2】:

service.AddAuthentication() 的 ConfigureServices 下,您可以为 OnAuthenticationFailed 添加 事件

并检查异常类型是否为SecurityTokenExpiredException,然后设置响应头tokenExpired=true

稍后在客户端检查响应头并根据 tokenExpired 值调用刷新令牌 API。

伪代码:

 services.AddAuthentication(opt =>
            {
                opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                opt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            }).AddJwtBearer(options =>
            {
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuer = true,
                    ValidateAudience = true,
                    ValidateLifetime = true,
                    ValidateIssuerSigningKey = true,

                    ValidIssuer = jwtSettings.ValidIssuer,
                    ValidAudience = jwtSettings.ValidAudience,
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration[ConfigurationKeyConstant.JWT_SECRET_KEY])),
                    ClockSkew = TimeSpan.Zero
                };
                options.Events = new JwtBearerEvents {
                    OnAuthenticationFailed = context => {
                     if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                     {
                        context.Response.Headers.Add("IS-TOKEN-EXPIRED", "true");
                     }
                  return Task.CompletedTask;
            }
        };
  });

如果令牌过期,则在响应标头中将 IS-TOKEN-EXPIRED 设置为 true。 更多详情可以查看我写的Blog article

【讨论】:

    猜你喜欢
    • 2018-09-29
    • 1970-01-01
    • 2016-03-05
    • 2016-06-25
    • 2021-08-23
    • 2021-12-31
    • 1970-01-01
    • 1970-01-01
    • 2020-02-17
    相关资源
    最近更新 更多