有没有办法在Identity API生成的cookie中存储用户的一些自定义数据?
我们正在构建一个多租户应用程序,因此多家公司可以访问我们的应用程序的同一个实例。这就是为什么我需要为特定用户在身份cookie中存储来自用户的公司代码,以便在关闭浏览器后返回Web应用程序时从用户检索数据。
答案 0 :(得分:13)
您可以通过实施自定义UserClaimsPrincipalFactory并为商店编号添加自定义声明来实现这一目标,然后将其与其他声明一起存储在Cookie中。
以下是我项目中的示例代码,其中我添加了几个自定义声明,包括SiteGuid,因为我的场景也是多租户
using cloudscribe.Core.Models;
using Microsoft.AspNet.Identity;
using Microsoft.Extensions.OptionsModel;
using System;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
namespace cloudscribe.Core.Identity
{
public class SiteUserClaimsPrincipalFactory<TUser, TRole> : UserClaimsPrincipalFactory<TUser, TRole>
where TUser : SiteUser
where TRole : SiteRole
{
public SiteUserClaimsPrincipalFactory(
ISiteRepository siteRepository,
SiteUserManager<TUser> userManager,
SiteRoleManager<TRole> roleManager,
IOptions<IdentityOptions> optionsAccessor) : base(userManager, roleManager, optionsAccessor)
{
if (siteRepository == null) { throw new ArgumentNullException(nameof(siteRepository)); }
siteRepo = siteRepository;
options = optionsAccessor.Value;
}
private ISiteRepository siteRepo;
private IdentityOptions options;
public override async Task<ClaimsPrincipal> CreateAsync(TUser user)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
var userId = await UserManager.GetUserIdAsync(user);
var userName = await UserManager.GetUserNameAsync(user);
var id = new ClaimsIdentity(
options.Cookies.ApplicationCookie.AuthenticationScheme,
Options.ClaimsIdentity.UserNameClaimType,
Options.ClaimsIdentity.RoleClaimType
);
id.AddClaim(new Claim(Options.ClaimsIdentity.UserIdClaimType, userId));
id.AddClaim(new Claim(Options.ClaimsIdentity.UserNameClaimType, userName));
if (UserManager.SupportsUserSecurityStamp)
{
id.AddClaim(new Claim(Options.ClaimsIdentity.SecurityStampClaimType,
await UserManager.GetSecurityStampAsync(user)));
}
if (UserManager.SupportsUserRole)
{
var roles = await UserManager.GetRolesAsync(user);
foreach (var roleName in roles)
{
id.AddClaim(new Claim(Options.ClaimsIdentity.RoleClaimType, roleName));
if (RoleManager.SupportsRoleClaims)
{
var role = await RoleManager.FindByNameAsync(roleName);
if (role != null)
{
id.AddClaims(await RoleManager.GetClaimsAsync(role));
}
}
}
}
if (UserManager.SupportsUserClaim)
{
id.AddClaims(await UserManager.GetClaimsAsync(user));
}
ClaimsPrincipal principal = new ClaimsPrincipal(id);
if (principal.Identity is ClaimsIdentity)
{
ClaimsIdentity identity = (ClaimsIdentity)principal.Identity;
Claim displayNameClaim = new Claim("DisplayName", user.DisplayName);
if (!identity.HasClaim(displayNameClaim.Type, displayNameClaim.Value))
{
identity.AddClaim(displayNameClaim);
}
Claim emailClaim = new Claim(ClaimTypes.Email, user.Email);
if (!identity.HasClaim(emailClaim.Type, emailClaim.Value))
{
identity.AddClaim(emailClaim);
}
ISiteSettings site = await siteRepo.Fetch(user.SiteId, CancellationToken.None);
if (site != null)
{
Claim siteGuidClaim = new Claim("SiteGuid", site.SiteGuid.ToString());
if (!identity.HasClaim(siteGuidClaim.Type, siteGuidClaim.Value))
{
identity.AddClaim(siteGuidClaim);
}
}
}
return principal;
}
}
}
然后在Startup中,您需要注册自定义工厂,以便注入并使用它而不是默认工具
services.AddScoped<IUserClaimsPrincipalFactory<SiteUser>, SiteUserClaimsPrincipalFactory<SiteUser, SiteRole>>();
另一种方法是使用声明转换,但是这种方法不会在Cookie中存储额外的声明,而是更新每个请求的声明,也就是说它会在请求的生命周期中向Cookie添加更多声明但不会修改cookie中的声明。
public class ClaimsTransformer : IClaimsTransformer
{
public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
((ClaimsIdentity)principal.Identity).AddClaim(new Claim("ProjectReader", "true"));
return Task.FromResult(principal);
}
}
然后在startup.cs中:
app.UseClaimsTransformation(new ClaimsTransformationOptions
{
Transformer = new ClaimsTransformer()
});