简体   繁体   中英

ASP.NET MVC AspNetUserClaims How can I read, write and update data in the table?

This is my first question in StackOverflow :) so please don't judge me strong :) Ok. Here we go.

I'm new in ASP.NET MVC Framework, and trying to implement multi-language in my site. So I had added the drop-down language list to the _LoginPartial.cshtml:

<li class="dropdown">
    <a href="#" class="dropdown-toggle" data-toggle="dropdown">@(MEDONET.Resources.IndexTexts.Language)<b class="caret"></b></a>
    <ul class="dropdown-menu">
        <li>@Html.ActionLink("Кыргызча", "Change", "Language", new { lang = "ky" }, null)</li>
        <li>@Html.ActionLink("Русский", "Change", "Language", new { lang = "ru" }, null)</li>
        <li>@Html.ActionLink("English", "Change", "Language", new { lang = "en" }, null)</li>
        <li>@Html.ActionLink("O'zbekcha", "Change", "Language", new { lang = "uz" }, null)</li>
    </ul>
</li>

As you can see, I am passing selected Language abbreviation to the Change method of the Language controller. In the LanguageController I have the code looking like this:

public ActionResult Change(string lang)
    {
        if (lang != null)
        {
            if (User.Identity.IsAuthenticated)
            {
                //var user =  _userManager.FindByName(User.Identity.Name);
                //_userManager.AddClaim(user.Id, new Claim("Language", lang));
                //var claims = _userManager.GetClaims(user.Id);

                ////User.RemoveClaimIfExists("Language");
                ////var claims = new List<Claim>();
                ApplicationDbContext mycontext = new ApplicationDbContext();
                UserStore<ApplicationUser> mystore = new UserStore<ApplicationUser>(mycontext);
                ApplicationUserManager UserMan = new ApplicationUserManager(mystore);
                //ApplicationUser _user = UserMan.FindById(User.Identity.GetUserId());
                UserMan.AddClaim(User.Identity.GetUserId(), new Claim("Language", lang));

                //UserMan.RemoveClaim(User.Identity.GetUserId(), User.GetClaim("Language"));
                //User.AddClaim(lang);

            }
            Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(lang);
            Thread.CurrentThread.CurrentUICulture = new CultureInfo(lang);

            HttpCookie cookie = new HttpCookie("Language");
            cookie.Value = lang;
            Response.Cookies.Add(cookie);

            return Redirect(Request.UrlReferrer.ToString());
        }
        return Redirect(Request.UrlReferrer.ToString());
    }

As you could notice, I was trying A LOT of different ways to implement the feature. And after day of the torments it became working. But I am not sure my Frankenshtain is the best way to write a Claim to the AspNetUserClaims table. So this is my first question:

1) How can I improve my writing claims code?

The second question is much near to the first:

2) How can I update existing user claim?

And finally the last third obvious for my knowledge level question is:

3) How to read the stored claims?

Once set claim needs to be read in the next sessions. So therefor I have created the Claims class, added there this

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;
    }

code and call it from Login method of AccountController:

 [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
    {
        if (!ModelState.IsValid)
        {
            return View(model);
        }

        // This doesn't count login failures towards account lockout
        // To enable password failures to trigger account lockout, change to shouldLockout: true
        var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
        switch (result)
        {
            case SignInStatus.Success:
                HttpCookie cookie = new HttpCookie("Language");
                string lang = GetClaimValue("Language");
                if (lang == null) // language was not selected befor
                    lang = "ru"; 
                Response.Cookies.Add(cookie);

                Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(cookie.Value);
                Thread.CurrentThread.CurrentUICulture = new CultureInfo(cookie.Value);
                return RedirectToLocal(returnUrl);
            case SignInStatus.LockedOut:
                return View("Lockout");
            case SignInStatus.RequiresVerification:
                return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
            case SignInStatus.Failure:
            default:
                ModelState.AddModelError("", "Invalid login attempt.");
                return View(model);
        }
    }

Bu as you could guess I always get Russian language instead of Kyrgyz which abbr "ky" stored in the AspNetUserClaims table.

That's it :) I hope it wasn't too much table talks in here. Please help me!

I'm going to skip your first question, as it's hard for me to answer without seing all the contexts in which you use your claims-code. However, for you other questions:

2) How can I update existing user claim?

You can use an approach like this:

    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 });
    }

Now you can use it from any controller like this:

User.AddUpdateClaim("Language", "ru");

3) How to read the stored claims?

Something like this:

    public static string GetClaimValue(this IPrincipal principal, string type)
    {
        var user = principal as ClaimsPrincipal;
        var claim = user.Claims.FirstOrDefault(x => x.Type == type);

        if (claim != null) return claim.Value;

        throw new Exception($"Claim with type {type} not set");
    }

Both methods are extension-metods, usable on the Controller.User object.

EDIT: As a response to your comment, this is how you could create the claims identity to begin with:

 var ident = new ClaimsIdentity(
                new[] { 
                    new Claim("http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider", "ASP.NET Identity", "http://www.w3.org/2001/XMLSchema#string"),               
                },
                DefaultAuthenticationTypes.ApplicationCookie);

//Add default language to claims
ident.AddClaim(new Claim("Language", "ru"));

HttpContext.GetOwinContext().Authentication.SignIn(ident);

For updating claims

// GET: Language
    public ActionResult Change(string lang)
    {
        if(lang != null)
        {

            if(User.Identity.IsAuthenticated)
            {
                var claims = new List<Claim>();

                ApplicationDbContext mycontext = new ApplicationDbContext();
                UserStore<ApplicationUser> mystore = new UserStore<ApplicationUser>(mycontext);
                ApplicationUserManager UserMan = new ApplicationUserManager(mystore);
                ApplicationUser _user = UserMan.FindById(User.Identity.GetUserId());

                var claim = _user.Claims.FirstOrDefault(c => c.ClaimType == "Language");

                if (claim != null) // User have Language claim the table 
                {
                    if (claim.ClaimValue != lang) // and chosen language doesn't match it
                    {
                        UserMan.RemoveClaim(User.Identity.GetUserId(), new Claim("Language", claim.ClaimValue));
                        UserMan.AddClaimAsync(User.Identity.GetUserId(), new Claim("Language", lang)); 
                    }
                }
                else if(claim == null)
                {
                    UserMan.AddClaimAsync(User.Identity.GetUserId(), new Claim("Language", lang));
                }

            }
            Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(lang);
            Thread.CurrentThread.CurrentUICulture = new CultureInfo(lang);

            HttpCookie cookie = new HttpCookie("Language");
            cookie.Value = lang;
            Response.Cookies.Add(cookie);


            return Redirect(Request.UrlReferrer.ToString());
        }
        return Redirect(Request.UrlReferrer.ToString());
    }

In this Change action I'm actually reading the claim, but if want to read it while login in, I do something like that:

 [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
    {
        if (!ModelState.IsValid)
        {
            return View(model);
        }

        // This doesn't count login failures towards account lockout
        // To enable password failures to trigger account lockout, change to shouldLockout: true
        var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
        switch (result)
        {
            case SignInStatus.Success:
                HttpCookie cookie = new HttpCookie("Language");
                cookie.Value = "ru";
                var user = UserManager.FindByEmail(model.Email);

                    var claim = user.Claims.FirstOrDefault(c => c.ClaimType == "Language");

                    if (claim == null)
                    {
                        cookie.Value = Thread.CurrentThread.CurrentCulture.Name.ToString(); // interface language used befor logining in
                    }
                    else
                        cookie.Value = claim.ClaimValue; // Language from the UserClaim 

                Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(cookie.Value);
                Thread.CurrentThread.CurrentUICulture = new CultureInfo(cookie.Value);
                Response.Cookies.Add(cookie);
                return RedirectToLocal(returnUrl);
            case SignInStatus.LockedOut:
                return View("Lockout");
            case SignInStatus.RequiresVerification:
                return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
            case SignInStatus.Failure:
            default:
                ModelState.AddModelError("", "Invalid login attempt.");
                return View(model);
        }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM