ChatGPT解决这个技术问题 Extra ChatGPT

MVC 5 Access Claims Identity User Data

I am developing an MVC 5 web application using Entity Framework 5 Database First approach. I am using OWIN for the authentication of Users. Below shows my Login method within my Account Controller.

public ActionResult Login(LoginViewModel model, string returnUrl)
{
    if (ModelState.IsValid)
    {
        var user = _AccountService.VerifyPassword(model.UserName, model.Password, false);
        if (user != null)
        {
            var identity = new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, model.UserName), }, DefaultAuthenticationTypes.ApplicationCookie, ClaimTypes.Name, ClaimTypes.Role);

            identity.AddClaim(new Claim(ClaimTypes.Role, "guest"));
            identity.AddClaim(new Claim(ClaimTypes.GivenName, "A Person"));
            identity.AddClaim(new Claim(ClaimTypes.Sid, user.userID)); //OK to store userID here?

            AuthenticationManager.SignIn(new AuthenticationProperties
            {
                IsPersistent = model.RememberMe
            }, identity);

            return RedirectToAction("Index", "MyDashboard");
        }
        else
        {
            ModelState.AddModelError("", "Invalid username or password.");
        }
    }
    // If we got this far, something failed, redisplay form
    return View(model);
}

As you can see I'm creating a ClaimsIdentity and adding several claims to it, then passing it to OWIN using the AuthenticationManager to perform the sign in.

The problem I am having is that I'm not sure how to access the claims in the rest of my application, either in Controllers or in Razor Views.

I had tried the approach listed in this tutorial

http://brockallen.com/2013/10/24/a-primer-on-owin-cookie-authentication-middleware-for-the-asp-net-developer/

For example, I tried this in my Controller code in an attempt to get access to the values passed into the Claims, however, the user.Claims is equal to null

var ctx = HttpContext.GetOwinContext();
ClaimsPrincipal user = ctx.Authentication.User;
IEnumerable<Claim> claims = user.Claims;

Perhaps I am missing something here.

UPDATE

Based on Darin's answer, I added his code but still I fail to see access to the Claims. Please see screenshot below showing what I see when hovered over identity.Claims.

https://i.stack.imgur.com/Uk6RQ.jpg

Can you confirm that the cookie is sent back by the browser? Maybe your security settings demand SSL?
@leastprivilege Thanks, I'll look into that now. I found this question on Stackoverflow, stackoverflow.com/questions/20319118/… it is the exact same problem I am having, but unfortunately no answer to it :(
How are your OWIN components initialized?
I recently had a problem like this; I hope this solution helps: stackoverflow.com/questions/34537475/…

D
Darin Dimitrov

Try this:

[Authorize]
public ActionResult SomeAction()
{
    var identity = (ClaimsIdentity)User.Identity;
    IEnumerable<Claim> claims = identity.Claims;
    ...
}

Thanks for your help. I used your suggested answer in an Action within a Controller to try and access the Claims values, however, I identity.Claims is still NULL (see updated question with screenshot). Any other ideas? I appreciate your help.
No, sorry I don't have other ideas. This has always worked for me.
Sorry, one last question. Do I need to create my own custom ClaimsAuthenticationManager class and Application_PostAuthenticateRequest() in Global.asax like this dotnetcodr.com/2013/02/25/… before my code above will work? Thanks again.
Until you Authorize for the first time, you wont have access to this until after your login method which is why the OP doesn't see it at that time. You have to load manually at this time if you want it in the Login method.
J
Jay Stevens

You can also do this:

//Get the current claims principal
var identity = (ClaimsPrincipal)Thread.CurrentPrincipal;
var claims = identity.Claims;

Update

To provide further explanation as per comments.

If you are creating users within your system as follows:

UserManager<applicationuser> userManager = new UserManager<applicationuser>(new UserStore<applicationuser>(new SecurityContext()));
ClaimsIdentity identity = userManager.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie);

You should automatically have some Claims populated relating to you Identity.

To add customized claims after a user authenticates you can do this as follows:

var user = userManager.Find(userName, password);
identity.AddClaim(new Claim(ClaimTypes.Email, user.Email));

The claims can be read back out as Darin has answered above or as I have.

The claims are persisted when you call below passing the identity in:

AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = persistCookie }, identity);

