繁体   English   中英

ASP.Net Core 2.1 IdentityCore(在用户登录时未添加角色声明)

[英]ASP.Net Core 2.1 IdentityCore (Role Claims not being added on user sign in)

我将ASP.Net Core 2.1IdentityCore Service结合使用 ,该应用程序是纯API,完全没有视图。 对于身份验证,我纯粹使用https://github.com/aspnet-contrib/AspNet.Security.OpenId.Providers提供的Steam身份验证 (无用户/密码登录)

创建此API的目的是为了适应非常具体的身份验证工作流(用户只能使用Steam登录到API),因为前端Angular SPA可以很好地处理工作流。

问题是,当我向用户添加角色时(我已经播种了角色,并且已经将自己的Steam帐户添加到了“管理员角色”中),因此在登录时不会添加角色类型声明,因此当管理员用户尝试访问受[Authorize(Roles =“ Admin”)保护的API路由,我将收到未经授权的重定向。

在下面,我添加了我认为是必需的所有代码段(可以随意请求更多代码段)。

如果我使用(我目前正在将其用作临时解决方案,但对于将来的开发而言并不理想);

services.AddIdentity<User, Role>()
   .AddEntityFrameworkStores<RSContext>()
   .AddSignInManager<SignInManager<User>>()
   .AddRoleManager<RoleManager<Role>>()
   .AddDefaultTokenProviders();

该应用程序使用AuthController.cs中的所有现有代码在用户登录时正确添加角色声明(并且Authorize属性起作用),但是使用IdentityCore失败。 我觉得我错过了对此负责的一行,但是在拖了MSDN文档几天之后,我终于被淘汰了。

注意: API将在登录时正确地验证身份并设置用户cookie,但不会将用户角色添加到用户身份声明中。 因此,身份验证有效,授权无效。 如果我在未指定角色的情况下使用[Authorize]属性,则它可以正常工作,并且仅允许经过身份验证的用户访问路由,同时拒绝未经身份验证的用户。 可以在最后的“测试屏幕截图”中看到identities [0] .isAuthenticated = True,但是没有将admin角色添加到Identity的Claims中。 如上所述,如果我不使用AddIdentityCore而是使用AddIdentity,则会将角色正确添加到用户的声明中,并且[Authorize(Role =“ Admin”)]属性将按预期工作,仅允许与Admin不在同一范围的用户角色来访问它。

Startup.cs(省略的不相关部分,例如数据库连接)

public void ConfigureServices(IServiceCollection services)
{
    IdentityBuilder builder = services.AddIdentityCore<User>(opt =>
    {
        opt.Password.RequireDigit = true;
        opt.Password.RequiredLength = 6;
        opt.Password.RequireNonAlphanumeric = true;
        opt.Password.RequireUppercase = true;
        opt.User.AllowedUserNameCharacters += ":/";
    });

    builder = new IdentityBuilder(builder.UserType, typeof(Role), builder.Services);
    builder.AddEntityFrameworkStores<RSContext>();
    builder.AddSignInManager<SignInManager<User>>();
    builder.AddRoleValidator<RoleValidator<Role>>();
    builder.AddRoles<Role>();
    builder.AddRoleManager<RoleManager<Role>>();
    builder.AddClaimsPrincipalFactory<UserClaimsPrincipalFactory<User>>();
    builder.AddDefaultTokenProviders();

    services.AddAuthentication(options =>
    {
        options.DefaultScheme = IdentityConstants.ApplicationScheme;
        options.DefaultChallengeScheme = IdentityConstants.ApplicationScheme;
        options.DefaultAuthenticateScheme = IdentityConstants.ApplicationScheme;
        options.DefaultSignInScheme = IdentityConstants.ApplicationScheme;
        options.DefaultSignOutScheme = IdentityConstants.ApplicationScheme;
        options.DefaultForbidScheme = IdentityConstants.ApplicationScheme;
    })
        .AddSteam(options =>
        {
            options.ApplicationKey = Configuration.GetSection("Authentication:Steam:Key").Value;
            options.CallbackPath = "/api/auth/steam/callback";
            options.Events.OnAuthenticated = OnClientAuthenticated;
        })
        .AddIdentityCookies(options =>
        {
            options.ApplicationCookie.Configure(appCookie =>
            {
                appCookie.Cookie.Name = "RaidSimulator";
                appCookie.LoginPath = "/api/auth/login";
                appCookie.LogoutPath = "/api/auth/logout";
                appCookie.Cookie.HttpOnly = true;
                appCookie.Cookie.SameSite = SameSiteMode.Lax;
                appCookie.Cookie.IsEssential = true;
                appCookie.SlidingExpiration = true;
                appCookie.Cookie.Expiration = TimeSpan.FromMinutes(1);
                appCookie.Cookie.MaxAge = TimeSpan.FromDays(7);
            });
            options.ExternalCookie.Configure(extCookie =>
            {
                extCookie.Cookie.Name = "ExternalLogin";
                extCookie.LoginPath = "/api/auth/login";
                extCookie.LogoutPath = "/api/auth/logout";
                extCookie.Cookie.HttpOnly = true;
                extCookie.Cookie.SameSite = SameSiteMode.Lax;
                extCookie.Cookie.IsEssential = true;
                extCookie.Cookie.Expiration = TimeSpan.FromMinutes(10);
            });
        });
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env, RoleManager<Role> roleManager)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    RolesSeed.Seed(roleManager).Wait();

    app.UseCors();
    app.UseAuthentication();
    app.UseMvc();
}

