User.Identity.Name равно нулю в моем ASP. NET Core Web API - PullRequest
0 голосов
/ 17 апреля 2020

Я добавил ASP. NET Идентификатор ядра и Identity Server4 в одном проекте с одной базой данных, и я хочу использовать свой Identity Server во всех других проектах.

IdentityServer4 Класс запуска

public class Startup
{
    public IConfigurationRoot Config { get; set; }

    public Startup(IConfiguration configuration)
    {
        Config = new ConfigurationBuilder()
                     .SetBasePath(Directory.GetCurrentDirectory())
                     .AddJsonFile("appsettings.json", false)
                     .Build();

        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        IdentityModelEventSource.ShowPII = true;

        //=== Identity Config ===
        string ConnectionString = Config.GetSection("AppSettings:DefaultConnection").Value;
        var migrationAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;

        //-----------------------------------------------------------------
        services.AddDbContext<MyIdentityDbContext>(options =>
             options.UseSqlServer(ConnectionString, sql => sql.MigrationsAssembly(migrationAssembly)));

        //-----------------------------------------------------------------
        services.AddIdentity<MyIdentityUser, IdentityRole>(op =>
        {
            op.Password.RequireDigit = false;
            op.Password.RequiredLength = 6;
            op.Password.RequireUppercase = false;
            op.Password.RequireLowercase = false;
            op.Password.RequireNonAlphanumeric = false;
        })
        .AddEntityFrameworkStores<MyIdentityDbContext>()
        .AddDefaultTokenProviders();

        //=== IdentityServer4 config ===
        services.AddIdentityServer(options =>
        {
            options.Events.RaiseErrorEvents = true;
            options.Events.RaiseInformationEvents = true;
            options.Events.RaiseFailureEvents = true;
            options.Events.RaiseSuccessEvents = true;
        })
            .AddDeveloperSigningCredential()
            .AddConfigurationStore(options =>
            {
                options.ConfigureDbContext = b => b.UseSqlServer(ConnectionString, sql => sql.MigrationsAssembly(migrationAssembly));
            })
            .AddOperationalStore(options =>
            {
                options.ConfigureDbContext = b => b.UseSqlServer(ConnectionString, sql => sql.MigrationsAssembly(migrationAssembly));
            })
            .AddAspNetIdentity<MyIdentityUser>();

        services.AddMvc(options => options.EnableEndpointRouting = false);
        services.AddAuthorization();
        services.AddControllers();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        app.UseAuthentication();
        app.UseRouting();

        app.UseAuthorization();
        app.UseIdentityServer();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}

Мой конфигурационный класс, который я заполнил моей базой данных идентичности:

public class Config
{
    public static IEnumerable<IdentityResource> GetIdentityResources()
    {
        return new List<IdentityResource>
        {
            new IdentityResources.OpenId(),
            new IdentityResources.Email(),
            new IdentityResources.Profile(),
        };
    }

    public static IEnumerable<ApiResource> GetApis()
    {
        return new List<ApiResource>
        {
            new ApiResource("MyAPI", "My asp.net core web api"),
        };
    }

    public static IEnumerable<Client> GetClients()
    {
        return new List<Client>
        {
            new Client()
            {
                 ClientId = "MyAndroidApp",
                 ClientName = "My Application for Android",
                 AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
                 ClientSecrets =
                 {
                    new Secret("secret".Sha256())
                 },
                 AllowedScopes=
                 {
                     IdentityServerConstants.StandardScopes.OpenId,
                     IdentityServerConstants.StandardScopes.Profile,
                     IdentityServerConstants.StandardScopes.Email,
                     IdentityServerConstants.StandardScopes.Address,
                     "MyAPI"
                 },
            },
        };
    }
}

Я зарегистрировал пользователя с ролью Admin с методом ниже действия в Контроллере пользователя в моем проекте IdentityServer4 & Identity

[HttpPost]
public async Task<IActionResult> Post([FromBody]SignUpModel model)
{                               
    MydentityUser NewUser = new MydentityUser ()
            {
                UserName = model.UserName,
            };
    IdentityResult result = await UserManager.CreateAsync(NewUser, model.Password);

    if (result.Succeeded)
    {
        if (!RoleManager.RoleExistsAsync("Admin").Result)
        {
            IdentityResult r = RoleManager.CreateAsync(new IdentityRole("Admin")).Result;
            r = RoleManager.CreateAsync(new IdentityRole("Member")).Result;
            r = RoleManager.CreateAsync(new IdentityRole("Guest")).Result;
        }

        result = await UserManager.AddToRoleAsync(NewUser, "Admin");

        if (result.Succeeded)
        {
            List<Claim> UserClaims = new List<Claim>() {
                    new Claim("userName", NewUser.UserName),
                    new Claim(JwtClaimTypes.Role, "Admin"),
                };

            result = await UserManager.AddClaimsAsync(NewUser, UserClaims.ToArray());
            return Ok("Registered");
        }
    }            
}

Теперь у меня есть еще один ASP. NET проект веб-API, который я хочу использовать этот API в моем android приложении.

Мой класс запуска

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
            .AddJwtBearer(options =>
            {
                options.Authority = "https://identity.mywebsite.ir";
                options.RequireHttpsMetadata = false;
                options.Audience = "MyAPI";                    
            });
         //I used below but not work too
        //.AddIdentityServerAuthentication(options =>
        //{
        //    options.Authority = "https://identity.mywebsite.ir";
        //    options.RequireHttpsMetadata = false;
        //    options.ApiName = "MyAPI";
        //    options.NameClaimType = ClaimTypes.Name;
        //    options.RoleClaimType = ClaimTypes.Role;                    
        //});

        services.AddOptions();
        string cs = Configuration["AppSettings:DefaultConnection"];
        services.AddDbContext<MyApiContext>(options =>
        {
            options.UseSqlServer(cs,
                sqlServerOptions =>
                {
                    sqlServerOptions.MigrationsAssembly("MyApi.Database");
                });
        });

        services.AddControllers();

        services.AddCors(options =>
        {
            options.AddPolicy("default", policy =>
            {
                policy.WithOrigins("*")
                    .AllowAnyHeader()
                    .AllowAnyMethod();
            });
        });
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        app.UseRouting();
        app.UseCors("default");
        app.UseAuthentication();
        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}

Моя проблема в том, как мне найти userId в моем Webapi, когда я использовал аутентификацию пользователя с ASP. NET Базовая идентификация в другом проекте,

У меня есть метод действия ниже в моем двух проектах (мой webapi и идентификационный сервер и проект идентификации). Я получил токен из приложения android с адреса / connect / token и отправил токен доступа с моим запросом.

public class TestController : ControllerBase
{
    public async Task<IActionResult> Index()
    {            
        string message = "";

        if (User.Identity.IsAuthenticated)
        {
            message += "You are Registered ";
        }
        else
        {
            message += "You are not Registered ";
        }

        if (string.IsNullOrWhiteSpace(User.Identity.Name))
        {
            message += "UserId is null";
        }
        else
        {
            message += "UserId is not null";
        }

        return Ok(message);
    }
}

Я получил это сообщение:

Вы не являетесь зарегистрированный идентификатор пользователя имеет значение null

Как я могу получить доступ к своему идентификатору пользователя в моем WebAPI? Почему User.Identity.Name имеет значение null? Почему User.Identity.Claims.Count 0?

Редактировать

Я ввел токен доступа на веб-сайте jwt.io, это вывод

{
  "nbf": 1587133648,
  "exp": 1587137248,
  "iss": "https://identity.mywebsite.ir",
  "aud": "MyAPI",
  "client_id": "MyAndroidApp",
  "sub": "7e904278-78cc-46a8-9943-51dfeb360d8e",// I want this in my api but i get null
  "auth_time": 1587133648,
  "idp": "local",
  "scope": [
    "openid",
    "MyAPI"
  ],
  "amr": [
    "pwd"
  ]
}

