验证 JWT 令牌 IDX12741 时出错。 JWT:必须有三个段(JWS)或五个段(JWE)

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

我正在尝试在 Angular 12 和 .net core 5 中实现令牌刷新功能。

这是我的 JWT 服务注册:

启动.cs:

services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;

        }).AddJwtBearer(options =>
        {
            options.SaveToken = true;
            options.RequireHttpsMetadata = false;
            options.TokenValidationParameters = new TokenValidationParameters()
            {
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidateLifetime = true,
                ValidateIssuerSigningKey = true,
                ClockSkew = TimeSpan.Zero,

                ValidAudience = _conf["JWT:ValidAudience"],
                ValidIssuer = _conf["JWT:ValidIssuer"],
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_conf["JWT:Secret"]))
            };
        });

这是我的appsettings.json:

"JWT": {
"ValidAudience": "http://localhost:4200",
"ValidIssuer": "http://localhost:4200",
"Secret": "JWTRefreshTokenHIGHsecuredPasswordVVVp1OH7Xzyr",
"TokenValidityInMinutes": 1,
"RefreshTokenValidityInDays": 7
}

调用登录控制器后,一个简单的访问令牌和刷新令牌将发送回客户端:

登录控制器:

            //login codes omitted for simplicity
            foreach (var role in userRoles)
            {
                authClaims.Add(new Claim(ClaimTypes.Role, role));
            }

            var accessToken = CreateToken(authClaims);
            var refreshToken = GenerateRefreshToken();

            _ = int.TryParse(_configuration["JWT:RefreshTokenValidityInDays"], out int refreshTokenValidityInDays);

            user.RefreshToken = refreshToken;
            user.RefreshTokenExpiryTime = DateTime.Now.AddDays(refreshTokenValidityInDays);

创建代币方法:

    private JwtSecurityToken CreateToken(List<Claim> authClaims)
    {
        var authSecurityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JWT:Secret"]));
        _ = int.TryParse(_configuration["JWT:TokenValidityInMinutes"], out int TokenValidityInMinutes);

        var expires = DateTime.Now.ToLocalTime().AddMinutes(TokenValidityInMinutes);
        var token = new JwtSecurityToken(
            issuer: _configuration["JWT:ValidIssuer"],
            audience: _configuration["JWT:ValidAudience"],
            claims: authClaims,
            expires: expires,
            signingCredentials: new SigningCredentials(authSecurityKey, SecurityAlgorithms.HmacSha256)
            ) ;
        return token;
    }

现在登录后,我拥有保存在浏览器SessionStorage中的访问令牌和刷新令牌。

在客户端,我实现了一个 HTTP 拦截器来处理 401 未经授权的错误。当我的后端说令牌已过期时,我想刷新我的 JWT 令牌。

auth.interceptor.ts:

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

  private isRefreshing = false;

  private refreshTokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(null);

  constructor(private tokenService: tokenStorageService, private authService: AuthenticationService) { }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<Object>> {
    debugger;
    let authReq = req;
    const token = this.tokenService.getToken();

    if (token != null) {
      authReq = this.addTokenHeader(req, token);
    }
    return next.handle(authReq).pipe(catchError(error => {
      if (error instanceof HttpErrorResponse && !authReq.url.includes('auth/login') && error.status === 401) {
        debugger;
        return this.handle401Error(authReq, next);
      }
      return throwError(error);
    }));
  }
  private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
    if (!this.isRefreshing) {
      this.isRefreshing = true;
      this.refreshTokenSubject.next(null);
      const token = this.tokenService.getRefreshToken();
      const refreshToken=this.tokenService.getRefreshToken();
      if (token)
        return this.authService.refreshToken(token,refreshToken).pipe(
          switchMap((token: any) => {
            this.isRefreshing = false;
            this.tokenService.saveToken(token.accessToken);
            this.refreshTokenSubject.next(token.accessToken);

            return next.handle(this.addTokenHeader(request, token.accessToken));
          }),
          catchError((err) => {
            debugger;
            this.isRefreshing = false;

            //this.tokenService.signOut();
            return throwError(err);
          })
        );
    }
    return this.refreshTokenSubject.pipe(
      filter(token => token !== null),
      take(1)        switchMap((token) => next.handle(this.addTokenHeader(request, token)))
    );
  }
  private addTokenHeader(request: HttpRequest<any>, token: string) {

    return  request.clone({
      setHeaders: {
        Authorization: `Bearer ${token}`
      }
    });

  }
}

