我有一个SPA,它具有ASP.NET Core Web API以及使用AddIdentityServer
然后AddIdentityServerJwt
然后打开的内置身份服务器:
services.AddIdentityServer()
.AddApiAuthorization<User, UserDataContext>();
services.AddAuthentication()
.AddIdentityServerJwt();
我也有一个授权策略设置,需要一个“管理员”角色声明:
services.AddAuthorization(options =>
{
options.AddPolicy("IsAdmin", policy => policy.RequireClaim(ClaimTypes.Role, "Admin"));
});
我有一个使用此策略的控制器操作
[Authorize(Policy = "IsAdmin")]
[HttpDelete("{id}")]
public IActionResult Deleten(int id)
{
...
}
经过身份验证的用户确实具有“管理员”角色声明:
此身份验证用户的访问令牌似乎不包含管理员声明:
尝试向管理员用户请求此资源时,我收到403返回:
因此,如果我正确理解这一点,则IdentityServer不包含管理员角色声明,因此用户无权访问该资源。
是否可以使用AddIdentityServerJwt
配置IdentityServer使用的声明?还是我误解了为什么这不起作用。
答案 0 :(得分:1)
在Identity Server方面,您可以创建Profile Service,以使IDS4在颁发令牌时包含role
声明。
您可以从ClaimsPrincipal获取角色声明,或者从数据库获取角色并创建个人档案服务,例如:
public class MyProfileService : IProfileService
{
public MyProfileService()
{ }
public Task GetProfileDataAsync(ProfileDataRequestContext context)
{
//get role claims from ClaimsPrincipal
var roleClaims = context.Subject.FindAll(JwtClaimTypes.Role);
//add your role claims
context.IssuedClaims.AddRange(roleClaims);
return Task.CompletedTask;
}
public Task IsActiveAsync(IsActiveContext context)
{
// await base.IsActiveAsync(context);
return Task.CompletedTask;
}
}
并在Startup.cs中注册:
services.AddTransient<IProfileService, MyProfileService>();
在客户端,您应该从JWT令牌映射角色声明,并尝试在AddOpenIdConnect
中间件中的config下面进行操作:
options.ClaimActions.MapJsonKey("role", "role", "role");
options.TokenValidationParameters.RoleClaimType = "role";
然后您的api可以验证访问令牌并使用角色策略进行授权。
答案 1 :(得分:0)
我这样做的时候没有使用角色,而是使用了添加到用户令牌中的特殊声明。我创建了一个CustomUserClaimsPrincipalFactory,这使我可以向用户添加其他声明。
services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, CustomUserClaimsPrincipalFactory>();
public class CustomUserClaimsPrincipalFactory : UserClaimsPrincipalFactory<ApplicationUser, IdentityRole<long>>
{
public CustomUserClaimsPrincipalFactory(
UserManager<ApplicationUser> userManager,
RoleManager<IdentityRole<long>> roleManager,
IOptions<IdentityOptions> optionsAccessor)
: base(userManager, roleManager, optionsAccessor)
{
}
protected override async Task<ClaimsIdentity> GenerateClaimsAsync(ApplicationUser user)
{
var userId = await UserManager.GetUserIdAsync(user);
var userName = await UserManager.GetUserNameAsync(user);
var id = new ClaimsIdentity("Identity.Application",
Options.ClaimsIdentity.UserNameClaimType,
Options.ClaimsIdentity.RoleClaimType);
id.AddClaim(new Claim(Options.ClaimsIdentity.UserIdClaimType, userId));
id.AddClaim(new Claim(Options.ClaimsIdentity.UserNameClaimType, user.Name));
id.AddClaim(new Claim("preferred_username", userName));
id.AddClaim(new Claim("culture", user.Culture ?? "da-DK"));
if (UserManager.SupportsUserSecurityStamp)
{
id.AddClaim(new Claim(Options.ClaimsIdentity.SecurityStampClaimType,
await UserManager.GetSecurityStampAsync(user)));
}
if (UserManager.SupportsUserClaim)
{
id.AddClaims(await UserManager.GetClaimsAsync(user));
}
if(user.IsXenaSupporter)
id.AddClaim(new Claim("supporter", user.Id.ToString()));
return id;
}
}
services.AddAuthorization(options =>
{
options.AddPolicy("Supporter", policy => policy.RequireClaim("supporter"));
});
[Authorize(AuthenticationSchemes = "Bearer", Policy = "Supporter")]
[HttpPost("supporter")]
public async Task<ActionResult> ChangeToSpecificUser([FromBody] ChangeUserRequest request)
{
// ..................
}
答案 2 :(得分:0)
其他答案之一确实与所讨论的特定用例非常接近,但是没有找到关于SPA的要点。
首先,您必须像建议的那样添加IProfileService实现:
public class MyProfileService : IProfileService
{
public MyProfileService()
{ }
public Task GetProfileDataAsync(ProfileDataRequestContext context)
{
//get role claims from ClaimsPrincipal
var roleClaims = context.Subject.FindAll(JwtClaimTypes.Role);
//add your role claims
context.IssuedClaims.AddRange(roleClaims);
return Task.CompletedTask;
}
public Task IsActiveAsync(IsActiveContext context)
{
// await base.IsActiveAsync(context);
return Task.CompletedTask;
}
}
但是请继续执行以下操作:
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>()
.AddProfileService<MyProfileService>();
您的主张将在JWT上公开。将 ClaimTypes.Role 常量替换为与要公开的声明类型相对应的任何字符串。