JWT 身份验证始终返回 401 HTTP 错误 asp.net core

Posted

技术标签:

【中文标题】JWT 身份验证始终返回 401 HTTP 错误 asp.net core【英文标题】:JWT authentication always return 401 HTTP Error asp.net core 【发布时间】:2021-10-14 15:16:01 【问题描述】:

我每次尝试登录时都会收到相同的错误,即 401 Http 错误。我正在使用 JWT 进行身份验证。

下面是我的启动课:

public class Startup

    private readonly IConfiguration _configuration;
    public Startup(IConfiguration configuration)
    
        _configuration = configuration;
    

    public void ConfigureServices(IServiceCollection services)
    
        services.AddControllersWithViews().AddRazorRuntimeCompilation();
        services.AddDbContext<DatabaseContext>(options => options.UseSqlServer(_configuration.GetConnectionString("DBCS")));
        services.AddIdentity<ApplicationUser, IdentityRole>().AddEntityFrameworkStores<DatabaseContext>().AddDefaultTokenProviders();

        services.AddSingleton<EmailHelper>();
        services.AddScoped<IUnitOfWork, UnitOfWork>();
        services.AddTransient<IProductService, ProductService>();
        services.AddTransient<ICustomerService, CustomerService>();
        services.AddTransient<ISelectListService, SelectListService>();
        services.AddTransient<ITokenService, TokenService>();
        services.AddTransient<UserResolverService>();
        services.AddAutoMapper(typeof(Startup));
        services.AddSession(options =>
        
            options.IdleTimeout = TimeSpan.FromMinutes(180);
        );

        var secretKey = Encoding.ASCII.GetBytes(_configuration["JWT:key"]);
        services.AddAuthentication(options =>
        
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        )
        .AddJwtBearer(jwt =>
        
            var key = Encoding.ASCII.GetBytes(_configuration["JWT:key"]);
            jwt.SaveToken = true;
            jwt.TokenValidationParameters = new TokenValidationParameters
            
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(key),
                ValidateIssuer = false,
                ValidateAudience = false,
                RequireExpirationTime = false,
                ValidateLifetime = true
            ;
        );

        //Localization
        services.AddLocalization(options => options.ResourcesPath = "Resources");

        services.AddMvc()
             .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
             .AddDataAnnotationsLocalization(options =>
             
                 options.DataAnnotationLocalizerProvider = (type, factory) =>
                     factory.Create(typeof(SharedResource));
             );

        services.AddMvc()
           .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
           .AddDataAnnotationsLocalization(options =>
           
               options.DataAnnotationLocalizerProvider = (type, factory) =>
                   factory.Create(typeof(SharedResource));
           );

    services.Configure<RequestLocalizationOptions>(options =>
    
        var culture_en = CultureInfo.CreateSpecificCulture("en-US");
        //var culture_ar = CultureInfo.CreateSpecificCulture("ar-JO");
        var supportedCultures = new[]
        
            culture_en,
            //culture_ar,
        ;

        options.DefaultRequestCulture = new RequestCulture(culture: "en-US", uiCulture: "en-US");
        options.SupportedCultures = supportedCultures;
        options.SupportedUICultures = supportedCultures;
        options.RequestCultureProviders = new[] new RouteDataRequestCultureProvider
                IndexOfCulture=1,
                IndexofUICulture=1
            ;
        );
        services.Configure<RouteOptions>(options =>
        
            options.ConstraintMap.Add("culture", typeof(LanguageRouteConstraint));
        );
    

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    
        if (env.IsDevelopment())
        
            app.UseDeveloperExceptionPage();
            //app.UseExceptionHandler("/Error");
        
        else
        
            app.UseExceptionHandler("/Error");
            app.UseHsts();
        

        app.UseStaticFiles();
        //app.UseStatusCodePagesWithRedirects("/Error");
        app.UseHttpsRedirection();
        app.UseSession();
        app.UseRouting();
        app.UseAuthentication();
        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        
            endpoints.MapControllerRoute(
                name: "LocalizedAreas",
                pattern: "culture=en/area:exists/controller=Home/action=Index/id?"
            );

            endpoints.MapControllerRoute(
                name: "Areas",
                pattern: "area:exists/controller=Home/action=Index/id?"
            );

            endpoints.MapControllerRoute(
                name: "LocalizedDefault",
                pattern: "culture=en/controller=Home/action=Index/id?"
            );

            endpoints.MapControllerRoute(
                name: "default",
                pattern: "*catchall",
                defaults: new  controller = "Home", action = "RedirectToDefaultLanguage", culture = "en" );
        );
    

    public class LanguageRouteConstraint : IRouteConstraint
    
        public bool Match(HttpContext httpContext, IRouter route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection)
        
            if (!values.ContainsKey("culture"))
                return false;

            var culture = values["culture"].ToString();
            return culture == "en" || culture == "ar";
        
    

