偽造防止トークンの問題(MVC 5)


122

偽造防止トークンに問題があります:(私は自分のUserクラスを作成しましたが、正常に機能しましたが、/ Account / Registerページにアクセスするたびにエラーが発生します。エラーは次のとおりです。

タイプ「http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier」または「http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider」のクレームは、提供されたClaimsIdentityには存在しません。クレームベースの認証で偽造防止トークンのサポートを有効にするには、構成されたクレームプロバイダーが、生成するClaimsIdentityインスタンスでこれらのクレームの両方を提供していることを確認してください。構成されたクレームプロバイダーが一意の識別子として別のクレームの種類を使用する場合は、静的プロパティAntiForgeryConfig.UniqueClaimTypeIdentifierを設定することで構成できます。

私はこの記事を見つけました:

http://stack247.wordpress.com/2013/02/22/antiforgerytoken-a-claim-of-type-nameidentifier-or-identityprovider-was-not-present-on-provided-claimsidentity/

Application_Startメソッドを次のように変更しました。

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    BundleConfig.RegisterBundles(BundleTable.Bundles);

    AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Email;
}

しかし、それを行うと、次のエラーが発生します。

タイプ ' http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress 'のクレームは、提供されたClaimsIdentityに存在しませんでした。

誰かこれに出くわしたことはありますか?もしそうなら、それを解決する方法を知っていますか?

事前に乾杯、
r3plica

アップデート1

これが私のカスタムユーザークラスです:

public class Profile : User, IProfile
{
    public Profile()
        : base()
    {
        this.LastLoginDate = DateTime.UtcNow;
        this.DateCreated = DateTime.UtcNow;
    }

    public Profile(string userName)
        : base(userName)
    {
        this.CreatedBy = this.Id;

        this.LastLoginDate = DateTime.UtcNow;
        this.DateCreated = DateTime.UtcNow;

        this.IsApproved = true;
    }

    [NotMapped]
    public HttpPostedFileBase File { get; set; }

    [Required]
    public string CompanyId { get; set; }

    [Required]
    public string CreatedBy { get; set; }
    public string ModifiedBy { get; set; }

    public DateTime DateCreated { get; set; }
    public DateTime? DateModified { get; set; }
    public DateTime LastLoginDate { get; set; }

    [Required(ErrorMessageResourceType = typeof(Resources.Resources), ErrorMessageResourceName = "RequiredTitle")]
    public string Title { get; set; }
    [Required(ErrorMessageResourceType = typeof(Resources.Resources), ErrorMessageResourceName = "RequiredFirstName")]
    public string Forename { get; set; }
    [Required(ErrorMessageResourceType = typeof(Resources.Resources), ErrorMessageResourceName = "RequiredLastName")]
    public string Surname { get; set; }

    [Required(ErrorMessageResourceType = typeof(Resources.Resources), ErrorMessageResourceName = "RequiredEmail")]
    public string Email { get; set; }
    public string JobTitle { get; set; }
    public string Telephone { get; set; }
    public string Mobile { get; set; }
    public string Photo { get; set; }
    public string LinkedIn { get; set; }
    public string Twitter { get; set; }
    public string Facebook { get; set; }
    public string Google { get; set; }
    public string Bio { get; set; }

    public string CompanyName { get; set; }

    [Required(ErrorMessageResourceType = typeof(Resources.Resources), ErrorMessageResourceName = "RequiredCredentialId")]
    public string CredentialId { get; set; }
    [Required(ErrorMessageResourceType = typeof(Resources.Resources), ErrorMessageResourceName = "RequiredSecurityCode")]
    public bool IsLockedOut { get; set; }
    public bool IsApproved { get; set; }

    [Display(Name = "Can only edit own assets")]
    public bool CanEditOwn { get; set; }
    [Display(Name = "Can edit assets")]
    public bool CanEdit { get; set; }
    [Display(Name = "Can download assets")]
    public bool CanDownload { get; set; }
    [Display(Name = "Require approval to upload assets")]
    public bool RequiresApproval { get; set; }
    [Display(Name = "Can approve assets")]
    public bool CanApprove { get; set; }
    [Display(Name = "Can synchronise assets")]
    public bool CanSync { get; set; }

    public bool AgreedTerms { get; set; }
    public bool Deleted { get; set; }
}

public class ProfileContext : IdentityStoreContext
{
    public ProfileContext(DbContext db)
        : base(db)
    {
        this.Users = new UserStore<Profile>(this.DbContext);
    }
}

public class ProfileDbContext : IdentityDbContext<Profile, UserClaim, UserSecret, UserLogin, Role, UserRole>
{
}

私のプロファイルは私のリポジトリにとって単純で、次のようになります:

public interface IProfile
{
    string Id { get; set; }
    string CompanyId { get; set; }

    string UserName { get; set; }
    string Email { get; set; }

    string CredentialId { get; set; }
}

そしてユーザークラスがあるMicrosoft.AspNet.Identity.EntityFramework.Userのクラス。私のAccountControllerは次のようになります。

[Authorize]
public class AccountController : Controller
{
    public IdentityStoreManager IdentityStore { get; private set; }
    public IdentityAuthenticationManager AuthenticationManager { get; private set; }

    public AccountController() 
    {
        this.IdentityStore = new IdentityStoreManager(new ProfileContext(new ProfileDbContext()));
        this.AuthenticationManager = new IdentityAuthenticationManager(this.IdentityStore);
    }

    //
    // GET: /Account/Register
    [AllowAnonymous]
    public ActionResult Register()
    {
        return View();
    }

    //
    // POST: /Account/Register
    [HttpPost]
    [AllowAnonymous]
    public async Task<ActionResult> Register(RegisterViewModel model)
    {
        if (ModelState.IsValid)
        {
            try
            {
                // Create a profile, password, and link the local login before signing in the user
                var companyId = Guid.NewGuid().ToString();
                var user = new Profile(model.UserName)
                {
                    CompanyId = companyId,
                    Title = model.Title,
                    Forename = model.Forename,
                    Surname = model.Surname,
                    Email = model.Email,
                    CompanyName = model.CompanyName,
                    CredentialId = model.CredentialId
                };

                if (await IdentityStore.CreateLocalUser(user, model.Password))
                {
                    //Create our company
                    var company = new Skipstone.Web.Models.Company()
                    {
                        Id = companyId,
                        CreatedBy = user.Id,
                        ModifiedBy = user.Id,
                        Name = model.CompanyName
                    };

                    using (var service = new CompanyService())
                    {
                        service.Save(company);
                    }

                    await AuthenticationManager.SignIn(HttpContext, user.Id, isPersistent: false);
                    return RedirectToAction("Setup", new { id = companyId });
                }
                else
                {
                    ModelState.AddModelError("", "Failed to register user name: " + model.UserName);
                }
            }
            catch (IdentityException e)
            {
                ModelState.AddModelError("", e.Message);
            }
        }

        // If we got this far, something failed, redisplay form
        return View(model);
    }

    //
    // POST: /Account/Setup
    public ActionResult Setup(string id)
    {
        var userId = User.Identity.GetUserId();
        using (var service = new CompanyService())
        {
            var company = service.Get(id);
            var profile = new Profile()
            {
                Id = userId,
                CompanyId = id
            };

            service.Setup(profile);

            return View(company);
        }
    }
}

以前は[ValidateAntiForgeryToken]属性で装飾されていましたが、そこで機能しなくなりました。

私はそれで十分だと思います:)


カスタムUserクラスとその使用方法を教えてください。
LostInComputer 2013年

カスタムUserクラスと、それをどのように使用しているかを追加しました。
r3plica 2013年

ベータ版を使用しています。リリースバージョンにアップグレードしてから、問題が引き続き発生するかどうかを確認することをお勧めします。
LostInComputer 2013年

回答:


230

(global.csで)設定してみてください:

AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;

33
これが機能する理由に注意することが重要だと思います。これにより、AntiForgeryクラスにを使用するよう指示しますNameIdentifier(これは、によって検出されたユーザーID文字列GetUserIdです)。これを学ぶのを助けてくれたマイク・グッドウィンの答えに感謝します!
Matt DeKrey 2014年

「AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Name;」を試してみました そして、「シーケンスに複数の一致する要素が含まれています」というエラーが発生しました。私の場合、いくつかのクレーム(名前、役割、メールアドレス)があります。どうすればこれを整理できますか?
Dhanuka777

9
私はこれをGlobal.asax.csで設定しました
Mike Taverne

