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

JWT 不记名令牌授权应用于 .NET Core 中的现有 MVC Web 应用程序

ASP.NET Core API 使用 JWT 不记名令牌进行身份验证

使用不记名令牌授权 ASP.net mvc 操作方法

JWT 不记名令牌不适用于 ASP.Net Core 3.1 + Identity Server 4

在 ASP.NET Core 2.1 Web 客户端中存储不记名令牌的位置

Angular 4 对 asp.net 核心 API 的请求(在标头中使用授权)不起作用