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的主要内容,如果未能解决你的问题,请参考以下文章