위조 방지 토큰 발행 (MVC 5)


122

위조 방지 토큰에 문제가 있습니다. (잘 작동하는 내 사용자 클래스를 만들었지 만 이제는 / 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;
}

그러나 그렇게하면 다음 오류가 발생합니다.

제공된 ClaimsIdentity에 ' http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress ' 유형의 클레임 이 없습니다.

누군가 전에 이것을 본 적이 있습니까? 그렇다면 해결 방법을 알고 있습니까?

미리 건배,
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

베타 버전을 사용하고 있습니다. 릴리스 버전으로 업그레이드 한 다음 문제가 계속 발생하는지 확인하는 것이 좋습니다.
LostInComputer 2013

답변:


230

(global.cs에서) 설정을 시도하십시오.

AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;

33
이것이 작동하는 이유를 주목하는 것이 중요하다고 생각합니다. 이것은 AntiForgery클래스가 NameIdentifier(에서 찾은 사용자 ID 문자열 GetUserId) 을 사용하도록 지시합니다 . 이것을 배우도록 도와 준 Mike Goodwin의 답변에 감사드립니다!
Matt DeKrey 2014-06-29

"AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Name;"을 시도했습니다. "시퀀스에 일치하는 요소가 두 개 이상 포함되어 있습니다"라는 오류가 발생했습니다. 제 경우에는 여러 클레임 (이름, 역할 및 이메일 주소)이 있습니다. 이것을 어떻게 분류 할 수 있습니까?
Dhanuka777

9
나는 이것을 Global.asax.cs에 설정했다
Mike Taverne 2015 년

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
직접적인 스푼 피드 답변을 제공하는 것 이상으로, 이것은 배경을 알려주고 자기 발견을 가능하게합니다. :) 감사합니다
NitinSingh

2
6. [ValidateAntiForgeryToken]속성을 되 돌리십시오
Scott Fraley

1
이것은 정말 나를 도왔습니다. 내 로컬 호스트에서 실행되는 다른 응용 프로그램에 대한 클레임이 사용 된 내 응용 프로그램에서 사용 된 클레임이 이상하게 들리는 이유가 밝혀졌습니다. 따라서 다른 응용 프로그램에서 로그 아웃하면 클레임이 사라지고 오류도 사라졌습니다. 라이브 테스트 환경에서 이러한 사이트는 더 분리되어 있습니다. 따라서 위에서 언급 한 솔루션이 필요하다고 생각하지만 로컬 개발에만 해당됩니다.
Michel

26

이것을 global.asax.cs에 넣으십시오.

AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimsIdentity.DefaultNameClaimType;

감사. 내가 이해하지 못하는 것은 내가이 변경을해야하는 이유이며, 어제 밤에 내 코드에서 겪었던 몇 가지 다른 문제를 수정했으며 모든 것이 잘 작동했습니다. 아무것도 변경하지 않고 다른 컴퓨터에서 이것을 테스트했으며 몇 분 전까지 모두 작동했습니다.
Artorias2718

14

시크릿 창에서 링크를 열거 나 해당 도메인 (예 : localhost)에서 쿠키를 삭제 해보세요.


이것이 작동하는 이유와 문제의 원인은 무엇입니까?
mok

이는 유효하지 않은 이름 식별자가있는 세션 쿠키가있을 때 서버가 사용자를 로그인 페이지로 리디렉션하지 않고 유효하지 않은 식별자를 사용하고 적절한 이름 식별자를 가져 오기 때문에 작동합니다.
rawel

3

편집 : 현재이 문제에 대해 더 잘 이해하고 있으면 아래의 내 대답을 무시할 수 있습니다.

AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;Global.asax.cs의 Application_Start ()에서 설정하면 문제가 해결 되었습니다. 클레임 http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier세트 가 있지만 원래 질문과 동일한 오류가 발생합니다. 그러나 위와 같이 지적하면 어떻게 든 작동합니다.



MVC4부터 위조 방지 토큰은 User.Identity.Name고유 식별자로 사용되지 않습니다 . 대신 오류 메시지에 제공된 두 가지 클레임을 찾습니다.

업데이트 참고 : 이것은 필요하지 않습니다 . 사용자가 로그인 할 때 다음과 같이 누락 된 클레임을 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
userId를 "/ nameidentifier"로 사용하는 이유를 이해합니다.하지만 userId를 "/ identityprovider"로 입력하는 이유는 무엇입니까?
AaronLS

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

사용을 추가해 주셔서 감사합니다. @NitinSingh 내 프로젝트에서 세 가지 잠재적 인 네임 스페이스 중 어떤 것을 사용할지 몰랐기 때문에 더 많은 가치를 추가한다고 생각합니다.
Keisha W

새 기능을 추가 할 때마다 올바른 참조를 요청합니다. 컴파일이 완료되면 오른쪽 클릭시 리팩터링 메뉴를 통해 사용하지 않는 항목을 제거해야합니다
NitinSingh

0
AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Email;

내 경우에는 ADFS 인증을 사용하고 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.