Jak mogę wysłać informacje o autoryzacji z powrotem do mojej aplikacji klienta, gdy używam AngularJS, WebAPI 2 i OAuth 2?

Mam AngularJS aplikację kliencką, która używa javascript (nie coffeescript lub typescript) Oauth2 do uwierzytelniania z WebAPI 2 aplikacji przy użyciu najnowszego Identity 2. Całe oprogramowanie w mojej aplikacji jest najnowsze i opiera się na this example. Moje cele przeglądarki klienta są IE9 i powyżej.

Zauważ, że wprowadziłem kilka drobnych zmian w powyższym przykładzie, ponieważ nie koduję wszystkich danych wysyłanych do serwera za pomocą transformacji. Zamiast tego Kod urlencode tylko w authenticate metoda poniżej:

user.authenticate = function (userName, password, rememberMe, successCallback, errorCallback) {
    var config = {
        method: 'POST',
        url: '/Token',
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        data: 'grant_type=password&username=' + encodeURIComponent(userName) + '&password=' + encodeURIComponent(password),
    };

Rozwijam się z VS2013 Update 2 i na serwerze używam C#, najnowszego Entity Framework i SQL Server 2012.

Aby się zalogować mój klient wywołuje metodę / Token do WebAPI i podaje identyfikator użytkownika i hasło. Następnie WebAPI odpowiada tokenem do klienta, który przechowuję. Przy każdym żądaniu do WebAPI token jest wysyłany z powrotem i uwierzytelniany:

$http.defaults.headers.common.Authorization = 'Bearer ' + user.data.bearerToken;

To działa bardzo dobrze na razie, ale w obecnej sytuacji aplikacja nie jest w stanie powiedzieć różnica między użytkownikami, którzy mają przypisane różne role.

Niektóre metody WebAPI mogą być wykonywane tylko przez użytkowników, którzy mają określoną rolę. Chciałbym dostosować menu mojej front-endowej aplikacji AngularJS tak, aby tylko wtedy, gdy użytkownik ma tę rolę, widoczne będą odpowiednie linki. Zdaję sobie sprawę, że nie powstrzyma to Użytkownika przed sprawdzaniem HTML i publikowaniem, ale nie martwię się o to, ponieważ nadal będę miał dekorację metody, aby ograniczyć możliwość użytkownicy nie pełniący żadnej roli do wykonywania działań.

Czy ktoś może podać mi przykład, Jak mogę to zrobić za pomocą Tylko pakiet produktów wymienionych powyżej, o których wspominam w pytaniu plus tokeny JavaScript Web, jeśli pomogą one doprowadzić rozwiązanie do aktualności. Z tego co rozumiem role są obsługiwane przez roszczenia, ale nie rozumiem, jak je dodać i odesłać do klienta z tokenami. Zrobiłem wiele badań w Internecie, ale nie byłem w stanie znaleźć żadnego dobrego przykłady, ponieważ myślę, że większość z tego jest bardzo nowa i niewiele osób miało okazję zbadać, w jaki sposób SPA może korzystać z tych najnowszych komponentów oprogramowania.

Odpowiadając na to pytanie, proszę zauważyć, że nie szukam odpowiedzi, która powie społeczności, jak skonfigurować role na serwerze, lub odpowiedzi wyjaśniającej, jak ważne jest zapewnienie kontroli ról na serwerze. Myślę, że prawie każdy jest tego świadomy. To, co naprawdę myślę, będzie przydatne, to niektóre bardzo szczegółowe sugestie techniczne z przykładowym kodem i wyjaśnieniem. Aby skupić się na odpowiedzi, prawdopodobnie byłoby pomocne dla wszystkich, Jeśli odpowiedzi, które nie spełniają tej potrzeby, nie zostaną opublikowane jako sugerowane odpowiedzi.

Z góry dziękuję.
Author: Samantha J T Star, 2014-05-19

4 answers

Krótka odpowiedź na twoje pytanie to ApplicationOAuthProvider.CreateProperties metoda. Jest on domyślnie stworzony dla Ciebie i znajduje się w WebApi2 / Provider / ApplicationOAuthProvider.cs, domyślnie wysyła tylko userName

//WepApi2/Providers/ApplicationOAuthProvider.cs
public static AuthenticationProperties CreateProperties(string userName)
{
    IDictionary<string, string> data = new Dictionary<string, string>
    {
        { "userName", userName }
    };
    return new AuthenticationProperties(data);
}

Dokonałbym następującej aktualizacji (na wypadek, gdybym musiał później wysłać więcej danych Użytkownika):

public static AuthenticationProperties CreateProperties(string userName, ClaimsIdentity oAuthIdentity)
{ 
  IDictionary<string, string> data = new Dictionary<string, string>
  {
      { "userName", userName},
      { "roles",string.Join(",",oAuthIdentity.Claims.Where(c=> c.Type == ClaimTypes.Role).Select(c => c.Value).ToArray())}

  };
  return new AuthenticationProperties(data);
}

Jeśli nie dokonałeś istotnych zmian w projekcie WebApi, ApplicationOAuthProvider.CreateProperties jest odwołany tylko w dwóch miejscach, po prostu zaktualizuj kod wywołujący, aby przekazać oAuthIdentity wraz z user.UserName, a otrzymanie ról użytkowników wysłanych wraz z odpowiedzią tokena dostępu:

{
 "access_token": "ZpxAZyYuvCaWgShUz0c_XDLFqpbC0-DIeXl_tuFbr11G-5hzBzSUxFNwNPahsasBD9t6mDDJGHcuEqdvtBT4kDNQXFcjWYvFP7U2Y0EvLS3yejdSvUrh2v1N7Ntz80WKe5G_wy2t11eT0l48dgdyak8lYcl3Nx8D0cgwlQm-pePIanYZatdPFP9q5jzhD-_k9SF-ARTHgf0ePnbvhLBi1MCYQjvfgPKlbBHt0M5qjwGAeFg1IhSVj0gb4g9QTXoiPhRmxGBmjOpGgzxXixavmrpM7cCBFLoR3DCGnIJo6pwT-6VArxlB8-ZyyOZqh_6gGtptd0lIu8iJRUIGwO9HFNkROdoE9T4buwLnhPpWpy9geBjPVwsB1K3xnbch26YbklhxIHVybBxeIVXd17QTw_LjlQ5TJdqpAYfiZ5B9Nx2AFYYYe3--aemh4y1XOIvN",
 "token_type": "bearer",
 "expires_in": 1209599,
 "userName": "MK",
 "roles": "Admin,Public",
 ".issued": "Fri, 23 May 2014 17:36:54 GMT",
 ".expires": "Fri, 06 Jun 2014 17:36:54 GMT"
}

Teraz, gdy masz dostępne role, możesz użyć dyrektyw warunkowych kątowych do pokazywania / ukrywania akcji zgodnie z rolami użytkownika.

Jeśli potrzebujesz więcej wyjaśnień, daj mi znać.

Edit:

Dekorowanie metod kontrolera atrybutem Authorize jest poprawne, ponieważ {[12] } jest w rzeczywistości ClaimsIdentity. Ale jak nie do twardego kodu logiki bezpieczeństwa wewnątrz aplikacji, wolę używać ClaimsAuthorizationManager

public ActionResult Secure()
{
  if(!ClaimsPrincipalPermission.CheckAccess("resource", "action"))
    return new HttpUnauthorizedResult();

  ViewBag.Message = "You are allowed to perform action on resource.";
  return View();
}

Tworzenie ról za pomocą RoleManager:

RoleManager roleManger = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>());
roleManager.Create(new IdentityRole() { Name = "Admin" });

