Saya menggunakan otentikasi OWIN untuk proyek MVC5 saya. Ini milikkuSignInAsync
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);
}
Seperti yang Anda lihat, saya menambahkan AccountNo
ke dalam daftar Klaim.
Sekarang, bagaimana saya dapat memperbarui Klaim ini di beberapa titik dalam aplikasi saya? Sejauh ini, saya punya ini:
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));
}
}
ketika saya mencoba untuk menghapus klaim, saya mendapatkan pengecualian ini:
Klaim ' http://schemas.microsoft.com/ws/2008/06/identity/claims/userdata : 101' tidak dapat dihapus. Ini bukan merupakan bagian dari Identitas ini atau merupakan klaim yang dimiliki oleh Prinsipal yang mengandung Identitas ini. Misalnya, Prinsipal akan memiliki klaim saat membuat GenericPrincipal dengan peran. Peran akan diekspos melalui Identitas yang diteruskan dalam konstruktor, tetapi sebenarnya tidak dimiliki oleh Identitas. Logika serupa ada untuk RolePrincipal.
Bisakah seseorang membantu saya mencari cara untuk memperbarui Klaim?
SignInManager.SignInAsync
untuk menyegarkan nilai Klaim. Lihat pertanyaan iniJawaban:
Saya membuat metode Ekstensi untuk Menambahkan / Memperbarui / Membaca Klaim berdasarkan ClaimsIdentity yang diberikan
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; } } }
dan kemudian menggunakannya
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"); } } }
sumber
Anda dapat membuat yang baru
ClaimsIdentity
dan kemudian melakukan pembaruan klaim dengan itu.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 } ); }
sumber
Pendekatan (async) lainnya, menggunakan Identity's UserManager dan SigninManager untuk mencerminkan perubahan dalam cookie Identity (dan secara opsional menghapus klaim dari tabel 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); } }
sumber
SignInAsync()
setelah menyiapkan Klaim.Menggunakan Identitas Asp.Net terbaru dengan .net core 2.1, saya dapat memperbarui klaim pengguna dengan logika berikut.
Daftarkan a
UserClaimsPrincipalFactory
sehingga setiap kaliSignInManager
pengguna masuk, klaim dibuat.Terapkan kebiasaan
UserClaimsPrincipalFactory<TUser, TRole>
seperti di bawah inipublic 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; } }
Nanti di aplikasi Anda ketika Anda mengubah sesuatu di DB dan ingin mencerminkan ini ke pengguna yang diautentikasi dan masuk, baris berikut mencapai ini:
var user = await _userManager.GetUserAsync(User); await _signInManager.RefreshSignInAsync(user);
Ini memastikan pengguna dapat melihat informasi terkini tanpa perlu login lagi. Saya meletakkan ini tepat sebelum mengembalikan hasil ke pengontrol sehingga ketika operasi selesai, semuanya disegarkan dengan aman.
Alih-alih mengedit klaim yang ada dan membuat ketentuan balapan untuk cookie aman, dll., Anda cukup memasukkan pengguna secara diam-diam dan menyegarkan status :)
sumber
Saya mendapatkan pengecualian itu juga dan membereskan hal-hal seperti ini
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);
sumber
Menyusun beberapa jawaban dari sini menjadi kelas ClaimsManager yang dapat digunakan kembali dengan tambahan saya.
Klaim dipertahankan, cookie pengguna diperbarui, proses masuk disegarkan.
Harap dicatat bahwa ApplicationUser dapat diganti dengan IdentityUser jika Anda tidak menyesuaikan sebelumnya. Juga dalam kasus saya itu perlu memiliki logika yang sedikit berbeda dalam lingkungan Pengembangan, jadi Anda mungkin ingin menghapus ketergantungan IWebHostEnvironment.
using System; using System.Collections.Generic; using System.Linq; using System.Security.Claims; using System.Threading.Tasks; using YourMvcCoreProject.Models; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Hosting; namespace YourMvcCoreProject.Identity { public class ClaimsManager { private readonly UserManager<ApplicationUser> _userManager; private readonly SignInManager<ApplicationUser> _signInManager; private readonly IWebHostEnvironment _env; private readonly ClaimsPrincipalAccessor _currentPrincipalAccessor; public ClaimsManager( ClaimsPrincipalAccessor currentPrincipalAccessor, UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager, IWebHostEnvironment env) { _currentPrincipalAccessor = currentPrincipalAccessor; _userManager = userManager; _signInManager = signInManager; _env = env; } /// <param name="refreshSignin">Sometimes (e.g. when adding multiple claims at once) it is desirable to refresh cookie only once, for the last one </param> public async Task AddUpdateClaim(string claimType, string claimValue, bool refreshSignin = true) { await AddClaim( _currentPrincipalAccessor.ClaimsPrincipal, claimType, claimValue, async user => { await RemoveClaim(_currentPrincipalAccessor.ClaimsPrincipal, user, claimType); }, refreshSignin); } public async Task AddClaim(string claimType, string claimValue, bool refreshSignin = true) { await AddClaim(_currentPrincipalAccessor.ClaimsPrincipal, claimType, claimValue, refreshSignin); } /// <summary> /// At certain stages of user auth there is no user yet in context but there is one to work with in client code (e.g. calling from ClaimsTransformer) /// that's why we have principal as param /// </summary> public async Task AddClaim(ClaimsPrincipal principal, string claimType, string claimValue, bool refreshSignin = true) { await AddClaim( principal, claimType, claimValue, async user => { // allow reassignment in dev if (_env.IsDevelopment()) await RemoveClaim(principal, user, claimType); if (GetClaim(principal, claimType) != null) throw new ClaimCantBeReassignedException(claimType); }, refreshSignin); } public async Task RemoveClaims(IEnumerable<string> claimTypes, bool refreshSignin = true) { await RemoveClaims(_currentPrincipalAccessor.ClaimsPrincipal, claimTypes, refreshSignin); } public async Task RemoveClaims(ClaimsPrincipal principal, IEnumerable<string> claimTypes, bool refreshSignin = true) { AssertAuthenticated(principal); foreach (var claimType in claimTypes) { await RemoveClaim(principal, claimType); } // reflect the change in the Identity cookie if (refreshSignin) await _signInManager.RefreshSignInAsync(await _userManager.GetUserAsync(principal)); } public async Task RemoveClaim(string claimType, bool refreshSignin = true) { await RemoveClaim(_currentPrincipalAccessor.ClaimsPrincipal, claimType, refreshSignin); } public async Task RemoveClaim(ClaimsPrincipal principal, string claimType, bool refreshSignin = true) { AssertAuthenticated(principal); var user = await _userManager.GetUserAsync(principal); await RemoveClaim(principal, user, claimType); // reflect the change in the Identity cookie if (refreshSignin) await _signInManager.RefreshSignInAsync(user); } private async Task AddClaim(ClaimsPrincipal principal, string claimType, string claimValue, Func<ApplicationUser, Task> processExistingClaims, bool refreshSignin) { AssertAuthenticated(principal); var user = await _userManager.GetUserAsync(principal); await processExistingClaims(user); var claim = new Claim(claimType, claimValue); ClaimsIdentity(principal).AddClaim(claim); await _userManager.AddClaimAsync(user, claim); // reflect the change in the Identity cookie if (refreshSignin) await _signInManager.RefreshSignInAsync(user); } /// <summary> /// Due to bugs or as result of debug it can be more than one identity of the same type. /// The method removes all the claims of a given type. /// </summary> private async Task RemoveClaim(ClaimsPrincipal principal, ApplicationUser user, string claimType) { AssertAuthenticated(principal); var identity = ClaimsIdentity(principal); var claims = identity.FindAll(claimType).ToArray(); if (claims.Length > 0) { await _userManager.RemoveClaimsAsync(user, claims); foreach (var c in claims) { identity.RemoveClaim(c); } } } private static Claim GetClaim(ClaimsPrincipal principal, string claimType) { return ClaimsIdentity(principal).FindFirst(claimType); } /// <summary> /// This kind of bugs has to be found during testing phase /// </summary> private static void AssertAuthenticated(ClaimsPrincipal principal) { if (!principal.Identity.IsAuthenticated) throw new InvalidOperationException("User should be authenticated in order to update claims"); } private static ClaimsIdentity ClaimsIdentity(ClaimsPrincipal principal) { return (ClaimsIdentity) principal.Identity; } } public class ClaimCantBeReassignedException : Exception { public ClaimCantBeReassignedException(string claimType) : base($"{claimType} can not be reassigned") { } } public class ClaimsPrincipalAccessor { private readonly IHttpContextAccessor _httpContextAccessor; public ClaimsPrincipalAccessor(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; } public ClaimsPrincipal ClaimsPrincipal => _httpContextAccessor.HttpContext.User; } // to register dependency put this into your Startup.cs and inject ClaimsManager into Controller constructor (or other class) the in same way as you do for other dependencies public class Startup { public IServiceProvider ConfigureServices(IServiceCollection services) { services.AddTransient<ClaimsPrincipalAccessor>(); services.AddTransient<ClaimsManager>(); } }
}
sumber
saat saya menggunakan MVC5, dan menambahkan klaim di sini.
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; }
ketika saya memeriksa hasil klaim di fungsi SignInAsync, saya tetap tidak bisa mendapatkan nilai peran yang digunakan. Tapi...
setelah permintaan ini selesai, saya dapat mengakses Peran dalam tindakan lain (permintaan lain).
var userWithClaims = (ClaimsPrincipal)User; Claim CRole = userWithClaims.Claims.First(c => c.Type == ClaimTypes.Role);
jadi, saya pikir mungkin asynchronous menyebabkan IEnumerable diperbarui di belakang proses tersebut.
sumber
Anda dapat memperbarui klaim untuk pengguna saat ini dengan menerapkan
CookieAuthenticationEvents
kelas dan menggantiValidatePrincipal
. Di sana Anda dapat menghapus klaim lama, menambahkan yang baru, dan kemudian mengganti prinsipal menggunakanCookieValidatePrincipalContext.ReplacePrincipal
. Ini tidak memengaruhi klaim apa pun yang disimpan dalam database. Ini menggunakan ASP.NET Core Identity 2.2.public class MyCookieAuthenticationEvents : CookieAuthenticationEvents { string newAccountNo = "102"; public override Task ValidatePrincipal(CookieValidatePrincipalContext context) { // first remove the old claim var claim = context.Principal.FindFirst(ClaimTypes.UserData); if (claim != null) { ((ClaimsIdentity)context.Principal.Identity).RemoveClaim(claim); } // add the new claim ((ClaimsIdentity)context.Principal.Identity).AddClaim(new Claim(ClaimTypes.UserData, newAccountNo)); // replace the claims context.ReplacePrincipal(context.Principal); context.ShouldRenew = true; return Task.CompletedTask; } }
Anda perlu mendaftarkan kelas acara di
Startup.cs
:public IServiceProvider ConfigureServices(IServiceCollection services) { services.AddScoped<MyCookieAuthenticationEvents>(); services.ConfigureApplicationCookie(o => { o.EventsType = typeof(MyCookieAuthenticationEvents); }); }
Anda dapat memasukkan layanan ke dalam kelas peristiwa untuk mengakses nilai baru
AccountNo
tetapi sesuai peringatan di halaman ini, Anda harus menghindari melakukan sesuatu yang terlalu mahal:sumber
Untuk menghapus detail klaim dari database kita dapat menggunakan kode di bawah ini. Selain itu, kami perlu masuk lagi untuk memperbarui nilai 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");
sumber
Beberapa Cookie, Banyak Klaim
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 } }
sumber
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)); }
sumber
Saya menggunakan aplikasi .net core 2.2 dan menggunakan solusi berikut: di statup.cs saya
public void ConfigureServices(IServiceCollection services) { ... services.AddIdentity<IdentityUser, IdentityRole>(options => { ... }) .AddEntityFrameworkStores<AdminDbContext>() .AddDefaultTokenProviders() .AddSignInManager();
pemakaian
private readonly SignInManager<IdentityUser> _signInManager; public YourController( ..., SignInManager<IdentityUser> signInManager) { ... _signInManager = signInManager; } public async Task<IActionResult> YourMethod() // <-NOTE IT IS ASYNC { var user = _userManager.FindByNameAsync(User.Identity.Name).Result; var claimToUse = ClaimsHelpers.CreateClaim(ClaimTypes.ActiveCompany, JsonConvert.SerializeObject(cc)); var claimToRemove = _userManager.GetClaimsAsync(user).Result .FirstOrDefault(x => x.Type == ClaimTypes.ActiveCompany.ToString()); if (claimToRemove != null) { var result = _userManager.ReplaceClaimAsync(user, claimToRemove, claimToUse).Result; await _signInManager.RefreshSignInAsync(user); //<--- THIS } else ...
sumber
Metode ekstensi bekerja sangat baik untuk saya dengan satu pengecualian bahwa jika pengguna logout di sana, kumpulan klaim lama masih ada, jadi dengan sedikit modifikasi seperti melewatkan usermanager melalui semuanya berfungsi dengan baik dan Anda tidak perlu logout dan login. Saya tidak bisa menjawab secara langsung karena reputasi saya telah dibenci :(
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(); } }
sumber
Ini dia:
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);
diambil dari sini.
sumber