为什么基于角色的授权无法获取用户的角色?



我为我的API设置了映射到MySQL数据库的身份。我在初创公司播种我的角色和一个用户:

if (!_roleManager.RoleExistsAsync("Admin").Result)
{
Role role = new Role();
role.Name = "Admin";
IdentityResult roleResult = _roleManager.CreateAsync(role).Result;
}
if (_userManager.FindByNameAsync("myuser").Result == null)
{
User user = new User();
user.UserName = "myuser";
user.Email = "myuser@test.com";
// Not secure, to be moved
user.PasswordHash = SecurePasswordHasher.Hash("testtest");
IdentityResult result = _userManager.CreateAsync(user).Result;
if (result.Succeeded)
{
_userManager.AddToRoleAsync(user, "Admin").Wait();
}
}

AspnetrolesAspnetusersAspnetuserroles填充正确。登录时,获取我的持有者令牌并尝试访问如下所示的 API 端点:

[HttpGet]
[Authorize]
public async Task<IActionResult> Index()
{
// Get objects
var objects = _repository.FindAll();
// Return response
return Ok(objects);
}

它工作正常。如果没有令牌,我会收到 401 未经授权。一切正常。但是当我尝试访问这样的端点时:

[HttpGet]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> Index()
{
// Get objects
var objects = _repository.FindAll();
// Return response
return Ok(objects);
}

它不起作用,我得到了 403 禁止。如果我的角色错了,这就是我想要的,但事实并非如此。如果我将角色设置为null而不是"Admin",那么它就可以了!所以我想有问题,应用程序无法检索我的用户角色。

数据库上下文:

public class AppContext : IdentityDbContext<User, Role, int, UserClaim, UserRole, UserLogin, RoleClaim, UserToken>
{
private Settings Settings { get; }
// Constructor
public AppContext()
{
// Build the MySql settings:
Settings = new Settings("dev")
.Configure(new MysqlSettings())
.Build();
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if(!optionsBuilder.IsConfigured)
{
optionsBuilder.UseLazyLoadingProxies();
optionsBuilder.UseMySql(Settings.GetRequiredConf<MysqlSettings>().Connection +
"TreatTinyAsBoolean=false;");
}
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Role>().ToTable("role");
builder.Entity<User>().ToTable("user");
builder.Entity<UserClaim>().ToTable("user_claim");
builder.Entity<UserToken>().ToTable("user_token");
builder.Entity<UserLogin>().ToTable("user_login");
builder.Entity<RoleClaim>().ToTable("role_claim");
builder.Entity<UserRole>(entity =>
{
entity.ToTable("user_role");
entity.HasOne(d => d.User)
.WithOne(p => (Data.Models.Identity.UserRole)p.UserRole)
.HasForeignKey<UserRole>(d => d.UserId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("FK_user_role_user_UserId");
entity.HasOne(d => d.Role)
.WithOne(p => (Data.Models.Identity.UserRole)p.UserRole)
.HasForeignKey<UserRole>(d => d.RoleId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("FK_user_role_role_RoleId");
});
}
}

启动:

public void ConfigureServices(IServiceCollection services)
{
// Add the environment to the services
services.AddSingleton(Environment);
// Add the settings to the services
services.AddSingleton(Settings);
// Add repository manager
services.AddRepositoryManager();
// Add Db context
services.AddSingleton(Context);
// Mvc service
services.AddMvc(options =>
{
options.Filters.Add(new AuthorizeFilter());
})
.AddJsonOptions(options =>
{
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
// Identity
services.AddIdentity<User, Role>(options =>
{
options.Tokens.PasswordResetTokenProvider = TokenOptions.DefaultEmailProvider;
options.Tokens.EmailConfirmationTokenProvider = TokenOptions.DefaultEmailProvider;
options.Password.RequireDigit = false;
options.Password.RequiredLength = 8;
options.Password.RequireLowercase = true;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
})
.AddEntityFrameworkStores<BreathBalanzContext>()
.AddDefaultTokenProviders();
// Custom password hasher
services.AddScoped<IPasswordHasher<User>, CustomPasswordHasher>();
// Authentication service
services.AddAuthentication(options =>
{
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(o =>
{
o.Authority = Settings.GetRequiredConf<AuthSettings>().Address;
o.Audience = "Api";
o.RequireHttpsMetadata = false;
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, UserManager<User> userManager, RoleManager<Role> roleManager)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseAuthentication();

// Seeding the roles and users here
Seeder.SeedData(userManager, roleManager);
app.UseMvc();
}

最初没有设置延迟加载,我认为这是问题的根源,但它没有帮助(不过延迟加载现在可以正常工作)。我不确定我错过了什么。

一个简单的_context.Set<User>().Find(id)返回以下内容,这应该证明延迟加载正在工作,因此应该足以让 Identity 获得我的用户角色?

{
"userClaim": [],
"userToken": [],
"userLogin": [],
"userRole": [
{
"role": {
"userRole": [],
"roleClaim": [],
"id": 3,
"name": "Admin",
"normalizedName": "ADMIN",
"concurrencyStamp": "34d3e595-035c-4f4b-9e5e-99c8f4221701"
},
"userId": 11,
"roleId": 3
}
],
"id": 11,
"userName": "myuser",
"normalizedUserName": "MYUSER",
"email": "myuser@dtest.com",
"normalizedEmail": "MYUSER@TEST.COM",
"emailConfirmed": false,
"passwordHash": "$MYHASH$V1$10000$v19jvXvMQeU5KtwWg2vs0f3Ou6J8+ANWJaXjPEkM9eEvQ6pm",
"securityStamp": "LRITM53N625U4SSFQLCPIYN2UFJHMYJP",
"concurrencyStamp": "1897bc87-fcd0-4759-ad8c-d21210351e04",
"phoneNumber": null,
"phoneNumberConfirmed": false,
"twoFactorEnabled": false,
"lockoutEnd": null,
"lockoutEnabled": true,
"accessFailedCount": 0
}

经过几个小时的搜索,我发现了问题。我只需要在创建令牌时向令牌添加一个Roles范围。

public override async Task<TokenResponse> GetTokenAsync(HttpClient client, string address)
{
return await client.RequestPasswordTokenAsync(new PasswordTokenRequest
{
Address = address,
UserName = Username,
Password = Password,
GrantType = "password",
ClientId = ClientId,
ClientSecret = ClientSecret,
Scope = "Api roles"
});
}

当然,在身份服务器的客户端配置中允许此作用域:

new Client
{
ClientId = "ro.security",
ClientSecrets = { new Secret(clientSecret.Sha256()) },
AllowedScopes = { "Api", "introspection", "roles" },
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
AllowOfflineAccess = true
}

最新更新