簡體   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