ASP.NET Core Web API中JWT承载令牌授权不起作用

15

我创建了一个使用JWT令牌进行授权的Web API,该令牌基于基于角色的策略(根据文章)。用户登录后会生成一个用于授权的令牌。我成功生成了令牌,但当我开始使用它来访问受限API操作时,它无法正常工作,并不断给出401 HTTP错误(考虑到动作调用不触发,我甚至无法进行调试)。我做错了什么?。

类:

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.AddControllers();
        services.AddScoped<ICountriesService, CountriesService>();
        services.AddScoped<ICompanyService, CompanyService>();
        services.AddScoped<IPlaneServices, PlaneService>();
        services.AddScoped<IPlaneTypeService, PlaneTypeService>();
        services.AddScoped<ICitiesService, CitiesService>();
        services.AddScoped<IAirfieldService, AirfieldService>();
        services.AddScoped<ITicketTypeService, TicketTypeService>();
        services.AddScoped<IFlightService, FlightService>();
        services.AddScoped<ILuxuryService, LuxuryService>();
        services.AddScoped<IUserService, UserService>();

        // Register the Swagger generator, defining 1 or more Swagger documents
        services.AddSwaggerGen(c =>
        {
            c.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo { Title = "My API", Version = "v1" });


            c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
            {
                Description = @"JWT Authorization header using the Bearer scheme. \r\n\r\n 
                  Enter 'Bearer' [space] and then your token in the text input below.
                  \r\n\r\nExample: 'Bearer 12345abcdef'",
                Name = "Authorization",
                In = ParameterLocation.Header,
                Type = SecuritySchemeType.ApiKey,
                Scheme = "Bearer"
            });

            c.AddSecurityRequirement(new OpenApiSecurityRequirement()
          {
            {
              new OpenApiSecurityScheme
              {
                    Reference = new OpenApiReference
                      {
                        Type = ReferenceType.SecurityScheme,
                        Id = "Bearer"
                      },
                      Scheme = "oauth2",
                      Name = "Bearer",
                      In = ParameterLocation.Header,

                    },
                    new List<string>()
                  }
            });
            //        var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
            //var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
            //c.IncludeXmlComments(xmlPath);
        });

        services.AddAutoMapper(cfg => cfg.AddProfile<Mapper.Mapper>(),
                          AppDomain.CurrentDomain.GetAssemblies());

        services.AddDbContext<FlightMasterContext>();


        services.AddCors();

        var secret = Configuration.GetValue<string>(
            "AppSettings:Secret");

        var key = Encoding.ASCII.GetBytes(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
            };
        });



    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        // Enable middleware to serve generated Swagger as a JSON endpoint.
        app.UseSwagger();

        // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
        // specifying the Swagger JSON endpoint.
        app.UseSwaggerUI(c =>
        {
            c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
            c.RoutePrefix = string.Empty;
        });



        app.UseHttpsRedirection();

        app.UseRouting();

        app.UseAuthorization();
        app.UseAuthentication();

        // global cors policy
        app.UseCors(x => x
            .AllowAnyOrigin()
            .AllowAnyMethod()
            .AllowAnyHeader());

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();

        });
    }
}
控制器:
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class AaTestController : ControllerBase
{

    private FlightMasterContext db { get; set; }

    private IUserService _userService;

    public AaTestController(FlightMasterContext db, IUserService userService)
    {
        this.db = db;
        _userService = userService;
    }

    [AllowAnonymous]
    [HttpPost("authenticate")]
    public IActionResult Authenticate([FromBody]AuthenticateModel model)
    {
        var user = _userService.Authenticate(model.Username, model.Password);

        if (user == null)
            return BadRequest(new { message = "Username or password is incorrect" });

        return Ok(user);
    }
    //DOESNT TRIGGER
    [Authorize(Roles = Role.Admin)]
    [HttpGet]
    public IActionResult GetAll()
    {
        var users = _userService.GetAll();
        return Ok(users);
    }

    [HttpGet("{id}")]
    public IActionResult GetById(int id)
    {
        // only allow admins to access other user records
        var currentUserId = int.Parse(User.Identity.Name);
        if (id != currentUserId && !User.IsInRole(Role.Admin))
            return Forbid();

        var user = _userService.GetById(id);

        if (user == null)
            return NotFound();

        return Ok(user);
    }
}

用于认证和授权的服务:

public interface IUserService
{
    User Authenticate(string username, string password);
    IEnumerable<User> GetAll();
    User GetById(int id);
}

public class UserService : IUserService
{
    // users hardcoded for simplicity, store in a db with hashed passwords in production applications
    private List<User> _users = new List<User>
    {
        new User { Id = 1, FirstName = "Admin", LastName = "User", Username = "admin", Password = "admin", Role = Role.Admin },
        new User { Id = 2, FirstName = "Normal", LastName = "User", Username = "user", Password = "user", Role = Role.User }
    };






    public User Authenticate(string username, string password)
    {
        var user = _users.SingleOrDefault(x => x.Username == username && x.Password == password);



        var secret = "THIS IS Ughjgjhgjhghgighiizgzigiz";



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

        // authentication successful so generate jwt token
        var tokenHandler = new JwtSecurityTokenHandler();
        var key = Encoding.ASCII.GetBytes(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);

        return user.WithoutPassword();
    }

    public IEnumerable<User> GetAll()
    {
        return _users.WithoutPasswords();
    }

    public User GetById(int id)
    {
        var user = _users.FirstOrDefault(x => x.Id == id);
        return user.WithoutPassword();
    }
}

enter image description here

2个回答

59

这些方法应该按相反的顺序调用:

app.UseAuthentication();
app.UseAuthorization();

第一个中间件应该对用户进行身份验证,然后才能进行下一个中间件——授权。不幸的是,并非所有的 MS 文档都注意到了这个细节。


-2

首先中间件应该验证用户,然后才是下一个中间件 - 授权。不幸的是,并非所有的 MS 文档都注意到了这个细节。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接