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;

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

ОБНОВИТЬ

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

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

TCode
источник
Можете ли вы подтвердить, что файл 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);
hutchonoid
источник
Спасибо, но все равно у меня это не работает. Вы видите мой обновленный вопрос? И еще один последний вопрос. Нужно ли мне создавать свой собственный класс 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();
    }
}
Quentin
источник
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;
angularsen
источник
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();
conterio
источник
1
var claim = User.Claims.FirstOrDefault(c => c.Type == "claim type here");
Фелипе Девеза
источник
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();
    }
BM
источник