Megosztás a következőn keresztül:


Webes API védelme egyéni fiókokkal és helyi bejelentkezéssel a ASP.NET Web API 2.2-ben

készítette: Mike Wasson

Mintaalkalmazás letöltése

Ez a témakör bemutatja, hogyan lehet biztonságossá tenni egy webes API-t az OAuth2 használatával a tagsági adatbázison való hitelesítéshez.

Az oktatóanyagban használt szoftververziók

A Visual Studio 2013-ban a Webes API-projektsablon három hitelesítési lehetőséget kínál:

  • Egyéni fiókok. Az alkalmazás tagsági adatbázist használ.
  • Szervezeti fiókok. A felhasználók az Azure Active Directory, az Office 365 vagy a helyszíni Active Directory hitelesítő adataikkal jelentkeznek be.
  • Windows-hitelesítés. Ez a beállítás intranetes alkalmazásokhoz készült, és a Windows Authentication IIS modult használja.

A felhasználók az egyes fiókjaikba kétféleképpen jelentkezhetnek be.

  • helyi bejelentkezés. A felhasználó regisztrál a webhelyen, és megadja a felhasználónevet és a jelszót. Az alkalmazás a jelszókivonatot a tagsági adatbázisban tárolja. Amikor a felhasználó bejelentkezik, a ASP.NET identitásrendszer ellenőrzi a jelszót.
  • közösségi bejelentkezés. A felhasználó egy külső szolgáltatással, például a Facebookkal, a Microsofttal vagy a Google-jal jelentkezik be. Az alkalmazás továbbra is létrehoz egy bejegyzést a felhasználó számára a tagsági adatbázisban, de nem tárol hitelesítő adatokat. A felhasználó a külső szolgáltatásba való bejelentkezéssel hitelesít.

Ez a cikk a helyi bejelentkezési forgatókönyvet ismerteti. A helyi és a közösségi bejelentkezéshez a Web API az OAuth2 használatával hitelesíti a kéréseket. A hitelesítő adatok folyamatai azonban eltérnek a helyi és a közösségi bejelentkezéshez.

Ebben a cikkben bemutatok egy egyszerű alkalmazást, amellyel a felhasználó bejelentkezhet, és hitelesített AJAX-hívásokat küldhet egy webes API-nak. A mintakódot itt töltheti le . A readme leírja, hogyan lehet létrehozni a mintát a semmiből a Visual Studióban.

Mintaűrlap képe

A mintaalkalmazás Knockout.js használ adatkötéshez és jQuery-hez az AJAX-kérések küldéséhez. Az AJAX-hívásokra összpontosítok, ezért nem kell ismernie a Knockout.js-t ehhez a cikkhez.

Az út során a következőt írom le:

  • Az alkalmazás feladata az ügyféloldalon.
  • Mi történik a kiszolgálón?
  • A HTTP-forgalom középen.

Először meg kell határoznunk néhány OAuth2-terminológiát.

  • erőforrás. Néhány adat, amely védhető.
  • erőforráskiszolgáló. Az erőforrást üzemeltető kiszolgáló.
  • erőforrás tulajdonosa. Az az entitás, amely engedélyt adhat egy erőforrás elérésére. (Általában a felhasználó.)
  • ügyfél: Az az alkalmazás, amely hozzá szeretne férni az erőforráshoz. Ebben a cikkben az ügyfél egy webböngésző.
  • Hozzáférési jogkivonat. Egy erőforráshoz hozzáférést biztosító jogkivonat.
  • Tulajdonosi jogkivonat. A hozzáférési jogkivonat egy adott típusa, amelynek tulajdonságát bárki használhatja. Más szóval az ügyfélnek nincs szüksége titkosítási kulcsra vagy más titkos kulcsra a tulajdonosi jogkivonat használatához. Ezért a tulajdonosi jogkivonatokat csak HTTPS-en keresztül szabad használni, és viszonylag rövid lejárati idővel kell rendelkezniük.
  • engedélyezési kiszolgáló. Egy kiszolgáló, amely hozzáférési jogkivonatokat ad ki.

Az alkalmazások engedélyezési kiszolgálóként és erőforrás-kiszolgálóként is működhetnek. A Webes API-projektsablon ezt a mintát követi.

Helyi bejelentkezési hitelesítőadat-folyamat