Przypisanie ról za pomocą UserManager:

userManager.AddToRole(user.Id, "Admin");
 21
Author: MK.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-24 19:08:58

Widzę, że możesz podejść do swojego problemu na dwa sposoby.

  1. Dołącz informacje o "roli" do tokena za pomocą hasha lub prostego ciągu znaków, ponieważ to Ty generujesz token, a następnie możesz go odszyfrować na kąt.

  2. Wygląda na to, że chcesz użyć ASP.NET System Identyfikacji i przechowywać i pobierać informacje o roli tam. Jeśli tak jest, możesz przejść przez Ten post zwróć uwagę na " Initialize the database to create admin Role and Admin Sekcja użytkownika.

IMO, #1 da ci większą elastyczność w jaki sposób przechowujesz i używasz danych użytkownika, ponieważ #2 śledzisz IdentityUser Microsoftu, chociaż czasami wygląda to magicznie i ma tendencję do ograniczania postów i musisz poświęcić czas, aby zrozumieć, jak to działa za sceną i sprawić, że będzie działać dla Twojego projektu.

Aby dowiedzieć się więcej o "indywidualnych kontach użytkowników", które wybierzesz podczas tworzonego projektu WebAPI, możesz przejść do http://www.asp.net/visual-studio/overview/2013/creating-web-projects-in-visual-studio#indauth

 2
