首页 文章

如何更新ASP.NET标识中的声明?

提问于
浏览
69

我正在为我的MVC5项目使用OWIN身份验证 . 这是我的 SignInAsync

private async Task SignInAsync(ApplicationUser user, bool isPersistent)
        {
            var AccountNo = "101";
            AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
            var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
            identity.AddClaim(new Claim(ClaimTypes.UserData, AccountNo));
            AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent, RedirectUri="Account/Index"}, identity);
        }

如您所见,我在声明列表中添加了 AccountNo .

现在,如何在我的应用程序中的某个时刻更新此声明?到目前为止,我有这个:

public string AccountNo
        {

            get
            {
                var CP = ClaimsPrincipal.Current.Identities.First();
                var Account= CP.Claims.FirstOrDefault(p => p.Type == ClaimTypes.UserData);
                return Account.Value;
            }
            set
            {
                var CP = ClaimsPrincipal.Current.Identities.First();
                var AccountNo= CP.Claims.FirstOrDefault(p => p.Type == ClaimTypes.UserData).Value;
                CP.RemoveClaim(new Claim(ClaimTypes.UserData,AccountNo));
                CP.AddClaim(new Claim(ClaimTypes.UserData, value));
            }

        }

当我尝试删除声明时,我得到以下异常:

声明'http://schemas.microsoft.com/ws/2008/06/identity/claims/userdata:101'无法删除 . 它不是本身份的一部分,也不是包含此身份的委托人拥有的声明 . 例如,在创建具有角色的GenericPrincipal时,Principal将拥有该声明 . 角色将通过构造函数中传递的Identity公开,但不会由Identity实际拥有 . RolePrincipal存在类似的逻辑 .

有人可以帮我弄清楚如何更新索赔吗?