A helyi bejelentkezéshez a Webes API az OAuth2-ben definiált erőforrás-tulajdonosi jelszófolyamatot használja.

  1. A felhasználó nevet és jelszót ad meg az ügyfélnek.
  2. Az ügyfél elküldi ezeket a hitelesítő adatokat az engedélyezési kiszolgálónak.
  3. Az engedélyezési kiszolgáló hitelesíti a hitelesítő adatokat, és visszaad egy hozzáférési jogkivonatot.
  4. Védett erőforrás eléréséhez az ügyfél a HTTP-kérés Engedélyezési fejlécében tartalmazza a hozzáférési jogkivonatot.

helyi bejelentkezési hitelesítő adatok folyamatának diagramja

Amikor a Egyéni fiókokat választja a Web API projekt sablonban, a projekt tartalmaz egy engedélyezési kiszolgálót, amely ellenőrzi a felhasználói hitelesítő adatokat, és tokeneket ad ki. Az alábbi ábra ugyanazt a hitelesítőadat-folyamatot mutatja be a webes API-összetevők tekintetében.

Diagram, amikor az egyes fiókok ki vannak választva a Web A P I-ban

Ebben a forgatókönyvben a webes API-vezérlők erőforrás-kiszolgálókként működnek. A hitelesítési szűrő ellenőrzi a hozzáférési jogkivonatokat, és az [Engedélyezés] attribútumot használják az erőforrások védelmére. Ha egy vezérlő vagy művelet rendelkezik az [Engedélyezés] attribútummal, az adott vezérlőhöz vagy művelethez érkező összes kérést hitelesíteni kell. Ellenkező esetben a rendszer megtagadja az engedélyezést, és a Web API 401-es (jogosulatlan) hibát ad vissza.

Az engedélyezési kiszolgáló és a hitelesítési szűrő egyaránt meghív egy OWIN köztes szoftver összetevőbe, amely az OAuth2 részleteit kezeli. Az oktatóanyag későbbi részében részletesebben is bemutatom a tervet.

Jogosulatlan kérés küldése

Első lépésként futtassa az alkalmazást, és kattintson a Call API gombra. Amikor a kérés befejeződik, hibaüzenet jelenik meg az Eredmény mezőben. Ennek az az oka, hogy a kérelem nem tartalmaz hozzáférési jogkivonatot, ezért a kérés jogosulatlan.

Eredmény hibaüzenet képe

A Call API gomb egy AJAX-kérést küld a ~/api/values címre, amely webes API-vezérlőműveletet hív meg. Itt található az AJAX-kérést küldő JavaScript-kód szakasza. A mintaalkalmazásban az összes JavaScript-alkalmazáskód a Szkriptek\app.js fájlban található.

// If we already have a bearer token, set the Authorization header.
var token = sessionStorage.getItem(tokenKey);
var headers = {};
if (token) {
    headers.Authorization = 'Bearer ' + token;
}

$.ajax({
    type: 'GET',
    url: 'api/values/1',
    headers: headers
}).done(function (data) {
    self.result(data);
}).fail(showError);

Amíg a felhasználó be nem jelentkezik, nincs tulajdonosi jogkivonat, ezért nincs engedélyezési fejléc a kérelemben. Emiatt a kérés 401-et eredményez.

Itt található a HTTP-kérés. (Fiddler használtam a HTTP-forgalom rögzítéséhez.)

