You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core 7 Web API JWT认证持续返回401未授权问题排查

ASP.NET Core 7 Web API JWT认证401未授权问题排查与解决

我在ASP.NET Core 7 Web API中实现JWT认证,调用Login接口能正常获取token,但用Postman把token加到授权头访问其他接口时,一直收到401未授权错误。以下是我的代码实现:

Models

public class ApplicationUser : IdentityUser
{
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public virtual ICollection<RefreshToken> RefreshTokens { get; set; }
    public ICollection<ApplicationUserRole> UserRoles { get; set; }
}


public class RefreshToken
{
    //[JsonIgnore]
    public Guid Id { get; set; }
    public string UserId { get; set; }
    public string Token { get; set; }
    public DateTime ExpiryDate { get; set; }
    public virtual ApplicationUser User { get; set; }
}

TokenGeneratorService

public class TokenGeneratorService : ITokenGeneratorService
{
    private readonly IConfiguration _configuration;
    private readonly UserManager<ApplicationUser> _userManager;
    public TokenGeneratorService(IConfiguration configuration, UserManager<ApplicationUser> userManager)
    {
        _configuration = configuration;
        _userManager = userManager;
    }

    public async Task<string> GenerateToken(ApplicationUser user)
    {
        var authClaims = new List<Claim>
        {
            new Claim(ClaimTypes.NameIdentifier, user.Id),
            new Claim(ClaimTypes.Email, user.Email),
            new Claim(ClaimTypes.Name, user.UserName)
            //new Claim(ClaimTypes.GivenName, user.FirstName),
            //new Claim(ClaimTypes.Surname, user.LastName)
            //new Claim(ClaimTypes.Uri, avatar)
        };

        //Gets the roles of the logged in user and adds it to Claims
        var roles = await _userManager.GetRolesAsync(user);

        foreach (var role in roles)
        {
            authClaims.Add(new Claim(ClaimTypes.Role, role));
        }

        var signingKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JwtSettings:SecretKey"]));

        // Specifying JWTSecurityToken Parameters
        var token = new JwtSecurityToken
        (audience: _configuration["JwtSettings:Audience"],
         issuer: _configuration["JwtSettings:Issuer"],
         claims: authClaims,
         expires: DateTime.Now.AddHours(2),
         signingCredentials: new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256));

        return new JwtSecurityTokenHandler().WriteToken(token);
    }

    public int? ValidateJwtToken(string token)
    {
        if (token == null)
            return null;

        var tokenHandler = new JwtSecurityTokenHandler();
        var key = Encoding.ASCII.GetBytes(_configuration["JwtSettings:SecretKey"]);

        try
        {
            tokenHandler.ValidateToken(token, new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(key),
                ValidateIssuer = false,
                ValidateAudience = false,
                ClockSkew = TimeSpan.Zero
            }, out SecurityToken validatedToken);

            var jwtToken = (JwtSecurityToken)validatedToken;
            var userId = int.Parse(jwtToken.Claims.First(x => x.Type == "id").Value);
            return userId;
        }
        catch
        {
            return null;
        }
    }

    public Guid GenerateRefreshToken()
    {
        return Guid.NewGuid();
    }
}

TokenRepository

public class TokenRepository : ITokenRepository
{
    private readonly ApplicationDbContext _dbContext;

