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を作成するとき、プリンシパルはクレームを所有します。ロールは、コンストラクターで渡されるIdentityを介して公開されますが、実際にはIdentityによって所有されることはありません。 RolePrincipalにも同様のロジックが存在します。
クレームを更新する方法を見つけるのを手伝ってくれますか?
新しい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 }
);
}
特定のClaimsIdentityに基づいてクレームを追加/更新/読み取りする拡張メソッドを作成しました
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");
}
}
}
IdentityのUserManagerとSigninManagerを使用してID 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);
}
}
私もその例外を取得し、このようなものをクリアしました
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);
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関数で申し立ての結果を確認すると、とにかくロール値の使用を取得できません。しかし...
このリクエストが終了した後、他のアクション(アンサーリクエスト)でロールにアクセスできます。
var userWithClaims = (ClaimsPrincipal)User;
Claim CRole = userWithClaims.Claims.First(c => c.Type == ClaimTypes.Role);
そのため、おそらく非同期でプロセスの背後でIEnumerableが更新されると思います。
最新のAsp.Net Identityと.netコア2.1を使用して、次のロジックでユーザークレームを更新できます。
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;
}
}
アプリケーションの後半でDBで何かを変更し、これを認証およびサインインしたユーザーに反映させたい場合、次の行でこれを実現します。
var user = await _userManager.GetUserAsync(User);
await _signInManager.RefreshSignInAsync(user);
これにより、ユーザーは再度ログインしなくても最新の情報を確認できます。コントローラーに結果を返す直前にこれを配置して、操作が終了したときにすべてが安全に更新されるようにしました。
既存のクレームを編集して安全なCookieなどの競合状態を作成する代わりに、ユーザーをサイレントでサインインし、状態を更新するだけです:)
複数の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
}
}
データベースからクレームの詳細を削除するには、以下のコードを使用できます。また、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");
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));
}