Author: DigitalFreak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-22 02:45:48

Mam bardzo podobny scenariusz jak Twój, ale zamiast używać tokenów do uwierzytelniania, używam serwera tożsamości (Thinktecture ) do obsługi mojego uwierzytelniania. Moja aplikacja przekierowuje do serwera tożsamości, aby uwierzytelnić i wraca z kilkoma bardzo podstawowymi oświadczeniami (nazwa użytkownika i adres e-mail). Dzieje się tak, gdy tylko ktoś spróbuje najpierw przejść do strony. Po uwierzytelnieniu użytkownika i przekierowaniu do mojej aplikacji wykonuję kolejne połączenie na serwer, aby uzyskać uprawnienia użytkownika. Te uprawnienia są przechowywane w usłudze bezpieczeństwa (AngularJS), która również udostępnia metodę "hasPermissions". Następnie używam ng-if, aby zdecydować, czy mam zamiar wyświetlić pewne części strony - w tym pozycje menu. Coś takiego:

var service = {
    currentUser: ...,
    isAuthenticated: function() {
        return ...;
    },
    checkAccess: function(permission) {
        return service.isAuthenticated() ?
            !!(service.currentUser.permissions.indexOf(permission) > -1) : false;
    }
}

Pamiętaj, że wszystkie uprawnienia i elementy html są widoczne dla każdego, kto zdecyduje się nacisnąć przycisk dev tools i rzucić okiem. Musisz zrobić te same kontrole po stronie serwera przed wykonaniem jakiejkolwiek akcji. Mamy zwyczaj Atrybut autoryzacji oparty na this , który sprawdza, czy użytkownik ma niezbędne uprawnienia do wykonania akcji MVC / WebAPI przed wykonaniem jej dla prostych przypadków lub sprawdza ją w ramach akcji lub zasobu HTTP przed zrobieniem czegokolwiek, co wymaga podwyższonych uprawnień.

Jeśli chcesz, aby Klient nie widział żadnych elementów html lub niektórych sekcji witryny, możesz skierować szablony do akcji MVC, która uwierzytelni i zwróci szablon HTML lub przekierować na inną stronę (nie do serwera SPA) i uwierzytelnić je na serwerze przed powrotem odpowiedzi.

 0
Author: mithun_daa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-26 13:03:11

Tu kolejna odpowiedź:

In ApplicationOAuthProvider.cs

  public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {
            var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();

            ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);


            if (user == null)
            {
                context.SetError("invalid_grant", "The user name or password is incorrect.");
                return;
            }

Po prostu Dodaj niestandardowy nagłówek!

 context.OwinContext.Response.Headers.Add("Roles", userManager.GetRoles(user.Id).ToArray());
 0
Author: Pinch,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-29 22:15:14