Класс запуска MyApi

 public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = IdentityServerAuthenticationDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = "oidc";
            })

        .AddIdentityServerAuthentication(options =>
        {
            options.Authority = "https://identity.mywebsite.ir";
                options.RequireHttpsMetadata = false;
            options.ApiName = "MyAPI";
            });

            services.AddOptions();
            string cs = Configuration["AppSettings:DefaultConnection"];
            services.AddDbContext<MyCommonDbContext>(options =>
            {
                options.UseSqlServer(cs,
                    sqlServerOptions =>
                    {
                        sqlServerOptions.MigrationsAssembly("MyAppProjectName");
                    });
            });
            services.AddDbContext<MyAppContext>(options =>
            {
                options.UseSqlServer(cs,
                    sqlServerOptions =>
                    {
                        sqlServerOptions.MigrationsAssembly("MyAppProjectName");
                    });
            });

            services.AddControllers();

            services.AddCors(options =>
            {
                options.AddPolicy("default", policy =>
                {
                    policy.WithOrigins("http://*.mywebsite.ir")
                        .AllowAnyHeader()
                        .AllowAnyMethod();
                });
            });
        }
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            app.UseRouting();
            app.UseCors("default");
            app.UseAuthentication();
            app.UseAuthorization();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }

Ответы [ 2 ]

2 голосов
/ 20 апреля 2020

В файле «MyApi» startup.cs в ConfigureServices:

1 - убедитесь, что вы выполняете эту строку кода прямо перед AddAuthentication: JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear ();

Потому что (спасибо !!! Майкрософт -_-) по умолчанию сопоставление типа заявки для имени:

http://schemas.microsoft.com/ws/2008/06/identity/claims/name (для имени или чего-то подобного )

http://schemas.microsoft.com/ws/2008/06/identity/claims/role. (для роли)

http://schemas.microsoft.com/ws/2008/06/identity/claims/nameidentifier (для идентификатора)

Поэтому вам необходимо очистить это отображение, поскольку в вашем токене типы заявок являются стандартом jwt, sub == ID пользователя, и вы не встраиваете имя или роли на данный момент, основываясь на вашем токене, который вы поделились

, как я обычно использую эту часть кода:

services.AddAuthentication("Bearer")
                .AddJwtBearer("Bearer", options =>
                {
                    options.Authority = "";
                    options.RequireHttpsMetadata = true;
                    options.Audience = "myapi";
                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        NameClaimType = "name",
                        RoleClaimType = "role",
                    };
                });

Вам понадобится только эта часть:

                        options.TokenValidationParameters = new TokenValidationParameters
                    {
                        NameClaimType = "name",
                        RoleClaimType = "role",
                    };

Кстати, для keep require https задано значение true, а не false.

Для UserId я думаю, что достаточно только очистки входящего типа по умолчанию.


Я не уверен, что вам действительно нужен второй шаг, но просто дважды проверьте:

2 - убедитесь, что значением AuthenticationScheme является «Bearer»: options.DefaultAuthenticateScheme = IdentityServerAuthenticationDefaults.AuthenticationScheme ;

3 - при запуске IdentityServer4

, пожалуйста, сохраняйте UseAuthentication после UseRouting не раньше (Это не относится к вашему вопросу, но я только что заметил)

0 голосов
/ 19 апреля 2020

В моем случае проблема была в том, что я не добавил UserClaims в ApiResources, поэтому я изменил метод заполнения ApiResource, как показано ниже, и добавил утверждения:

public static IEnumerable<ApiResource> GetApis()
        {

            return new List<ApiResource>
            {
                new ApiResource("MyAPI", "My Asp.net core WebApi,the best Webapi!"){
                    UserClaims =
                    {
                        JwtClaimTypes.Name,
                        JwtClaimTypes.Subject,
                        JwtClaimTypes.Role,
                    }
                },
            };
        }

Теперь я получу UserId и Имя пользователя с кодом ниже


    public static class ClaimsPrincipalExtensions
    {
        public static string GetSub(this ClaimsPrincipal principal)
        {
            return principal?.FindFirst(x => x.Type.Equals("sub"))?.Value;
        }
        public static string GetEmail(this ClaimsPrincipal principal)
        {
            return principal?.FindFirst(x => x.Type.Equals("email"))?.Value;
        }
    }

Получение идентификатора пользователя

string UserId=User.GetSub();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...