MVC 5 访问声明身份用户数据

133

我正在使用 Entity Framework 5 Database First 方法来开发一个基于MVC 5的Web应用程序。 我正在使用OWIN来对用户进行身份验证。 下面显示了我在我的账户控制器中的登录方法。

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

正如您所看到的,我正在创建一个ClaimsIdentity并向其中添加多个声明,然后使用AuthenticationManager将其传递给OWIN以执行登录。

我的问题是,我不确定如何在我的应用程序的其他部分(控制器或Razor视图中)访问这些声明。

我尝试了本教程中列出的方法:

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

例如,在我的控制器代码中,我尝试获取传递到声明中的值,但是user.Claims等于null。

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

也许我在这里漏掉了什么。

更新

根据Darin的回答,我添加了他的代码,但我仍然没有看到访问Claims的权限。请参见下面的屏幕截图,显示当我悬停在identity.Claims上时看到的内容。

输入图像描述


你能确认浏览器是否发送了 cookie 吗?也许你的安全设置要求 SSL? - leastprivilege
@leastprivilege 谢谢,我现在会去看一下。我在Stackoverflow上找到了这个问题,https://dev59.com/ynnZa4cB1Zd3GeqPuMNu?rq=1 它和我遇到的问题完全相同,但不幸的是没有答案 :( - tcode
你的OWIN组件是如何初始化的? - Derek Van Cuyk
我最近遇到了这样的问题;希望这个解决方案能有所帮助:http://stackoverflow.com/questions/34537475/owin-identity-roles-work-locally-but-seem-to-disappear-when-i-publish-run-the-s/34548579#34548579 - Alexandru
12个回答

190

试试这个:

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

谢谢你的帮助。我在控制器中的一个操作中使用了你建议的答案来尝试访问Claims值,但是identity.Claims仍然为空(请参见更新的问题和截图)。还有其他想法吗?感谢你的帮助。 - tcode
不好意思,我没有其他的想法。这个方法一直对我有效。 - Darin Dimitrov
抱歉,最后一个问题。在我的代码能够工作之前,我是否需要创建自己的自定义ClaimsAuthenticationManager类和Global.asax中的Application_PostAuthenticateRequest(),就像这个链接中所示:http://dotnetcodr.com/2013/02/25/claims-based-authentication-in-mvc4-with-net4-5-c-part-1-claims-transformation/?再次感谢。 - tcode
8
在您首次授权之前,您将无法访问此内容,这就是为什么 OP 此时看不到它的登录方法。如果您想在登录方法中使用它,则必须手动加载它。 - Adam Tuliper

40

你也可以这样做:

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

更新

根据评论提供进一步说明。

如果您在系统内创建用户,如下所示:

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

您的身份应自动填充一些与之相关的声明。

要在用户验证后添加定制声明,可以按照以下步骤执行:

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

这些声明可以按照Darin在上面的回答或者我所说的那样被读取。

当你传入身份标识时,调用下面的方法时,这些声明将被持久化:

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

谢谢,但这对我仍然没有用。你能看到我的更新问题吗?还有一个最后的问题。在Global.asax中,我需要像这样创建自己的自定义ClaimsAuthenticationManager类和Application_PostAuthenticateRequest()吗,就像这个链接中所示:http://dotnetcodr.com/2013/02/25/claims-based-authentication-in-mvc4-with-net4-5-c-part-1-claims-transformation/,在我的上面的代码之前才能工作?再次感谢您的帮助。 - tcode
@tgriffiths 你好,我为你添加了更新。希望能提供更多信息。祝你好运。 :) - hutchonoid
很不幸我没有使用内置的Entity Framework Code First,比如UserManager等。但还是感谢您的建议。祝好。 - tcode
哦,好的。好吧,你可能可以像我在MVC 4中所做的那样来尝试它,但我没有在mvc5中尝试过。这是一篇非常好的文章,我用过...http://dotnetcodr.com/2013/02/25/claims-based-authentication-in-mvc4-with-net4-5-c-part-1-claims-transformation/ - hutchonoid
有没有想法在使用EF种子方法创建用户时添加声明? - TWilly

33

我创建了自己的扩展类以满足我的需求,因此当我需要在我的控制器或视图中使用时,只需要将其命名空间添加到 using 中,就像这样:

我创建了自己的扩展类来满足我的需求,所以当我需要在我的控制器或视图中使用时,只需将其命名空间添加到 using 中,就像这样:

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 ....
    {
      .....
    }
}

在我的控制器中:

using XXX.CodeHelpers.Extended;

var claimAddress = User.GetAddress();

在我的剃须刀里:

@using DinexWebSeller.CodeHelpers.Extended;

@User.GetFullName()

8
因为没有理由,所以返回 claim?.Value - Halter

19

如果您不想一直使用claims,那么这是一个替代方案。看看Ben Foster的教程

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

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

}

然后您可以添加基本控制器。

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

在您的控制器中,您需要执行:

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

14

进一步谈到Darin的回答,您可以使用FindFirst方法找到您特定的声明:

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

字符串 myValue = identity.FindFirstValue("MyClaimType"); - Juan Carlos Puerto
如果FindFirst没有找到任何“role”类型的索赔,会发生什么?会出现空异常吗? - Phil
var username = identity.FindFirst(ClaimTypes.NameIdentifier).Value; 变量用户名等于身份查找第一个声明类型的标识符。 - FreeClimb

10

你也可以这样做。

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

9

@Rosdi Kasim的答案的最短和简化版本是

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

Claimname是您想要检索的声明,例如,如果您正在寻找“StreedAddress”声明,则上面的答案将如下所示。

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

8

请记住,为了查询IEnumerable,您需要引用system.linq。
它将提供所需的扩展对象来执行以下操作:

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

6
Request.GetOwinContext().Authentication.User.Claims

然而,最好将声明添加到“GenerateUserIdentityAsync”方法内部,特别是如果在Startup.Auth.cs中启用了regenerateIdentity。


“GenerateUserIdentityAsync” 是个很棒的建议,我完全忽略了。非常感谢 Basil。 - timmi4sa

2
根据ControllerBase类,您可以获取执行操作的用户的声明。

enter image description here

这是一行代码实现的方法。
var claims = User.Claims.ToList();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接