EF Core 2.2-基于角色的授权无法获取我的用户的角色

时间:2019-07-10 14:56:20

标签: c# .net entity-framework identity core

我已经敲了好一阵子了。我为我的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已正确填充。登录时,获取我的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
}

1 个答案:

答案 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
}