// Responsible for storing/updating steam profile in database
private async Task OnClientAuthenticated(OpenIdAuthenticatedContext context)
{
    var rsContext = context.HttpContext.RequestServices.GetRequiredService<RSContext>();
    var userManager = context.HttpContext.RequestServices.GetRequiredService<UserManager<User>>();

    var profile = context.User?.Value<JObject>(SteamAuthenticationConstants.Parameters.Response)
                        ?.Value<JArray>(SteamAuthenticationConstants.Parameters.Players)?[0]?.ToObject<SteamProfile>();

    // TODO: Handle this better, Redir user to an informative error page or something
    if (profile == null)
        return;

    var dbProfile = await rsContext.SteamProfiles.FindAsync(profile.SteamId);
    if (dbProfile != null)
    {
        rsContext.Update(dbProfile);
        dbProfile.UpdateProfile(profile);
        await rsContext.SaveChangesAsync();
    }
    else
    {
        await rsContext.SteamProfiles.AddAsync(profile);
        await rsContext.SaveChangesAsync();
    }
}

AuthController.cs =>唯一负责对Identity.Application方案进行身份验证的代码

[HttpGet("callback")]
[Authorize(AuthenticationSchemes = "Steam")]
public async Task<IActionResult> Callback([FromQuery]string ReturnUrl)
{
    ReturnUrl = ReturnUrl?.Contains("api/") == true ? "/" : ReturnUrl;

    if (HttpContext.User.Claims.Count() > 0)
    {
        var provider = HttpContext.User.Identity.AuthenticationType;
        var nameIdentifier = HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
        var name = HttpContext.User.FindFirstValue(ClaimTypes.Name);

        var loginResult = await signInManager.ExternalLoginSignInAsync(provider, nameIdentifier, false);
        if (loginResult.Succeeded)
        {
            return Redirect(ReturnUrl ?? "/api/auth/claims");
        }

        var result = await userManager.CreateAsync(new User { UserName = nameIdentifier, SteamId = nameIdentifier.Split("/").Last() });
        if (result.Succeeded)
        {
            var user = await userManager.FindByNameAsync(nameIdentifier);
            var identity = await userManager.AddLoginAsync(user, new UserLoginInfo(provider, nameIdentifier, name));

            if (identity.Succeeded)
            {
                await signInManager.ExternalLoginSignInAsync(provider, nameIdentifier, false);
                return Redirect(ReturnUrl ?? "/api/auth/claims");
            }
        }
    }

    return BadRequest(new { success = false });
}

[HttpGet("claims")]
[Authorize]
public async Task<IActionResult> GetClaims()
{
    var user = await userManager.GetUserAsync(User);
    var claims =
        User.Claims.Select(c => new
        {
            c.Type,
            c.Value
        });

    var inAdmin = new string[] {
        "User.IsInRole(\"Admin\") = " + User.IsInRole("Admin"),
        "User.IsInRole(\"ADMIN\") = " + User.IsInRole("ADMIN"),
        "User.IsInRole(\"admin\") = " + User.IsInRole("admin"),
        "userManager.IsInRoleAsync(user, \"admin\") = " + await userManager.IsInRoleAsync(user, "admin")
    };

    return Ok(new { success = true, data = new { claims, inAdmin, User.Identities } });
}

RoleSeeder.cs

public static async Task Seed(RoleManager<Role> roleManager)
{
    // Developer Role
    if(!await roleManager.RoleExistsAsync("Developer"))
    {
        var role = new Role("Developer");
        await roleManager.CreateAsync(role);
    }
    // Community Manager Role
    if (!await roleManager.RoleExistsAsync("Community Manager"))
    {
        var role = new Role("Community Manager");
        await roleManager.CreateAsync(role);
    }
    // Admin Role
    if (!await roleManager.RoleExistsAsync("Admin"))
    {
        var role = new Role("Admin");
        await roleManager.CreateAsync(role);
    }
    // Moderator Role
    if (!await roleManager.RoleExistsAsync("Moderator"))
    {
        var role = new Role("Moderator");
        await roleManager.CreateAsync(role);
    }
}

测试屏幕截图: 声明/身份/最原始的API响应

将此问题发布到ASP.Net Identity GitHub存储库中,这是一个已知的错误,已在ASP.Net Core 2.2中解决。

链接: https//github.com/aspnet/Identity/issues/1997

您必须为这个问题拖路。

将请求发送到任何WebService ,如果已设置,则立即运行Authorization

  1. login之前如果要向WebService发送请求,并且想忽略Authorization ,则必须使用Allowanonymus Attribute,例如:

    [Allowanonymous] Public void Login(){//这里}

使用此属性,授权将忽略该请求。

  1. 现在! 如果要在login后发送请求,则应在登录时创建cookie ,然后将响应发送给client ,并且还将该cookie保存在client的localStorage中,以在client中进行标识。 之后,您必须在每个请求的header中设置该cookie 这样,您的授权将成为唐!

现在,如果您愿意,我可以按照最佳实践创建一个授权示例。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM