JWT不记名令牌授权不起作用asp net core web api

Posted

技术标签:

【中文标题】JWT不记名令牌授权不起作用asp net core web api【英文标题】:JWT bearer token Authorization not working asp net core web api 【发布时间】:2020-05-14 05:44:41 【问题描述】:

我创建了一个 Web api,它使用 JWT 令牌通过基于角色的策略进行授权(基于 this 文章)。 用户登录会生成一个用于授权的令牌。我成功生成了令牌,但是当我开始使用它来访问受限制的 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();
    

【问题讨论】:

【参考方案1】:

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

【讨论】:

您不应将另一个答案的副本作为答案本身发布。【参考方案2】:

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

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

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

【讨论】:

感谢十亿 谢谢googol 在这个序列上浪费了整整一个小时

以上是关于JWT不记名令牌授权不起作用asp net core web api的主要内容,如果未能解决你的问题,请参考以下文章