登录后一分钟,我的令牌过期,拦截器调用我的刷新令牌 api。

刷新令牌控制器:

    public async Task<object> RefreshToken(tokenModel tokenModel)
    {
        if (tokenModel == null)
            throw new ServiceException("Invalid Token Model");

        string accessToken = tokenModel.accessToken;
        string refreshToken = tokenModel.refreshToken;

        var principal = GetPrincipalsFromExpiredToken(accessToken);
        if(principal==null)
        {
            throw new  ServiceException("Invalid access token or refresh token");
        }

        string username = principal.Identity.Name;

        var user = await _userManager.FindByNameAsync(username);
        if(user==null || user.RefreshToken!=refreshToken || user.RefreshTokenExpiryTime<=DateTime.Now)
        {
            throw new ServiceException("Invalid access token or refresh token");
        }

        var newAccessToken = CreateToken(principal.Claims.ToList());
        var newRefreshToken = GenerateRefreshToken();
        user.RefreshToken = newRefreshToken;

        await _userManager.UpdateAsync(user);

        return new
        {
            accessToken=new JwtSecurityTokenHandler().WriteToken(newAccessToken),
            refreshToken=newRefreshToken
        };
    }

这是 GetPrincipalsFromExpiredToken 方法:

private ClaimsPrincipal GetPrincipalsFromExpiredToken(string token)
    {
        var tokenValidationParameters = new TokenValidationParameters
        {
            ValidateAudience = false,
            ValidateIssuer = false,
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JWT:Secret"])),
            ValidateLifetime = false
        };

        var tokenHandler = new JwtSecurityTokenHandler();
        SecurityToken securityToken = null;

        var principal = tokenHandler.ValidateToken(token, tokenValidationParameters, out securityToken);

        if (securityToken is not JwtSecurityToken jwtSecurityToken ||
        !jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256,
        StringComparison.InvariantCultureIgnoreCase))
            throw new SecurityTokenException("Invalid token");

        return principal;

    }

在 tokenHandler.ValidateToken 我收到此错误消息:

IDX12741:JWT:“[PII 已隐藏。有关更多详细信息,请参阅 https://aka.ms/IdentityModel/PII。]' 必须具有三个段 (JWS) 或五个段 (JWE)。

我做错了什么? 有没有更简单的方法来实现 jwt 刷新令牌?

这是生成的令牌:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoidGVzdEBrYXNiaW0uaX IiLCJqdGkiOiJjM2I2ZTQyZi00ZGI1LTQzMDMtYjY4Mi02YWU5Yzg3ZjI1ZTUiLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUI OiJVc2VyIiwiZXhwIjoxNjUyMzc0ODI4LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjQyMDAiLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjQyMDAifQ.8rzkMkVENPAyWV2DpPhUUAzza0cXY_HiUp Wq2u_Sqqs

angular jwt asp.net-core-webapi refresh-token angular12
2个回答
2
投票

你的 jwt 是什么样的? JWT由三部分组成:header、playload和signature,它们之间用“.”分隔。在你的 jwt 字符串中。我想你的错误是由 jwt 的错误结构引起的。 你可以看到测试结果如下:

已更新

第二个请求的错误信息(我删除了jwt中的一个“.”):


0
投票

您的 JWT 令牌应由 3 个部分组成,以点 (.) 分隔

要验证您的 JWT 令牌是否有效,请使用 https://jwt.io/

如果您的令牌有效,则应显示为

 Signature Verified
,如图所示。如果这是正确的,那么您的令牌没有问题。否则你一定发送了一些错误的值。

© www.soinside.com 2019 - 2024. All rights reserved.