    public TokenRepository(ApplicationDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public async Task<ApplicationUser> GetUserByRefreshToken(Guid token, string userId)
    {
        // Check for user Id
        var user = await _dbContext.ApplicationUsers.SingleOrDefaultAsync(u => u.RefreshToken == token && u.Id == userId);
        //var user = await _context.Users.SingleOrDefault(u => u.RefreshToken == token && u.Id == userId);
        //FirstOrDefaultAsync
        if (user == null)
        {
            throw new ArgumentException($"User with Id {userId} does not exist");
        }

        return user;
    }
}

Login方法

public async Task<Response<LoginResponseDto>> Login(LoginRequestDto model)
{
    var response = new Response<LoginResponseDto>();
    var getUser = await _userManager.FindByNameAsync(model.UserName);
    var loggedUser = _dbContext.ApplicationUsers.Where(u => u.UserName == model.UserName).Select(m => m.UserName).FirstOrDefault();

    var user1 = await _userManager.FindByNameAsync(model.UserName);

    var loggedUserRole = await _userManager.GetRolesAsync(getUser);
    var loggedUserRoleName = loggedUserRole[0].ToString();

    if (loggedUserRoleName == "Customer" || loggedUserRoleName == "CustomerUser")
    {
        var validityResult = await ValidateUser(model);

        if (!validityResult.Successful)
        {
            response.Successful = false;
            return response;
        }

        var user = await _userManager.FindByNameAsync(model.UserName);
        var roles = await _userManager.GetRolesAsync(user);
        var refreshToken = _tokenGenerator.GenerateRefreshToken();

        user.RefreshToken = refreshToken;
        user.RefreshTokenExpiryTime = DateTime.Now.AddDays(7); //sets refresh token for 7 days
        user.LastLogin = DateTime.Now;

        var result = new LoginResponseDto()
        {
            Id = user.Id,
            Token = await _tokenGenerator.GenerateToken(user),
            RefreshToken = refreshToken,
            User = _mapper.Map<UserDto>(user),
            Roles = roles,
            Expires = DateTime.Now.AddHours(2)
        };

        await _userManager.UpdateAsync(user);

        return response;
    }
    else
    {
        return response;
    }
}

扩展类

public static class AuthenticationServiceExtension
{
    public static void ConfigureAuthentication(this IServiceCollection services, IConfiguration configuration)
    {
        var tokenValidationParameters = new TokenValidationParameters
        {
            ValidateAudience = true,
            ValidateIssuer = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidAudience = configuration["JwtSettings:Audience"],
            ValidIssuer = configuration["JwtSettings:Issuer"],
            IssuerSigningKey = new SymmetricSecurityKey(Encoding
                .UTF8.GetBytes(configuration["JwtSettings:SecretKey"])),
            ClockSkew = TimeSpan.Zero
        };
     
        services.AddSingleton(tokenValidationParameters);

        services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
        })
       .AddJwtBearer(options =>
       {
           options.SaveToken = true;
           options.TokenValidationParameters = tokenValidationParameters;

       });
        
    }
}

public static class DIServiceExtension
{
    public static void AddDependencyInjection(this IServiceCollection services)
    {
        // Add Service Injections Here -- Auth
        services.AddScoped<IAuthService, AuthService>();
        services.AddScoped<ITokenGeneratorService, TokenGeneratorService>();
        services.AddScoped<ITokenRepository, TokenRepository>();
    }
}

Program.cs

builder.Services.ConfigureAuthentication(configuration);
builder.Services.AddDependencyInjection();

var app = builder.Build();

// Configure the HTTP request pipeline.

app.UseSwagger();
app.UseSwaggerUI();
  
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();

app.UseHsts();
app.UseCors(x => x
        .AllowAnyOrigin()
        .AllowAnyMethod()
        .AllowAnyHeader());

app.UseStaticFiles();

app.MapControllers();

string? port = Environment.GetEnvironmentVariable("PORT");

if (!string.IsNullOrWhiteSpace(port))
{
    app.Urls.Add("http://*:" + port);
}
app.Run();

问题排查与修复方案

1. Token验证编码不一致

TokenGeneratorService中生成token用Encoding.UTF8处理密钥,但ValidateJwtToken方法用了Encoding.ASCII,若密钥含非ASCII字符会导致验证失败。
修复:
将ValidateJwtToken中的编码改为UTF8:

var key = Encoding.UTF8.GetBytes(_configuration["JwtSettings:SecretKey"]);

2. Claim类型匹配错误

生成token时用ClaimTypes.NameIdentifier(对应JWT的nameid),但验证时查找"id"类型的Claim,会导致找不到值抛出异常。同时ApplicationUser.Id是string类型,用int.Parse会报错。
修复:
修改验证逻辑的Claim类型,并调整ID解析方式:

var userId = jwtToken.Claims.First(x => x.Type == ClaimTypes.NameIdentifier).Value;
// 若需要返回int类型,需确保User.Id是可转换的数字,否则直接返回string

3. Login方法未正确赋值响应结果

Login方法生成result后,未将其赋值给response.Data,也未设置response.Successful = true,可能导致响应数据异常。
修复:
在返回前补充响应赋值:

response.Successful = true;
response.Data = result;
return response;

4. 中间件顺序错误

Program.cs中UseCors位置在认证之后,不符合官方推荐顺序,可能影响请求处理。
修复:
调整中间件顺序为:

app.UseSwagger();
app.UseSwaggerUI();

app.UseHsts();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCors(x => x
        .AllowAnyOrigin()
        .AllowAnyMethod()
        .AllowAnyHeader());
app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

5. Postman授权头格式检查

确保Postman中授权头为Authorization: Bearer <token>,注意Bearer后有空格,token无多余空格或引号。

6. JWT配置项一致性检查

确认appsettings.json中JwtSettings:Issuer和JwtSettings:Audience的值,与生成、验证token时使用的完全一致,无拼写错误。

内容的提问来源于stack exchange,提问作者Ayobamilaye

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 17:55:01