User.Identity.Name 在我的 ASP.NET 核心网站 API 中为空
User.Identity.Name is null in my ASP.NET Core Web API
我已经在一个项目和一个数据库中添加了 ASP.NET Core identity 和 Identity Server4,我想在所有其他项目中使用我的 Identity Server。
IdentityServer4 启动Class
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();
});
}
}
我的配置 class,我用它来为我的身份数据库设置种子:
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"
},
},
};
}
}
我已经在我的 IdentityServer4&Identity 项目的用户控制器中使用以下操作方法注册了一个角色为 Admin 的用户
[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 Web API 项目,我想在我的 android 应用程序中使用这个 api。
我的创业公司class
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();
});
}
}
我的问题是当我在另一个项目中使用 ASP.NET Core Identity 的用户身份验证时,如何在我的 Webapi 中找到 userId,
我的两个项目(我的 webapi 和 identityserver & identity 项目)中有以下操作方法。我已从 /connect/token 地址的 android 应用程序获取令牌,并随请求发送访问令牌。
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);
}
}
我收到这条消息:
You are not registered UserId is null
如何在我的 WebAPI 中访问我的 UserId?为什么 User.Identity.Name 为空?为什么 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 启动Class
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();
});
}
}
在我的例子中,问题在于我没有将 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 和 UserName
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;
}
}
正在获取 UserId
string UserId=User.GetSub();
在 ConfigureServices 的 "MyApi" startup.cs 文件中:
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(对于 id)
因此您需要清除此映射,因为在您的令牌中,声明类型是 jwt 标准,sub == userid,并且您暂时不会根据共享的令牌嵌入名称或角色
顺便说一句,我通常使用这部分代码:
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",
};
顺便说一下,将要求 https 设置为 true 而不是 false。
对于UserId我觉得只要清除默认入站类型就可以了。
我不确定您是否真的需要第二步,但请仔细检查:
2- 确保 AuthenticationScheme 值为 "Bearer":
options.DefaultAuthenticateScheme = IdentityServerAuthenticationDefaults.AuthenticationScheme;
3-在IdentityServer4启动中
请在 UseRouting 之后保留 UseAuthentication,而不是之前(这与您的问题无关,但我刚刚注意到)
我已经在一个项目和一个数据库中添加了 ASP.NET Core identity 和 Identity Server4,我想在所有其他项目中使用我的 Identity Server。
IdentityServer4 启动Class
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();
});
}
}
我的配置 class,我用它来为我的身份数据库设置种子:
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"
},
},
};
}
}
我已经在我的 IdentityServer4&Identity 项目的用户控制器中使用以下操作方法注册了一个角色为 Admin 的用户
[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 Web API 项目,我想在我的 android 应用程序中使用这个 api。
我的创业公司class
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();
});
}
}
我的问题是当我在另一个项目中使用 ASP.NET Core Identity 的用户身份验证时,如何在我的 Webapi 中找到 userId,
我的两个项目(我的 webapi 和 identityserver & identity 项目)中有以下操作方法。我已从 /connect/token 地址的 android 应用程序获取令牌,并随请求发送访问令牌。
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);
}
}
我收到这条消息:
You are not registered UserId is null
如何在我的 WebAPI 中访问我的 UserId?为什么 User.Identity.Name 为空?为什么 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 启动Class
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();
});
}
}
在我的例子中,问题在于我没有将 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 和 UserName
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;
}
}
正在获取 UserId
string UserId=User.GetSub();
在 ConfigureServices 的 "MyApi" startup.cs 文件中:
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(对于 id)
因此您需要清除此映射,因为在您的令牌中,声明类型是 jwt 标准,sub == userid,并且您暂时不会根据共享的令牌嵌入名称或角色
顺便说一句,我通常使用这部分代码:
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",
};
顺便说一下,将要求 https 设置为 true 而不是 false。
对于UserId我觉得只要清除默认入站类型就可以了。
我不确定您是否真的需要第二步,但请仔细检查:
2- 确保 AuthenticationScheme 值为 "Bearer": options.DefaultAuthenticateScheme = IdentityServerAuthenticationDefaults.AuthenticationScheme;
3-在IdentityServer4启动中
请在 UseRouting 之后保留 UseAuthentication,而不是之前(这与您的问题无关,但我刚刚注意到)