Asp.Net Core WebApi:授权属性错误 403

Posted

技术标签:

【中文标题】Asp.Net Core WebApi:授权属性错误 403【英文标题】:Asp.Net Core WebApi: Authorize attribute Error 403 【发布时间】:2018-10-30 19:37:52 【问题描述】:

我在 Asp.Net Core WebApi 项目中工作并创建了一个角色 "admin" 并将其添加到我的用户。但是如果我以管理员身份登录,第一种方法会返回“这是管理员!”,但第二种方法会返回错误 403 Forbidden。

如果我从 Authorize 属性中删除 Roles 参数,一切都会好起来的。我不明白为什么我无法访问第二种方法,因为我的用户具有管理员角色。

// Host/api/roles/getroles
        [Authorize]
        [HttpGet]
        public async Task<IEnumerable<string>> GetRoles()
        
            var user = await _userManager.GetUserAsync(User);

            bool isAdmin = await _userManager.IsInRoleAsync(user, Roles.AdminRole);               
            if (isAdmin)
                return new[] "This is admin!";

            return await _userManager.GetRolesAsync(user);
        


        // ===== Admin Methods =====

        // Host/api/roles/createrole
        [Authorize(Roles = Roles.AdminRole)]
        [HttpPost]
        public async Task<IActionResult> CreateRole([FromBody] CreateRoleViewModel model)
        
            if (!ModelState.IsValid)
            
                return BadRequest();
            

            var result = await _roleManager.CreateAsync(new IdentityRole(model.RoleName));

            if (!result.Succeeded)
                return BadRequest(result);

            return Ok();
        

在我发送的第二种方法的请求中:

标题: 内容类型:应用程序/json 授权:承载eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJh...

正文: RoleName = "程序员"

也许我需要在标题中添加一些内容?

Startup.cs

public class Startup
    
        public IConfiguration Configuration  get; 

        public Startup(IConfiguration configuration)
        
            Configuration = configuration;
        


        public void ConfigureServices(IServiceCollection services)
        
            // ===== Add DbContext ========
            var connectionString = Configuration.GetConnectionString("DbConnection");
            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(connectionString));

            // ===== Add Identity ========
            services.AddIdentity<User, IdentityRole> (opts=> 
                opts.Password.RequiredLength = 5;
                opts.Password.RequireNonAlphanumeric = false;
                opts.Password.RequireLowercase = false;
                opts.Password.RequireUppercase = false;
                opts.Password.RequireDigit = false;
                ) 
                .AddEntityFrameworkStores<ApplicationDbContext>()
                .AddDefaultTokenProviders();

            // ===== Add Jwt Authentication ========
            JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); // => remove default claims
            services
                .AddAuthentication(options =>
                
                    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                )
                .AddJwtBearer(cfg =>
                
                    cfg.RequireHttpsMetadata = false;
                    cfg.SaveToken = true;
                    cfg.TokenValidationParameters = new TokenValidationParameters
                    
                        ValidIssuer   = Configuration["JwtIssuer"],
                        ValidAudience = Configuration["JwtIssuer"],
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JwtKey"])),

                        ClockSkew = TimeSpan.Zero // remove delay of token when expire
                    ;
                );

            // ===== Add MVC =====
            services.AddMvc();
        

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(
            IApplicationBuilder app,
            IHostingEnvironment env,
            ApplicationDbContext dbContext
        )
        
            if (env.IsDevelopment())
            
                app.UseDeveloperExceptionPage();
            

            // ===== Use Authentication ======
            app.UseAuthentication();

            // ===== Use MVC =====
            app.UseMvc();
        
    

创建 JWT 令牌方法

// ===== Token =====
        private async Task<object> GenerateJwtToken(IdentityUser user)
        
            var claims = new List<Claim>
            
                new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
                new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                new Claim(ClaimTypes.NameIdentifier, user.Id)
            ;

            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JwtKey"]));
            var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
            var expires = DateTime.Now.AddDays(Convert.ToDouble(_configuration["JwtExpireDays"]));

            var token = new JwtSecurityToken(
                _configuration["JwtIssuer"],
                _configuration["JwtIssuer"],
                claims,
                expires: expires,
                signingCredentials: creds
            );

            return new JwtSecurityTokenHandler().WriteToken(token);
        

【问题讨论】:

你在ConfigureServices()中配置了JWT Bearer Authentication吗? @DmitryEgorov 是的,我实现了 JWT @DmitryEgorov 我编辑了问题并添加了 Startup.cs JWT 传入的 header 中是否包含 roles 声明? 您需要多次添加相同的ClaimTypes.Role 声明,每个角色一次。 【参考方案1】:

我更改了我的 GenerateJwtToken() 方法以将角色添加为声明:

// Get User roles and add them to claims
                var roles = await _userManager.GetRolesAsync(user);
                AddRolesToClaims(claims, roles);

// ===== Token =====
        private async Task<object> GenerateJwtToken(User user)
        
            var claims = new List<Claim>
            
                new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
                new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                new Claim(ClaimTypes.NameIdentifier, user.Id),
            ;

            // Get User roles and add them to claims
            var roles = await _userManager.GetRolesAsync(user);
            AddRolesToClaims(claims, roles);

            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JwtKey"]));
            var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
            var expires = DateTime.Now.AddDays(Convert.ToDouble(_configuration["JwtExpireDays"]));

            var token = new JwtSecurityToken(
                _configuration["JwtIssuer"],
                _configuration["JwtIssuer"],
                claims,
                expires: expires,
                signingCredentials: creds
            );

            return new JwtSecurityTokenHandler().WriteToken(token);
        


        private void AddRolesToClaims(List<Claim> claims, IEnumerable<string> roles)
        
            foreach (var role in roles)
            
                var roleClaim = new Claim(ClaimTypes.Role, role);
                claims.Add(roleClaim);
            
        

【讨论】:

【参考方案2】:

您可以在生成令牌 ex 时在声明中包含您的角色名称:

var claims = new List<Claim>
            
                new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
                new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                new Claim(ClaimTypes.NameIdentifier, user.Id),
                new Claim(ClaimTypes.Role,"The Role Of the logged in user, you can get from your DB")
            ;

或者您可以在传递的参数中使用角色对象,然后添加相同的上述行。

 public class IdentityUser : IUser
    
        public IdentityUser();
        public IdentityUser(string userName);

        public virtual ICollection<IdentityUserClaim> Claims  get; 
        public virtual string Id  get; set; 
        public virtual ICollection<IdentityUserLogin> Logins  get; 
        public virtual string PasswordHash  get; set; 
        public virtual ICollection<IdentityUserRole> Roles  get; 
        public virtual string SecurityStamp  get; set; 
        public virtual string UserName  get; set; 
    

【讨论】:

以上是关于Asp.Net Core WebApi:授权属性错误 403的主要内容,如果未能解决你的问题,请参考以下文章

Asp.net Core WebAPI 基于资源的控制器级别之外的授权

如何在 ASP.NET Core 中基于 appsettings 有条件地使用授权

Asp.Net WebApi 上的自定义授权属性

选择 webApi 模板时如何将 ASP.Net 身份添加到 Asp.Net Core?

IdentityServer4 基于角色的 Web API 授权与 ASP.NET Core 身份

ASP.NET Core WebAPI FromBody 属性未验证对象非引用字段