在客户端向委托人添加自定义声明(用于授权)
Adding custom claims (for authorization) to Principal on Client side
我已经能够在 .net core 3.1 上成功实现我自己的 IdentityServer4 身份验证服务器,并且我有一个连接到它的客户端 Web 应用程序。客户端成功接收访问令牌和 ID。令牌包含所有用户识别信息。
我的下一个 objective 是将用户在客户端中拥有的权限添加到 HttpContext.User as claims 在我获得令牌之后,然后再呈现任何其他页面。权限存储在通过 subjectId 与用户关联的数据库中。
我需要帮助了解在哪里可以添加这些声明,以便我的授权策略可以与主体中的声明一起使用。我希望每次发出令牌时都执行该过程。
P.S。我正在使用 GetClaimsFromUserInfoEndpoint = true 和 SaveTokens = true 的身份服务器代码流。
谢谢!
您可以使用 TokenValidated 事件来做到这一点
public class CustomJwtBearerEvents : JwtBearerEvents
{
public override async Task TokenValidated(TokenValidatedContext context)
{
var claims = context.Principal.Claims.ToList();
claims.Add(new Claim("key", "value"));
context.Principal = new ClaimsPrincipal(new ClaimsIdentity(claims, "Bearer")); }
}
然后在启动时注册
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
{
...
options.Events = new JwtBearerEvents();
options.EventsType = typeof(CustomJwtBearerEvents);
});
您可以在 identityServer4 中使用配置文件服务:
services .AddIdentityServer(x =>
{
x.IssuerUri = "null";
x.Authentication.CookieLifetime = TimeSpan.FromDays(10);
})
.
.
.
.AddProfileService<ProfileService>();
这是一个很好的参考:http://docs.identityserver.io/en/latest/reference/profileservice.html
而 ProfileService 可以像这样的代码模板:
public class ProfileService : IProfileService
{
private readonly UserManager<ApplicationUser> _userManager;
public ProfileService(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var subject = context.Subject ?? throw new ArgumentNullException(nameof(context.Subject));
var subjectId = subject.Claims.Where(x => x.Type == "sub").FirstOrDefault().Value;
var user = await _userManager.FindByIdAsync(subjectId);
if (user == null)
throw new ArgumentException("Invalid subject identifier");
var claims = GetClaimsFromUser(user,subject);
context.IssuedClaims = claims.Result.ToList();
}
public async Task IsActiveAsync(IsActiveContext context)
{
var subject = context.Subject ?? throw new ArgumentNullException(nameof(context.Subject));
var subjectId = subject.Claims.Where(x => x.Type == "sub").FirstOrDefault().Value;
var user = await _userManager.FindByIdAsync(subjectId);
context.IsActive = false;
if (user != null)
{
if (_userManager.SupportsUserSecurityStamp)
{
var security_stamp = subject.Claims.Where(c => c.Type == "security_stamp").Select(c => c.Value).SingleOrDefault();
if (security_stamp != null)
{
var db_security_stamp = await _userManager.GetSecurityStampAsync(user);
if (db_security_stamp != security_stamp)
return;
}
}
context.IsActive =
!user.LockoutEnabled ||
!user.LockoutEnd.HasValue ||
user.LockoutEnd <= DateTime.Now;
}
}
private async Task<IEnumerable<Claim>> GetClaimsFromUser(ApplicationUser user,ClaimsPrincipal subject)
{
var claims = new List<Claim>
{
new Claim(JwtClaimTypes.Subject, user.Id),
new Claim(JwtClaimTypes.PreferredUserName, user.UserName),
new Claim(JwtRegisteredClaimNames.UniqueName, user.UserName)
};
if (!string.IsNullOrWhiteSpace(user.Name))
claims.Add(new Claim("name", user.Name));
if (!string.IsNullOrWhiteSpace(user.LastName))
claims.Add(new Claim("last_name", user.LastName));
return claims;
}
}
我已经能够在 .net core 3.1 上成功实现我自己的 IdentityServer4 身份验证服务器,并且我有一个连接到它的客户端 Web 应用程序。客户端成功接收访问令牌和 ID。令牌包含所有用户识别信息。
我的下一个 objective 是将用户在客户端中拥有的权限添加到 HttpContext.User as claims 在我获得令牌之后,然后再呈现任何其他页面。权限存储在通过 subjectId 与用户关联的数据库中。
我需要帮助了解在哪里可以添加这些声明,以便我的授权策略可以与主体中的声明一起使用。我希望每次发出令牌时都执行该过程。
P.S。我正在使用 GetClaimsFromUserInfoEndpoint = true 和 SaveTokens = true 的身份服务器代码流。
谢谢!
您可以使用 TokenValidated 事件来做到这一点
public class CustomJwtBearerEvents : JwtBearerEvents
{
public override async Task TokenValidated(TokenValidatedContext context)
{
var claims = context.Principal.Claims.ToList();
claims.Add(new Claim("key", "value"));
context.Principal = new ClaimsPrincipal(new ClaimsIdentity(claims, "Bearer")); }
}
然后在启动时注册
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
{
...
options.Events = new JwtBearerEvents();
options.EventsType = typeof(CustomJwtBearerEvents);
});
您可以在 identityServer4 中使用配置文件服务:
services .AddIdentityServer(x =>
{
x.IssuerUri = "null";
x.Authentication.CookieLifetime = TimeSpan.FromDays(10);
})
.
.
.
.AddProfileService<ProfileService>();
这是一个很好的参考:http://docs.identityserver.io/en/latest/reference/profileservice.html 而 ProfileService 可以像这样的代码模板:
public class ProfileService : IProfileService
{
private readonly UserManager<ApplicationUser> _userManager;
public ProfileService(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var subject = context.Subject ?? throw new ArgumentNullException(nameof(context.Subject));
var subjectId = subject.Claims.Where(x => x.Type == "sub").FirstOrDefault().Value;
var user = await _userManager.FindByIdAsync(subjectId);
if (user == null)
throw new ArgumentException("Invalid subject identifier");
var claims = GetClaimsFromUser(user,subject);
context.IssuedClaims = claims.Result.ToList();
}
public async Task IsActiveAsync(IsActiveContext context)
{
var subject = context.Subject ?? throw new ArgumentNullException(nameof(context.Subject));
var subjectId = subject.Claims.Where(x => x.Type == "sub").FirstOrDefault().Value;
var user = await _userManager.FindByIdAsync(subjectId);
context.IsActive = false;
if (user != null)
{
if (_userManager.SupportsUserSecurityStamp)
{
var security_stamp = subject.Claims.Where(c => c.Type == "security_stamp").Select(c => c.Value).SingleOrDefault();
if (security_stamp != null)
{
var db_security_stamp = await _userManager.GetSecurityStampAsync(user);
if (db_security_stamp != security_stamp)
return;
}
}
context.IsActive =
!user.LockoutEnabled ||
!user.LockoutEnd.HasValue ||
user.LockoutEnd <= DateTime.Now;
}
}
private async Task<IEnumerable<Claim>> GetClaimsFromUser(ApplicationUser user,ClaimsPrincipal subject)
{
var claims = new List<Claim>
{
new Claim(JwtClaimTypes.Subject, user.Id),
new Claim(JwtClaimTypes.PreferredUserName, user.UserName),
new Claim(JwtRegisteredClaimNames.UniqueName, user.UserName)
};
if (!string.IsNullOrWhiteSpace(user.Name))
claims.Add(new Claim("name", user.Name));
if (!string.IsNullOrWhiteSpace(user.LastName))
claims.Add(new Claim("last_name", user.LastName));
return claims;
}
}