GET https://localhost:44305/api/values HTTP/1.1
Host: localhost:44305
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:32.0) Gecko/20100101 Firefox/32.0
Accept: */*
Accept-Language: en-US,en;q=0.5
X-Requested-With: XMLHttpRequest
Referer: https://localhost:44305/

HTTP-válasz:

HTTP/1.1 401 Unauthorized
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/8.0
WWW-Authenticate: Bearer
Date: Tue, 30 Sep 2014 21:54:43 GMT
Content-Length: 61

{"Message":"Authorization has been denied for this request."}

Figyelje meg, hogy a válasz tartalmaz egy Www-Authenticate fejlécet, amelyben a kihívás a Bearer értékre van beállítva. Ez azt jelzi, hogy a kiszolgáló hordozó tokent vár.

Felhasználó regisztrálása

Az alkalmazás Regisztrálás szakaszában adjon meg egy e-mailt és egy jelszót, majd kattintson a Regisztrálás gombra.

Ehhez a mintához nem kell érvényes e-mail-címet használnia, de egy valódi alkalmazás megerősítené a címet. (Lásd: Biztonságos ASP.NET MVC 5 webalkalmazás létrehozása bejelentkezéssel, e-mail-megerősítéssel és jelszó-visszaállítással.) A jelszóhoz használjon valami hasonlót: "Jelszó1!", nagybetűvel, kisbetűvel, számmal és nem alfa-numerikus karakterrel. Az alkalmazás egyszerűségéhez kihagytam az ügyféloldali érvényesítést, így ha probléma van a jelszóformátummal, 400 -os (hibás kérelem) hibaüzenet jelenik meg.

Felhasználói szakasz regisztrálásának képe

A Regisztrálás gomb post kérést küld a ~/api/Account/Register/címre. A kérelem törzse egy JSON-objektum, amely tartalmazza a nevet és a jelszót. Itt található a kérést küldő JavaScript-kód:

var data = {
    Email: self.registerEmail(),
    Password: self.registerPassword(),
    ConfirmPassword: self.registerPassword2()
};

$.ajax({
    type: 'POST',
    url: '/api/Account/Register',
    contentType: 'application/json; charset=utf-8',
    data: JSON.stringify(data)
}).done(function (data) {
    self.result("Done!");
}).fail(showError);

HTTP-kérés, ahol $CREDENTIAL_PLACEHOLDER$ a jelszókulcs-érték pár helyőrzője:

POST https://localhost:44305/api/Account/Register HTTP/1.1
Host: localhost:44305
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:32.0) Gecko/20100101 Firefox/32.0
Accept: */*
Content-Type: application/json; charset=utf-8
X-Requested-With: XMLHttpRequest
Referer: https://localhost:44305/
Content-Length: 84

{"Email":"alice@example.com",$CREDENTIAL_PLACEHOLDER1$,$CREDENTIAL_PLACEHOLDER2$"}

HTTP-válasz:

HTTP/1.1 200 OK
Server: Microsoft-IIS/8.0
Date: Wed, 01 Oct 2014 00:57:58 GMT
Content-Length: 0

Ezt a kérést a AccountController osztály kezeli. Belsőleg a AccountController ASP.NET Identity használatával kezeli a tagsági adatbázist.

Ha az alkalmazást helyileg futtatja a Visual Studióból, a felhasználói fiókok a LocalDB-ben, az AspNetUsers táblában lesznek tárolva. A Táblák Visual Studióban való megtekintéséhez kattintson a Nézet menüre, válassza Kiszolgálókezelőlehetőséget, majd bontsa ki adatkapcsolatok.

adatkapcsolatok képe

Hozzáférési jogkivonat lekérése

Eddig nem végeztünk OAuth-hitelesítést, de most működés közben látni fogjuk az OAuth engedélyezési kiszolgálót, amikor hozzáférési jogkivonatot kérünk. A mintaalkalmazás Bejelentkezés területén adja meg az e-mailt és a jelszót, majd kattintson Bejelentkezésgombra.

Bejelentkezési szakasz képe

A Bejelentkezés gomb kérést küld a token végpontra. A kérelem törzse az alábbi űrlap-URL-kódolású adatokat tartalmazza:

  • grant_type: "jelszó"
  • felhasználónév: <a felhasználó e-mailje:>
  • jelszó: <jelszó>

Itt található az AJAX-kérést küldő JavaScript-kód:

var loginData = {
    grant_type: 'password',
    username: self.loginEmail(),
    password: self.loginPassword()
};

$.ajax({
    type: 'POST',
    url: '/Token',
    data: loginData
}).done(function (data) {
    self.user(data.userName);
    // Cache the access token in session storage.
    sessionStorage.setItem(tokenKey, data.access_token);
}).fail(showError);

Ha a kérés sikeres, az engedélyezési kiszolgáló egy hozzáférési jogkivonatot ad vissza a válasz törzsében. Figyelje meg, hogy a jogkivonatot munkamenet-tárolóban tároljuk, hogy később használhassuk, amikor kéréseket küldünk az API-nak. A hitelesítés egyes formáival (például a cookie-alapú hitelesítéssel) ellentétben a böngésző nem fogja automatikusan belefoglalni a hozzáférési jogkivonatot a későbbi kérésekbe. Az alkalmazásnak kifejezetten ezt kell tennie. Ez jó dolog, mert korlátozza CSRF biztonsági réseit.

HTTP-kérés:

POST https://localhost:44305/Token HTTP/1.1
Host: localhost:44305
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:32.0) Gecko/20100101 Firefox/32.0
Accept: */*
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Referer: https://localhost:44305/
Content-Length: 68

grant_type=password&username=alice%40example.com&password=Password1!

Láthatja, hogy a kérelem tartalmazza a felhasználó hitelesítő adatait. Az átviteli réteg biztonságának biztosításához https kell használnia.

HTTP-válasz:

HTTP/1.1 200 OK
Content-Length: 669
Content-Type: application/json;charset=UTF-8
Server: Microsoft-IIS/8.0
Date: Wed, 01 Oct 2014 01:22:36 GMT

{
  "access_token":"imSXTs2OqSrGWzsFQhIXziFCO3rF...",
  "token_type":"bearer",
  "expires_in":1209599,
  "userName":"alice@example.com",
  ".issued":"Wed, 01 Oct 2014 01:22:33 GMT",
  ".expires":"Wed, 15 Oct 2014 01:22:33 GMT"
}

Az olvashatóság érdekében behúztam a JSON-t, és csonkoltam a hozzáférési jogkivonatot, amely meglehetősen hosszú.

A access_token, token_typeés expires_in tulajdonságait az OAuth2 specifikáció határozza meg. A többi tulajdonság (userName, .issuedés .expires) csak tájékoztató jellegűek. A további tulajdonságokat hozzáadó kódot a /Providers/ApplicationOAuthProvider.cs fájlban, a TokenEndpoint metódusban találja.

Hitelesített kérelem küldése

Most, hogy rendelkezünk egy tulajdonosi jogkivonattal, hitelesíthetjük a kérést az API-hoz. Ez a kérelem engedélyezési fejlécének beállításával végezhető el. Kattintson ismét a Call API gombra, hogy ezt láthassa.

Kép az API gombra kattintás után

HTTP-kérés:

GET https://localhost:44305/api/values/1 HTTP/1.1
Host: localhost:44305
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:32.0) Gecko/20100101 Firefox/32.0
Accept: */*
Authorization: Bearer imSXTs2OqSrGWzsFQhIXziFCO3rF...
X-Requested-With: XMLHttpRequest