11 回答

  • -1

    我根据给定的ClaimsIdentity创建了一个Add方法来添加/更新/读取声明

    namespace Foobar.Common.Extensions
    {
        public static class Extensions
        {
                public static void AddUpdateClaim(this IPrincipal currentPrincipal, string key, string value)
                {
                    var identity = currentPrincipal.Identity as ClaimsIdentity;
                    if (identity == null)
                        return;
    
                    // check for existing claim and remove it
                    var existingClaim = identity.FindFirst(key);
                    if (existingClaim != null)
                        identity.RemoveClaim(existingClaim);
    
                    // add new claim
                    identity.AddClaim(new Claim(key, value));
                    var authenticationManager = HttpContext.Current.GetOwinContext().Authentication;
                    authenticationManager.AuthenticationResponseGrant = new AuthenticationResponseGrant(new ClaimsPrincipal(identity), new AuthenticationProperties() { IsPersistent = true });
                }
    
                public static string GetClaimValue(this IPrincipal currentPrincipal, string key)
                {
                    var identity = currentPrincipal.Identity as ClaimsIdentity;
                    if (identity == null)
                        return null;
    
                    var claim = identity.Claims.FirstOrDefault(c => c.Type == key);
                    return claim.Value;
                }
        }
    }
    

    然后使用它

    using Foobar.Common.Extensions;
    
    namespace Foobar.Web.Main.Controllers
    {
        public class HomeController : Controller
        {
            public ActionResult Index()
            {
                // add/updating claims
                User.AddUpdateClaim("key1", "value1");
                User.AddUpdateClaim("key2", "value2");
                User.AddUpdateClaim("key3", "value3");
            }
    
            public ActionResult Details()
            {
                // reading a claim
                var key2 = User.GetClaim("key2");           
            }
        }
    }
    
  • 1

    您可以创建一个新的 ClaimsIdentity ,然后使用这样做进行声明更新 .

    set {
        // get context of the authentication manager
        var authenticationManager = HttpContext.GetOwinContext().Authentication;
    
        // create a new identity from the old one
        var identity = new ClaimsIdentity(User.Identity);
    
        // update claim value
        identity.RemoveClaim(identity.FindFirst("AccountNo"));
        identity.AddClaim(new Claim("AccountNo", value));
    
        // tell the authentication manager to use this new identity
        authenticationManager.AuthenticationResponseGrant = 
            new AuthenticationResponseGrant(
                new ClaimsPrincipal(identity),
                new AuthenticationProperties { IsPersistent = true }
            );
    }
    
  • -1

    另一种(异步)方法,使用Identity的UserManager和SigninManager来反映Identity cookie中的更改(并可选择从db表AspNetUserClaims中删除声明):

    // Get User and a claims-based identity
    ApplicationUser user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
    var Identity = new ClaimsIdentity(User.Identity);
    
    // Remove existing claim and replace with a new value
    await UserManager.RemoveClaimAsync(user.Id, Identity.FindFirst("AccountNo"));
    await UserManager.AddClaimAsync(user.Id, new Claim("AccountNo", value));
    
    // Re-Signin User to reflect the change in the Identity cookie
    await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
    
    // [optional] remove claims from claims table dbo.AspNetUserClaims, if not needed
    var userClaims = UserManager.GetClaims(user.Id);
    if (userClaims.Any())
    {
      foreach (var item in userClaims)
      {
        UserManager.RemoveClaim(user.Id, item);
      }
    }
    
  • 7

    我也得到了那个例外并且像这样清理了一些事情

    var identity = User.Identity as ClaimsIdentity;
    var newIdentity = new ClaimsIdentity(identity.AuthenticationType, identity.NameClaimType, identity.RoleClaimType);
    newIdentity.AddClaims(identity.Claims.Where(c => false == (c.Type == claim.Type && c.Value == claim.Value)));
    // the claim has been removed, you can add it with a new value now if desired
    AuthenticationManager.SignOut(identity.AuthenticationType);
    AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, newIdentity);
    
  • 0

    当我使用MVC5时,在这里添加声明 .

    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(PATAUserManager manager)
        {
            // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
            var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
            // Add custom user claims here
            userIdentity.AddClaim(new Claim(ClaimTypes.Role, this.Role));
    
            return userIdentity;
        }
    

    当我在SignInAsync函数中检查声明结果时,无论如何我都无法使用角色值 . 但...

    在此请求完成后,我可以在其他操作中访问Role(另一个请求) .

    var userWithClaims = (ClaimsPrincipal)User;
            Claim CRole = userWithClaims.Claims.First(c => c.Type == ClaimTypes.Role);
    

    所以,我认为可能异步导致IEnumerable在流程后面更新 .

  • 93

    使用.net核心2.1的最新Asp.Net标识,我能够使用以下逻辑更新用户声明 .

    • 注册 UserClaimsPrincipalFactory ,以便每次 SignInManager 用户进入时,都会创建声明 .
    services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, UserClaimService>();
    
    • 如下所示实现自定义 UserClaimsPrincipalFactory<TUser, TRole>
    public class UserClaimService : UserClaimsPrincipalFactory<ApplicationUser, ApplicationRole>
    {
        private readonly ApplicationDbContext _dbContext;
    
        public UserClaimService(ApplicationDbContext dbContext, UserManager<ApplicationUser> userManager, RoleManager<ApplicationRole> roleManager, IOptions<IdentityOptions> optionsAccessor) : base(userManager, roleManager, optionsAccessor)
        {
            _dbContext = dbContext;
        }
    
        public override async Task<ClaimsPrincipal> CreateAsync(ApplicationUser user)
        {
            var principal = await base.CreateAsync(user);
    
            // Get user claims from DB using dbContext
    
            // Add claims
            ((ClaimsIdentity)principal.Identity).AddClaim(new Claim("claimType", "some important claim value"));
    
            return principal;
        }
    }
    
    • 稍后在您的应用程序中,当您更改数据库中的某些内容并希望将其反映给经过身份验证和登录的用户时,以下行将实现此目的:
    var user = await _userManager.GetUserAsync(User);
    await _signInManager.RefreshSignInAsync(user);
    

    这样可以确保用户无需再次登录即可查看最新信息 . 我把它放在控制器中返回结果之前,这样当操作完成时,一切都安全地刷新 .

    您只需以静默方式签署用户并刷新状态,而不是编辑现有声明并为安全cookie等创建竞争条件

  • 0

    要从数据库中删除声明详细信息,我们可以使用以下代此外,我们需要再次登录才能更新Cookie值

    // create a new identity 
                var identity = new ClaimsIdentity(User.Identity);
    
                // Remove the existing claim value of current user from database
                if(identity.FindFirst("NameOfUser")!=null)
                    await UserManager.RemoveClaimAsync(applicationUser.Id, identity.FindFirst("NameOfUser"));
    
                // Update customized claim 
                await UserManager.AddClaimAsync(applicationUser.Id, new Claim("NameOfUser", applicationUser.Name));
    
                // the claim has been updates, We need to change the cookie value for getting the updated claim
                AuthenticationManager.SignOut(identity.AuthenticationType);
                await SignInManager.SignInAsync(Userprofile, isPersistent: false, rememberBrowser: false);
    
                return RedirectToAction("Index", "Home");
    
  • 13

    多个Cookie,多个声明

    public class ClaimsCookie
        {
            private readonly ClaimsPrincipal _user;
            private readonly HttpContext _httpContext;
            public ClaimsCookie(ClaimsPrincipal user, HttpContext httpContext = null)
            {
                _user = user;
                _httpContext = httpContext;
            }
    
            public string GetValue(CookieName cookieName, KeyName keyName)
            {
                var principal = _user as ClaimsPrincipal;
                var cp = principal.Identities.First(i => i.AuthenticationType == ((CookieName)cookieName).ToString());
                return cp.FindFirst(((KeyName)keyName).ToString()).Value;
            }
            public async void SetValue(CookieName cookieName, KeyName[] keyName, string[] value)
            {
                if (keyName.Length != value.Length)
                {
                    return;
                }
                var principal = _user as ClaimsPrincipal;
                var cp = principal.Identities.First(i => i.AuthenticationType == ((CookieName)cookieName).ToString());
                for (int i = 0; i < keyName.Length; i++)
                {
                    if (cp.FindFirst(((KeyName)keyName[i]).ToString()) != null)
                    {
                        cp.RemoveClaim(cp.FindFirst(((KeyName)keyName[i]).ToString()));
                        cp.AddClaim(new Claim(((KeyName)keyName[i]).ToString(), value[i]));
                    }
    
                }
                await _httpContext.SignOutAsync(CookieName.UserProfilCookie.ToString());
                await _httpContext.SignInAsync(CookieName.UserProfilCookie.ToString(), new ClaimsPrincipal(cp),
                    new AuthenticationProperties
                    {
                        IsPersistent = bool.Parse(cp.FindFirst(KeyName.IsPersistent.ToString()).Value),
                        AllowRefresh = true
                    });
            }
            public enum CookieName
            {
                CompanyUserProfilCookie = 0, UserProfilCookie = 1, AdminPanelCookie = 2
            }
            public enum KeyName
            {
                Id, Name, Surname, Image, IsPersistent
            }
        }
    
  • 2
    if (HttpContext.User.Identity is ClaimsIdentity identity)
            {
                identity.RemoveClaim(identity.FindFirst("userId"));
                identity.AddClaim(new Claim("userId", userInfo?.id.ToString()));
                await HttpContext.SignInAsync(
                    CookieAuthenticationDefaults.AuthenticationScheme,
                    new ClaimsPrincipal(HttpContext.User.Identity));
            }
    
  • 0

    扩展方法对我来说非常有用,只有一个例外,即如果用户注销旧的声明集仍然存在,那么只需稍加修改就像通过用户管理器一切都很好,你不需要注销和登录 . 我无法直接回答,因为我的名声已被废除:(

    public static class ClaimExtensions
    {
        public static void AddUpdateClaim(this IPrincipal currentPrincipal,    string key, string value, ApplicationUserManager userManager)
        {
            var identity = currentPrincipal.Identity as ClaimsIdentity;
            if (identity == null)
                return;
    
            // check for existing claim and remove it
            var existingClaim = identity.FindFirst(key);
            if (existingClaim != null)
            {
                RemoveClaim(currentPrincipal, key, userManager);
            }
    
            // add new claim
            var claim = new Claim(key, value);
            identity.AddClaim(claim);
            var authenticationManager = HttpContext.Current.GetOwinContext().Authentication;
            authenticationManager.AuthenticationResponseGrant = new AuthenticationResponseGrant(new ClaimsPrincipal(identity), new AuthenticationProperties() { IsPersistent = true });
            //Persist to store
            userManager.AddClaim(identity.GetUserId(),claim);
    
        }
    
        public static void RemoveClaim(this IPrincipal currentPrincipal, string key, ApplicationUserManager userManager)
        {
            var identity = currentPrincipal.Identity as ClaimsIdentity;
            if (identity == null)
                return ;
    
            // check for existing claim and remove it
            var existingClaims = identity.FindAll(key);
            existingClaims.ForEach(c=> identity.RemoveClaim(c));
    
            //remove old claims from store
            var user = userManager.FindById(identity.GetUserId());
            var claims =  userManager.GetClaims(user.Id);
            claims.Where(x => x.Type == key).ToList().ForEach(c => userManager.RemoveClaim(user.Id, c));
    
        }
    
        public static string GetClaimValue(this IPrincipal currentPrincipal, string key)
        {
            var identity = currentPrincipal.Identity as ClaimsIdentity;
            if (identity == null)
                return null;
    
            var claim = identity.Claims.First(c => c.Type == key);
            return claim.Value;
        }
    
        public static string GetAllClaims(this IPrincipal currentPrincipal, ApplicationUserManager userManager)
        {
            var identity = currentPrincipal.Identity as ClaimsIdentity;
            if (identity == null)
                return null;
    
            var claims = userManager.GetClaims(identity.GetUserId());
            var userClaims = new StringBuilder();
            claims.ForEach(c => userClaims.AppendLine($"<li>{c.Type}, {c.Value}</li>"));
            return userClaims.ToString();
        }
    
    
    }
    
  • 44

    干得好:

    var user = User as ClaimsPrincipal;
                var identity = user.Identity as ClaimsIdentity;
                var claim = (from c in user.Claims
                             where c.Type == ClaimTypes.UserData
                             select c).Single();
                identity.RemoveClaim(claim);
    

    取自here.

相关问题