Thanks, but still this doesn't work for me. Can you see my updated question? Also, one last question. Do I need to create my own custom ClaimsAuthenticationManager class and Application_PostAuthenticateRequest() in Global.asax like this dotnetcodr.com/2013/02/25/… before my code above will work? Thanks again for your help.
@tgriffiths Hi, I've added an update for you. Hopefully providing a bit more info. Good luck. :)
unfortunately I'm not using the built in Entity Framework Code First, e.g., UserManager etc. But thanks for your input. Cheers.
Oh, right. Ok, you might be able to do it similar to how I've done it in MVC 4 then but I've not tried it in mvc5. This was a very good article that I used...dotnetcodr.com/2013/02/25/…
any idea on how to add a claim when creating users via EF seed methods?
R
Rosdi Kasim

I make my own extended class to see what I need, so when I need into my controller or my View, I only add the using to my namespace something like this:

public static class UserExtended
{
    public static string GetFullName(this IPrincipal user)
    {
        var claim = ((ClaimsIdentity)user.Identity).FindFirst(ClaimTypes.Name);
        return claim == null ? null : claim.Value;
    }
    public static string GetAddress(this IPrincipal user)
    {
        var claim = ((ClaimsIdentity)user.Identity).FindFirst(ClaimTypes.StreetAddress);
        return claim == null ? null : claim.Value;
    }
    public ....
    {
      .....
    }
}

In my controller:

using XXX.CodeHelpers.Extended;

var claimAddress = User.GetAddress();

In my razor:

@using DinexWebSeller.CodeHelpers.Extended;

@User.GetFullName()

return claim?.Value; because why not
Q
Quentin

This is an alternative if you don't want to use claims all the time. Take a look at this tutorial by Ben Foster.

public class AppUser : ClaimsPrincipal
{
    public AppUser(ClaimsPrincipal principal)
        : base(principal)
    {
    }

    public string Name
    {
        get
        {
            return this.FindFirst(ClaimTypes.Name).Value;
        } 
    }

}

Then you can add a base controller.

public abstract class AppController : Controller
{       
    public AppUser CurrentUser
    {
        get
        {
            return new AppUser(this.User as ClaimsPrincipal);
        }
    }
}

In you controller, you would do:

public class HomeController : AppController
{
    public ActionResult Index()
    {
        ViewBag.Name = CurrentUser.Name;
        return View();
    }
}

P
Patrick

To further touch on Darin's answer, you can get to your specific claims by using the FindFirst method:

var identity = (ClaimsIdentity)User.Identity;
var role = identity.FindFirst(ClaimTypes.Role).Value;

Or this one too string myValue = identity.FindFirstValue("MyClaimType");
What happens if FindFirst does not find any claims with the "role" type? null exception?
var username = identity.FindFirst(ClaimTypes.NameIdentifier).Value;
P
Patrick

You can also do this.

IEnumerable<Claim> claims = ClaimsPrincipal.Current.Claims;

b
bummi

Remember that in order to query the IEnumerable you need to reference system.linq. It will give you the extension object needed to do:

CaimsList.FirstOrDefault(x=>x.Type =="variableName").toString();

G
Gwen

shortest and simplified version of @Rosdi Kasim'd answer is

string claimvalue = ((System.Security.Claims.ClaimsIdentity)User.Identity).
    FindFirst("claimname").Value;

Claimname is the claim you want to retrieve i.e if you are looking for "StreedAddress" claim then the above answer will be like this

string claimvalue = ((System.Security.Claims.ClaimsIdentity)User.Identity).
    FindFirst("StreedAddress").Value;

B
Bassel Banbouk
Request.GetOwinContext().Authentication.User.Claims

However it is better to add the claims inside the "GenerateUserIdentityAsync" method, especially if regenerateIdentity in the Startup.Auth.cs is enabled.


The GenerateUserIdentityAsync was an awesome suggestion, I have totally overlooked it. Thanks a lot Basil.
c
conterio

According to the ControllerBase class, you can get the claims for the user executing the action.

https://i.stack.imgur.com/NNXKa.png

here's how you can do it in 1 line.

var claims = User.Claims.ToList();

F
Felipe Deveza
var claim = User.Claims.FirstOrDefault(c => c.Type == "claim type here");

B
B M

I used it like that in my base controller. Just sharing for ready to use.

    public string GetCurrentUserEmail() {
        var identity = (ClaimsIdentity)User.Identity;
        IEnumerable<Claim> claims = identity.Claims;
        var email = claims.Where(c => c.Type == ClaimTypes.Email).ToList();
        return email[0].Value.ToString();
    }

    public string GetCurrentUserRole()
    {
        var identity = (ClaimsIdentity)User.Identity;
        IEnumerable<Claim> claims = identity.Claims;
        var role = claims.Where(c => c.Type == ClaimTypes.Role).ToList();
        return role[0].Value.ToString();
    }

关注公众号,不定期副业成功案例分享
Follow WeChat

Success story sharing

Want to stay one step ahead of the latest teleworks?

Subscribe Now