HTTP-válasz:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/8.0
Date: Wed, 01 Oct 2014 01:41:29 GMT
Content-Length: 27

"Hello, alice@example.com."

Kijelentkezés

Mivel a böngésző nem gyorsítótárazza a hitelesítő adatokat vagy a hozzáférési jogkivonatot, a kijelentkezés egyszerűen a jogkivonat "elfelejtéséről" szól, ha eltávolítja azt a munkamenet-tárolóból:

self.logout = function () {
    sessionStorage.removeItem(tokenKey)
}

Az egyes fiókok projektsablonjának ismertetése

Amikor az ASP.NET webalkalmazás projektsablonjában az Egyes fiókok lehetőséget választja, a projekt a következőket foglalja magában:

  • OAuth2 engedélyezési kiszolgáló.
  • Webes API-végpont felhasználói fiókok kezeléséhez
  • A felhasználói fiókok tárolására szolgáló EF-modell.

Az alábbi funkciókat megvalósító fő alkalmazásosztályok:

  • AccountController. Webes API-végpontot biztosít a felhasználói fiókok kezeléséhez. A Register művelet az egyetlen, amelyet ebben az oktatóanyagban használtunk. Az osztály egyéb módszerei támogatják a jelszó-visszaállítást, a közösségi bejelentkezéseket és más funkciókat.
  • ApplicationUser, definiálva a /Models/IdentityModels.cs. Ez az osztály a tagsági adatbázisban lévő felhasználói fiókok EF-modellje.
  • ApplicationUserManagera /App_Start/IdentityConfig.cs fájlban van definiálva. Ez az osztály a UserManager osztályból származik, és műveleteket végez a felhasználói fiókokon, például létrehoz egy új felhasználót, ellenőrzi a jelszavakat stb.; az adatbázis változásait pedig automatikusan menti.
  • ApplicationOAuthProvider. Ez az objektum az OWIN köztes szoftverhez csatlakozik, és feldolgozza a köztes szoftver által kiváltott eseményeket. Ez a OAuthAuthorizationServerProvideralapján működik.

Fő alkalmazásosztályok képe

Az engedélyezési kiszolgáló konfigurálása

A StartupAuth.cs az alábbi kód konfigurálja az OAuth2 engedélyezési kiszolgálót.

PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
    TokenEndpointPath = new PathString("/Token"),
    Provider = new ApplicationOAuthProvider(PublicClientId),
    AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
    AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
    // Note: Remove the following line before you deploy to production:
    AllowInsecureHttp = true
};

// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);

A TokenEndpointPath tulajdonság az engedélyezési kiszolgáló végpontjának URL-címe. Az alkalmazás ezt az URL-címet használja a tulajdonosi jogkivonatok lekéréséhez.

A Provider tulajdonság egy olyan szolgáltatót határoz meg, amely az OWIN köztes szoftverhez csatlakozik, és feldolgozza a köztes szoftver által kiváltott eseményeket.

Az alapvető folyamat, amikor az alkalmazás egy token-t szeretne lekérni, a következő:

  1. Hozzáférési jogkivonat lekéréséhez az alkalmazás kérést küld a ~/Token címre.
  2. Az OAuth köztes szoftver meghívja GrantResourceOwnerCredentials-t a szolgáltatónál.
  3. A szolgáltató hívja a ApplicationUserManager-t, hogy ellenőrizze a hitelesítő adatokat és létrehozzon egy igényidentitást.
  4. Ha ez sikerül, a szolgáltató létrehoz egy hitelesítési jegyet, amely a token létrehozásához használatos.

engedélyezési folyamat diagramja

Az OAuth köztes szoftver nem tud semmit a felhasználói fiókokról. A szolgáltató kommunikál a köztes szoftver és a ASP.NET Identitás között. Az engedélyezési kiszolgáló implementálásával kapcsolatos további információkért lásd OWIN OAuth 2.0 engedélyezési kiszolgáló.

A Webes API konfigurálása a Megbízó tokenek használatára

A WebApiConfig.Register metódusban a következő kód állítja be a webes API-folyamat hitelesítését:

config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));

A HostAuthenticationFilter osztály engedélyezi a hitelesítést bearer tokenekkel.

A SuppressDefaultHostAuthentication metódus arra utasítja a Web API-t, hogy hagyja figyelmen kívül azokat a hitelesítéseket, amelyek azelőtt történnek, hogy a kérés elérné a Webes API-folyamatot, akár IIS, akár OWIN köztes szoftver használatával. Így korlátozhatjuk, hogy a Webes API csak tulajdonosi jogkivonatok használatával hitelesítsen.

Jegyzet

Az alkalmazás MVC-része űrlaphitelesítést használhat, amely a hitelesítő adatokat egy cookie-ban tárolja. A cookie-alapú hitelesítéshez hamisítás elleni jogkivonatok használata szükséges a CSRF-támadások megelőzése érdekében. Ez problémát jelent a webes API-k esetében, mert a webes API nem kényelmesen küldheti el a hamisítás elleni jogkivonatot az ügyfélnek. (A probléma hátteréről további információt a CsRF-támadások megakadályozása a Webes API-bancímű témakörben talál.) A SuppressDefaultHostAuthentication hívásával biztosítható, hogy a Web API ne legyen sebezhető a cookie-kban tárolt hitelesítő adatok CSRF-támadásai ellen.

Amikor az ügyfél védett erőforrást kér, a web API-folyamat az alábbi módon történik:

  1. A HostAuthentication szűrő meghívja az OAuth köztes szoftvert a jogkivonat érvényesítéséhez.
  2. A köztes szoftver jogcím-identitássá alakítja a jogkivonatot.
  3. A kérés jelenleg hitelesített de nem engedélyezett.
  4. Az engedélyezési szűrő megvizsgálja a jogcím-identitást. Ha a jogcímek engedélyezik a felhasználó számára az erőforrást, a kérés engedélyezve van. Alapértelmezés szerint az [Engedélyezés] attribútum engedélyezi a hitelesített kéréseket. Azonban engedélyezheti szerepkör vagy más jogcímek alapján. További információ: Hitelesítés és engedélyezés a Webes API-ban.
  5. Ha az előző lépések sikeresek, a vezérlő visszaadja a védett erőforrást. Ellenkező esetben az ügyfél 401(jogosulatlan) hibaüzenetet kap.

Diagram, amely azt szemlélteti, hogy az ügyfél mikor kér védett erőforrást

További erőforrások