我已经敲了好一阵子了。我为我的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();
}
}
表Aspnetroles
,Aspnetusers
和Aspnetuserroles
已正确填充。登录时,获取我的Bearer令牌并尝试访问如下所示的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"
,那么它将起作用!因此,我想这有问题,应用程序无法检索我的用户角色。
DbContext:
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)
返回以下内容,这可以证明延迟加载正在工作,因此应该足以让身份获得我的用户角色?
{
"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
}
答案 0 :(得分: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"
});
}
当然可以在IdentityServer的客户端配置中允许此范围:
new Client
{
ClientId = "ro.security",
ClientSecrets = { new Secret(clientSecret.Sha256()) },
AllowedScopes = { "Api", "introspection", "roles" },
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
AllowOfflineAccess = true
}