6
これは、OpenId(Azure ActiveDirectoryなど)を認証として使用している場合のソリューションでもあります。
guysherman

6
完全な名前空間.. ClaimTypesが保持されている場所を見つけるために、少し掘り下げる必要がありました。System.Web.Helpers.AntiForgeryConfig.UniqueClaimTypeIdentifier = System.Security.Claims.ClaimTypes.NameIdentifier;
Mark Rowe

65

ClaimsIdentityで取得するクレームを知っていますか?そうでない場合:

  1. [ValidateAntiForgeryToken]属性を削除します
  2. コントローラーのどこかにブレークポイントを置き、そこでブレークします
  3. 次に、現在ClaimsIdentityを見て、クレームを調べます
  4. ユーザーを一意に識別すると思われるものを見つける
  5. AntiForgeryConfig.UniqueClaimTypeIdentifierをその要求タイプに設定します
  6. [ValidateAntiForgeryToken]属性を戻す

3
直接スプーンフィードの回答を提供するだけでなく、これは背景を伝え、自己発見を可能にします。:)
どうも

2
6. [ValidateAntiForgeryToken]属性を元に戻す
Scott Fraley

1
これは本当に私を助けました。使用されているクレームがないアプリケーションで、ローカルホスト上で実行されている別のアプリケーションからクレームが発生したことがわかりました(そのため、クレームが奇妙に聞こえました)。そのため、他のアプリケーションからログアウトすると、クレームがなくなり、エラーも発生しました。ライブテスト環境では、これらのサイトはより分離されています。だから私は上記の解決策が必要だと思いますが、それは地域開発のためだけです。
ミシェル

26

これをglobal.asax.csに入れてください

AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimsIdentity.DefaultNameClaimType;

ありがとう。私が得られないのは、なぜこの変更を行わなければならなかったのかということです。昨夜、コードで発生していたいくつかの異なる問題を修正しましたが、すべてうまくいきました。何も変更せずに、別のマシンでテストしましたが、数分前まではすべてうまくいきました。
Artorias2718

14

シークレットウィンドウでリンクを開くか、そのドメイン(localhost)からCookieをクリアしてください。


なぜこれが機能し、問題の原因は何ですか?
モク

これが機能するのは、無効なnameidentifierを持つセッションCookieがある場合、サーバーはユーザーをログインページにリダイレクトせずに無効な識別子を使用し、適切なnameidentifierを取得しようとするためです。
rawel 2017年

3

編集:現時点でこの問題をより深く理解しているので、以下の私の答えは無視できます。

AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;Global.asax.csのApplication_Start()の設定で修正されました。申し立てをhttp://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier設定しましたが、元の質問と同じエラーが発生します。しかし、上記のように指摘すると、どういうわけか機能します。



MVC4以降、偽造防止トークンはUser.Identity.Name一意の識別子として使用されません。代わりに、エラーメッセージで指定された2つのクレームを探します。

更新注:これは必要ありません。 ユーザーがログインしているときに、次のように不足しているクレームをClaimsIdentityに追加できます。

string userId = TODO;
var identity = System.Web.HttpContext.Current.User.Identity as ClaimsIdentity;
identity.AddClaim(new Claim("http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider", userId));
identity.AddClaim(new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier", userId));

クレームの1つが以前から存在している可能性があることに注意してください。両方を追加すると、クレームが重複するとエラーになります。その場合は、足りないものを追加してください。


1
userIdを「/ nameidentifier」として使用している理由を理解していますが、userIdを「/ identityprovider」として使用しているのはなぜですか。
AaronLS 2016

2

Global.asax.csでは、

1.これらの名前空間を追加する

using System.Web.Helpers;
using System.Security.Claims;

2.メソッドApplication_Startに次の行を追加します。

 protected void Application_Start()
 {
       .......
       AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimsIdentity.DefaultNameClaimType;
 } 

上記の回答よりも価値を高める方法
NitinSingh 2018

使い方を追加していただきありがとうございます。@NitinSinghプロジェクトで3つの潜在的な名前空間のどれを使用するかわからなかったので、これはさらに価値を追加すると思います。
ケイシャW

新しい機能を追加すると、正しい参照が求められます。コンパイルしたら、右クリックのリファクタリングメニューから未使用のものを削除する必要があります
NitinSingh

0
AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Email;

私の場合はADFS認証を使用しています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.