下面是我的名为 Account 的控制器,它确保用户存在并相应地生成一个令牌:

[HttpPost, ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginVM model)

    var user = await _userManager.FindByNameAsync(model.Email);
    if (user != null && await _userManager.CheckPasswordAsync(user, model.Password))
    
        var authClaim = new List<Claim> 
            new Claim("Id", user.CustomerId.ToString()),
            new Claim(JwtRegisteredClaimNames.Email, user.Email),
            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
        ;
        var JWTtoken = new JwtSecurityToken(
            issuer: _configuration["JWT:issuer"],
            audience: _configuration["JWT:audience"],
            claims: authClaim.AsEnumerable<Claim>(),
            expires: new DateTimeOffset(DateTime.Now.AddMinutes(90)).DateTime,
                    signingCredentials: new SigningCredentials(new SymmetricSecurityKey(Encoding.ASCII.GetBytes(_configuration["JWT:key"])), SecurityAlgorithms.HmacSha256)
        );
        var token = new JwtSecurityTokenHandler().WriteToken(JWTtoken);
        if (token != null)
        
            return RedirectToAction("Index", "Profile", new  area = "Store" );
        
        else
        
            ModelState.AddModelError("", (string)_localizer["InvalidLogin"]);
            return View(model);
        
    
    ModelState.AddModelError("", (string)_localizer["InvalidLogin"]);
    return View(model);

生成令牌后,我将用户重定向到他/她的个人资料,下面是个人资料控制器:

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[Area("Store")]
public class ProfileController : Controller

    [HttpGet]
    public IActionResult Index()
    
        return View();
    

提前感谢您,非常感谢您的帮助。

【问题讨论】:

您的代码没有显示客户端如何检索令牌或服务器如何验证令牌。 Login 不会将令牌返回给调用者。您是否正在调试代码,手动复制token,并将其分配到某个地方进行测试? 正如 Tom W 所说,当您调用另一个授权请求时,您需要将啤酒令牌复制到标头(或任何其他方式)。否则总是返回 401。另外,ValidateIssuerValidateAudience 设置为 false,所以JWTtoken 不需要添加受众和发布者。 另外,如果你使用mvc,我认为cookie认证是一种更好的方式。 Jwt 令牌认证更适合 web api 项目。 【参考方案1】:

谢谢大家的cmets和帮助,我上网找到了解决问题的办法。我通过在 Startup 类 Configure 方法中添加以下内容来解决它:

app.UseSession();
            app.Use(async (context, next) =>
            
                var token = context.Session.GetString("token");
                if (!string.IsNullOrEmpty(token))
                
                    context.Request.Headers.Add("Authorization", "Bearer " + token);
                
                await next();
            );
            app.UseStaticFiles();
            app.UseRouting();
            app.UseAuthentication();
            app.UseAuthorization();
            app.UseEndpoints()

【讨论】:

以上是关于JWT 身份验证始终返回 401 HTTP 错误 asp.net core的主要内容,如果未能解决你的问题,请参考以下文章

NestJs JWT 身份验证返回 401

护照-jwt 总是返回 401 未授权

为啥 OAuth2 身份验证服务器使用 jdbc 数据源返回 401 错误?

Nest.js Auth Guard JWT 身份验证不断返回 401 未授权

Android HttpClient 身份验证总是返回 401 代码

ASP.NET Core 5.0 JWT 身份验证总是抛出 HTTP 401 代码