MVC 5 претендует на доступ к данным пользователя


119

Я разрабатываю веб-приложение MVC 5 с использованием подхода Entity Framework 5 Database First . Я использую 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 и добавляю к нему несколько утверждений, а затем передаю его в OWIN с помощью AuthenticationManager для выполнения входа.

Проблема, с которой я столкнулся, заключается в том, что я не уверен, как получить доступ к утверждениям в остальной части моего приложения, будь то в контроллерах или в представлениях Razor.

Я пробовал подход, указанный в этом руководстве

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

Например, я пробовал это в своем коде контроллера, пытаясь получить доступ к значениям, переданным в утверждения, однако user.Claims равно нулю

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

Возможно, мне что-то здесь не хватает.

ОБНОВИТЬ

Основываясь на ответе Дарина, я добавил его код, но по-прежнему не вижу доступа к претензиям. См. Снимок экрана ниже, на котором показано, что я вижу при наведении указателя мыши на личность.

введите описание изображения здесь


Можете ли вы подтвердить, что файл cookie отправляется обратно браузером? Может быть, ваши настройки безопасности требуют SSL?
lessprivilege

@leastprivilege Спасибо, я сейчас займусь этим. Я нашел этот вопрос в Stackoverflow, stackoverflow.com/questions/20319118/ ... это точно такая же проблема, с которой я
столкнулся

Как инициализируются ваши компоненты OWIN?
Дерек Ван Кайк

У меня недавно была такая проблема; Надеюсь, это решение поможет: stackoverflow.com/questions/34537475/…
Alexandru

Ответы:


172

Попробуй это:

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

Спасибо за вашу помощь. Я использовал ваш предложенный ответ в Action в контроллере, чтобы попытаться получить доступ к значениям Claims, однако я identity.Claims по-прежнему имеет значение NULL (см. Обновленный вопрос со снимком экрана). Есть другие идеи? Я ценю вашу помощь.
tcode

Нет, извините, у меня нет других идей. У меня это всегда срабатывало.
Дарин Димитров

Извините, последний вопрос. Нужно ли мне создавать свой собственный класс ClaimsAuthenticationManager и Application_PostAuthenticateRequest () в Global.asax, как этот dotnetcodr.com/2013/02/25/…, прежде чем мой код, приведенный выше, будет работать? Еще раз спасибо.
tcode

7
Пока вы не авторизуетесь в первый раз, у вас не будет доступа к нему до тех пор, пока вы не введете метод входа в систему, поэтому OP не видит его в это время. Вы должны загрузить вручную в это время, если хотите использовать метод входа в систему.
Adam Tuliper - MSFT

Я работаю с ядром asp.net и ищу способ получить изображение профиля LinkedIn в течение более 2 часов. Я застрял, я устал, я хочу сдаться, пока не увижу твой ответ. Я хочу сказать спасибо миллион раз ... +1
Vayne

36

Вы также можете сделать это:

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

Претензии можно прочитать, как Дарин ответил выше или как я.

Утверждения сохраняются, когда вы вызываете ниже, передавая личность:

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

Спасибо, но все равно у меня это не работает. Вы видите мой обновленный вопрос? И еще один последний вопрос. Нужно ли мне создавать свой собственный класс ClaimsAuthenticationManager и Application_PostAuthenticateRequest () в Global.asax, как этот dotnetcodr.com/2013/02/25/…, прежде чем мой код, приведенный выше, будет работать? В очередной раз благодарим за помощь.
tcode

Привет, я посмотрю, когда вернусь к ПК.
hutchonoid

спасибо, очень признателен. На этапе, когда это начинает меня
раздражать

@tgriffiths Привет, я добавил для вас обновление. Надеюсь предоставить немного больше информации. Удачи. :)
hutchonoid

К сожалению, я не использую встроенный Entity Framework Code First, например UserManager и т. д. Но спасибо за ваш вклад. Приветствия.
tcode

30

Я создаю свой собственный расширенный класс, чтобы увидеть, что мне нужно, поэтому, когда мне нужно в моем контроллере или моем представлении, я только добавляю 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
return claim?.Value;потому что почему бы и нет
Холтер

17

Это альтернатива, если вы не хотите постоянно использовать претензии. Взгляните на это руководство Бена Фостера.

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

12

Чтобы подробнее коснуться ответа Дарина, вы можете перейти к своим конкретным претензиям с помощью метода FindFirst :

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

Или это тоже строка myValue = identity.FindFirstValue ("MyClaimType");
Хуан Карлос Пуэрто

Что произойдет, если FindFirst не найдет никаких утверждений с типом «роль»? нулевое исключение?
Фил

10

Вы тоже можете это сделать.

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

8

Помните, что для запроса IEnumerable вам нужно ссылаться на system.linq.
Это даст вам объект расширения, необходимый для выполнения:

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

7

самая короткая и упрощенная версия ответа @Rosdi Kasim'd:

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

Claimname - это утверждение, которое вы хотите получить, т.е. если вы ищете претензию "StreedAddress", то ответ будет таким:

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

приведя пример «заявленная стоимость», я сэкономил время. Спасибо
Нур Лабабиди

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

Однако лучше добавлять утверждения внутри метода «GenerateUserIdentityAsync», особенно если в Startup.Auth.cs включен параметр «регенератидентификат ».


Это GenerateUserIdentityAsyncбыло отличное предложение, я его полностью упустил. Большое спасибо, Василий.
timmi4sa

2

Согласно классу ControllerBase вы можете получить утверждения для пользователя, выполняющего действие.

введите описание изображения здесь

вот как вы можете сделать это в 1 строку.

var claims = User.Claims.ToList();


0

Я использовал это в своем базовом контроллере. Просто делюсь готовым к использованию.

    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();
    }
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.