ASP.Net Core API always returns 401 unauthorized whenever I send a request with Bearer token included

为君一笑 提交于 2020-02-24 03:07:37

问题


I have an ASP .NET Core web api and I generate a JWT token for authorization purposes but whenever I make a request with Postman with Bearer token header I get 401 Unauthorized. Same when I try from my front-end that's consuming the API. When I remove Authorize everything works fine

Tried changing Authorize in my header to //[Authorize(AuthenticationSchemes = "Bearer")] also went to jwt.io to ensure the JWT Token is valid which it is.

//function where I generate JWT
  public   User AuthenticateAdmin(string username, string password)
        {
            var user =  _context.User.FirstOrDefault(x => x.UserName == username && x.Password == password);

            //return null if user is not found 
            if  (user == null) return null;

            //authentication successful so generate jwt token
            var tokenHandler = new JwtSecurityTokenHandler();
            var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
            var tokenDescriptor = new SecurityTokenDescriptor
            {
                Subject= new ClaimsIdentity(new Claim[]
                {
                    new Claim(ClaimTypes.Name, user.Id.ToString()),
                    new Claim(ClaimTypes.Role,user.Role)
                }),
                Expires=DateTime.UtcNow.AddDays(7),
                SigningCredentials= new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
            };
            var token = tokenHandler.CreateToken(tokenDescriptor);
            user.Token = tokenHandler.WriteToken(token);

            user.Password = null;
            return user;
        }
//my startup.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.EntityFrameworkCore;
using TheBackend.Models;
using TheBackend.Helpers;
using Microsoft.IdentityModel.Tokens;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using System.Text;
using TheBackend.Services;
using Microsoft.AspNetCore.Identity.UI.Services;
using Newtonsoft.Json.Serialization;
using Microsoft.AspNetCore.Authorization;

namespace TheBackend
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.Configure<AuthMessengerOptions>(Configuration);
            var connection = @"Host=localhost;Database=PayArenaMock;Username=postgres;Password=tim";
            services.AddDbContext<PayArenaMockContext>(options => options.UseNpgsql(connection));
            services.AddTransient<IEmailSender, EmailSender>();

            //services.AddAuthorization(auth =>
            //{
            //    auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder()
            //        .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme‌​)
            //        .RequireAuthenticatedUser().Build());
            //});
            services.AddCors();
            //services.AddMvcCore()
            // .AddAuthorization() // Note - this is on the IMvcBuilder, not the service collection
            // .AddJsonFormatters(options => options.ContractResolver = new CamelCasePropertyNamesContractResolver());
            //services.AddMvcCore().AddJsonFormatters(options => options.ContractResolver = new CamelCasePropertyNamesContractResolver());
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
            //configure strongly typed settings objects
            var appSettingsSection = Configuration.GetSection("AppSettings");
            services.Configure<AppSettings>(appSettingsSection);
            //configure JWT authentication
            var appSettings = appSettingsSection.Get<AppSettings>();
            var key = Encoding.ASCII.GetBytes(appSettings.Secret);
            services.AddAuthentication(x =>
            {
                x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(x=>
            {
                x.RequireHttpsMetadata = false;
                x.SaveToken = true;
                x.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey=true,
                    IssuerSigningKey= new  SymmetricSecurityKey(key),
                    ValidateIssuer=false,
                    ValidateAudience=false
                };
            });

            services.AddScoped<IUserService, UserService>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }
            app.UseCors(x => x
             .AllowAnyOrigin()
             .AllowAnyMethod()
             .AllowAnyHeader());
            app.UseAuthentication();
            app.UseHttpsRedirection();
            app.UseMvc();
        }
    }
}

//controller
//[Authorize(AuthenticationSchemes = "Bearer")]
    [Authorize]
    [Route("api/[controller]")]
    [ApiController]
    public class BusinessListingsController : ControllerBase
    {
        private readonly PayArenaMockContext _context;

        public BusinessListingsController(PayArenaMockContext context)
        {
            _context = context;
        }

        // GET: api/BusinessListings
        [HttpGet]
        //[AllowAnonymous]
        //[Authorize(Roles = Role.Admin)]
        public async Task<ActionResult<IEnumerable<BusinessListing>>> GetBusinessListing()
        {

            //var businesslisting = _context.BusinessListing.Include(b => b.CategoryNameNav);
            var businesslisting = await _context.BusinessListing.ToListAsync()
           ;
            return Ok( businesslisting);
        }

回答1:


Once I had the same problem after I switched from HTTP to HTTPS. It was working fine in POSTMAN with HTTP but when I switched to HTTPS it began to return 401 unauthorized

The problem was solved as soon as I added

        app.UseAuthentication();

inside

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
    }

within the

public class Startup
{
}



回答2:


I had this issue with dotnet core 3.1, and I was flipping every switch trying to get this to work. Eventually what ended up getting this to run was tletle's answer. Execute app.UseAuthentication() before app.UseAuthorization(). To elaborate on tletle's answer, below is the relevant code.

In Startup.cs:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        // ...
        app.UseHsts();
    }
    app.UseHttpsRedirection();
    app.UseRouting();
    app.UseAuthentication(); // this one first
    app.UseAuthorization(); 
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });

My authentication middleware is in an extension method I wrote that is called from the ConfigureServices() method in Startup.cs:

        public static void ConfigureAuthentication(this IServiceCollection services, IConfiguration configuration)
        {
            string issuer = configuration.GetValue<string>("Jwt:Issuer");
            string signingKey = configuration.GetValue<string>("Jwt:Key");
            byte[] signingKeyBytes = System.Text.Encoding.UTF8.GetBytes(signingKey);

            services.AddAuthentication(opt=>
            {
                opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                opt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(options=>
            {
                options.RequireHttpsMetadata = false;
                options.SaveToken = true;
                options.TokenValidationParameters = new TokenValidationParameters()
                {
                    ValidateIssuer = true,
                    ValidIssuer = issuer,
                    ValidateAudience = true,
                    ValidAudience = issuer,
                    ValidateLifetime = true,
                    ValidateIssuerSigningKey = true,
                    ClockSkew = System.TimeSpan.Zero,
                    IssuerSigningKey = new SymmetricSecurityKey(signingKeyBytes)
                };
            });
        }

and the token was generated using this extension method:

public static string GenerateApiUserToken(this ApiUser user, IConfiguration configuration)
{
    string signingKey = configuration.GetValue<string>("Jwt:Key");
    string issuer = configuration.GetValue<string>("Jwt:Issuer");
    int hours = configuration.GetValue<int>("Jwt:HoursValid");
    System.DateTime expireDateTime = System.DateTime.UtcNow.AddHours(hours);

    byte[] signingKeyBytes = System.Text.Encoding.UTF8.GetBytes(signingKey);
    SymmetricSecurityKey secKey = new SymmetricSecurityKey(signingKeyBytes);
    SigningCredentials creds = new SigningCredentials(secKey, SecurityAlgorithms.HmacSha256);

    var authClaims = new List<Claim>
    {
        new Claim(ClaimTypes.Name, user.UserName),
        new Claim(ClaimTypes.Role, user.RoleName)
    };

    JwtSecurityToken token = new JwtSecurityToken(
        issuer:issuer,
        audience: issuer,
        claims: authClaims,
        expires: System.DateTime.UtcNow.AddHours(hours),
        signingCredentials:creds
    );
    JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
    string writtenToken = handler.WriteToken(token);

    return writtenToken;
}

My Controller class:

[Authorize]
[ApiController]
[Microsoft.AspNetCore.Mvc.Produces("application/json")]
[Microsoft.AspNetCore.Mvc.Route("/[controller]/values", Name="MyController")]
public class MyController : Microsoft.AspNetCore.Mvc.Controller

If the [Authorize] tag is on the controller, you should remove any that are on member methods; I left one on the method I was testing and the fix wouldn't work until I removed it.




回答3:


It works finally, not a 100% sure why but I did headers: { Authorization: "Bearer" + " " + Bearer } in my react app. I do the equivalent in Postman and it doesn't work.




回答4:


I had this same issue and this was what i did on postman, in the header section using bulk edit.

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6ImRhbmllbEBtZ2ljLmNvbToxMTExMTE6IiwibmJmIjoxNTcwNjIxMDE5LCJleHAiOjE1NzMyOTk0MTksImlhdCI6MTU3MDYyMTAxOX0.cTjhhHqf75VN0RwQvly6nppeNUkKbzQ5_ZVPFyruuKPBQ

Also remember to include the authentication filter at the top of your endpoints, which is [Authorize] in your case.




回答5:


I had same issue, but after moveup

app.UseAuthentication();

to before line of

app.UseAuthorization();

in

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    ..
    app.UseAuthentication();
    ..
    app.UseAuthorization();
    ...
}

it worked.



来源:https://stackoverflow.com/questions/56185834/asp-net-core-api-always-returns-401-unauthorized-whenever-i-send-a-request-with

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!