Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
oleh Mike Wasson
Topik ini menunjukkan cara mengamankan API web menggunakan OAuth2 untuk mengautentikasi terhadap database keanggotaan.
Versi perangkat lunak yang digunakan dalam tutorial
Di Visual Studio 2013, templat proyek API Web memberi Anda tiga opsi untuk autentikasi:
- Akun individual. Aplikasi ini menggunakan database keanggotaan.
- Akun organisasi. Pengguna masuk dengan kredensial Azure Active Directory, Office 365, atau Active Directory lokal mereka.
- Autentikasi Windows. Opsi ini ditujukan untuk aplikasi Intranet, dan menggunakan modul IIS Autentikasi Windows.
Akun individual menyediakan dua cara bagi pengguna untuk masuk:
- Masuk lokal. Pengguna mendaftar di situs, memasukkan nama pengguna dan kata sandi. Aplikasi ini menyimpan hash kata sandi dalam database keanggotaan. Saat pengguna masuk, sistem identitas ASP.NET memverifikasi kata sandi.
- Masuk sosial. Pengguna masuk dengan layanan eksternal, seperti Facebook, Microsoft, atau Google. Aplikasi ini masih membuat entri untuk pengguna dalam database keanggotaan, tetapi tidak menyimpan kredensial apa pun. Pengguna mengautentikasi dengan masuk ke layanan eksternal.
Artikel ini melihat skenario masuk lokal. Untuk login lokal dan sosial, Api Web menggunakan OAuth2 untuk mengautentikasi permintaan. Namun, alur kredensial berbeda untuk login lokal dan sosial.
Dalam artikel ini, saya akan menunjukkan aplikasi sederhana yang memungkinkan pengguna masuk dan mengirim panggilan AJAX terautentikasi ke API web. Anda dapat mengunduh kode sampel di sini. Readme menjelaskan cara membuat sampel dari awal di Visual Studio.
Aplikasi sampel menggunakan Knockout.js untuk pengikatan data dan jQuery untuk mengirim permintaan AJAX. Saya akan berfokus pada panggilan AJAX, jadi Anda tidak perlu tahu Knockout.js untuk artikel ini.
Sepanjang jalan, saya akan menjelaskan:
- Apa yang dilakukan aplikasi di sisi klien.
- Apa yang terjadi di server.
- Lalu lintas HTTP di tengah.
Pertama, kita perlu menentukan beberapa terminologi OAuth2.
- Sumber daya. Beberapa bagian data yang dapat dilindungi.
- Server sumber daya. Server yang menghosting sumber daya.
- Pemilik sumber daya. Entitas yang dapat memberikan izin untuk mengakses sumber daya. (Biasanya pengguna.)
- Klien: Aplikasi yang menginginkan akses ke sumber daya. Dalam artikel ini, klien adalah browser web.
- Token akses. Token yang memberikan akses ke sumber daya.
- Token pembawa. Jenis token akses tertentu, dengan properti yang dapat digunakan siapa pun untuk menggunakan token. Dengan kata lain, klien tidak memerlukan kunci kriptografi atau rahasia lain untuk menggunakan token pembawa. Untuk alasan itu, token pembawa hanya boleh digunakan melalui HTTPS, dan harus memiliki waktu kedaluwarsa yang relatif singkat.
- Server otorisasi. Server yang memberikan token akses.
Aplikasi dapat bertindak sebagai server otorisasi dan server sumber daya. Templat proyek API Web mengikuti pola ini.
Alur Kredensial Masuk Lokal
Untuk login lokal, Api Web menggunakan alur kata sandi pemilik sumber daya yang ditentukan dalam OAuth2.
- Pengguna memasukkan nama dan kata sandi ke klien.
- Klien mengirimkan kredensial ini ke server otorisasi.
- Server otorisasi mengautentikasi kredensial dan mengembalikan token akses.
- Untuk mengakses sumber daya yang dilindungi, klien menyertakan token akses di header Otorisasi permintaan HTTP.
Saat Anda memilih Akun individual dalam templat proyek API Web, proyek menyertakan server otorisasi yang memvalidasi kredensial pengguna dan mengeluarkan token. Diagram berikut menunjukkan alur kredensial yang sama dalam hal komponen API Web.
Dalam skenario ini, pengontrol API Web bertindak sebagai server sumber daya. Filter autentikasi memvalidasi token akses, dan atribut [Otorisasi] digunakan untuk melindungi sumber daya. Ketika pengontrol atau tindakan memiliki atribut [Otorisasi ], semua permintaan ke pengontrol atau tindakan tersebut harus diautentikasi. Jika tidak, otorisasi ditolak, dan API Web mengembalikan kesalahan 401 (Tidak Sah).
Server otorisasi dan filter autentikasi keduanya memanggil komponen middleware OWIN yang menangani detail OAuth2. Saya akan menjelaskan desain secara lebih rinci nanti dalam tutorial ini.
Mengirim Permintaan Tidak Sah
Untuk memulai, jalankan aplikasi dan klik tombol Panggil API . Saat permintaan selesai, Anda akan melihat pesan kesalahan di kotak Hasil . Itu karena permintaan tidak berisi token akses, sehingga permintaan tidak sah.
Tombol Panggil API mengirimkan permintaan AJAX ke ~/api/values, yang memanggil tindakan pengontrol API Web. Berikut adalah bagian kode JavaScript yang mengirim permintaan AJAX. Di aplikasi sampel, semua kode aplikasi JavaScript terletak di file Scripts\app.js.
// 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);
Hingga pengguna masuk, tidak ada token pembawa, dan oleh karena itu tidak ada header Otorisasi dalam permintaan. Ini menyebabkan permintaan mengembalikan kesalahan 401.
Berikut adalah permintaan HTTP. (Saya menggunakan Fiddler untuk menangkap lalu lintas HTTP.)
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/
Respons HTTP:
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."}
Perhatikan bahwa respons menyertakan header Www-Authenticate dengan tantangan yang diatur ke Pembawa. Itu menunjukkan server mengharapkan token pembawa.
Mendaftarkan Pengguna
Di bagian Daftarkan aplikasi, masukkan email dan kata sandi, dan klik tombol Daftar .
Anda tidak perlu menggunakan alamat email yang valid untuk sampel ini, tetapi aplikasi nyata akan mengonfirmasi alamatnya. (Lihat Buat aplikasi web ASP.NET MVC 5 yang aman dengan masuk, konfirmasi email, dan reset kata sandi.) Untuk kata sandi, gunakan sesuatu seperti "Password1!", dengan huruf besar, huruf kecil, angka, dan karakter non-alfa-numerik. Agar aplikasi tetap sederhana, saya meninggalkan validasi sisi klien, jadi jika ada masalah dengan format kata sandi, Anda akan mendapatkan kesalahan 400 (Permintaan Buruk).
Tombol Daftar mengirimkan permintaan POST ke ~/api/Account/Register/. Isi permintaan adalah objek JSON yang menyimpan nama dan kata sandi. Berikut adalah kode JavaScript yang mengirim permintaan:
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);
Permintaan HTTP, di mana $CREDENTIAL_PLACEHOLDER$
adalah tempat penampung untuk pasangan kunci-nilai kata sandi:
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$"}
Respons HTTP:
HTTP/1.1 200 OK
Server: Microsoft-IIS/8.0
Date: Wed, 01 Oct 2014 00:57:58 GMT
Content-Length: 0
Permintaan ini ditangani oleh AccountController
kelas . Secara internal, AccountController
menggunakan identitas ASP.NET untuk mengelola database keanggotaan.
Jika Anda menjalankan aplikasi secara lokal dari Visual Studio, akun pengguna disimpan di LocalDB, di tabel AspNetUsers. Untuk menampilkan tabel di Visual Studio, klik menu Tampilan , pilih Penjelajah Server, lalu perluas Koneksi Data.
Mendapatkan Token Akses
Sejauh ini kami belum melakukan OAuth apa pun, tetapi sekarang kita akan melihat server otorisasi OAuth beraksi, ketika kita meminta token akses. Di area Masuk dari aplikasi sampel, masukkan email dan kata sandi dan klik Masuk.
Tombol Masuk mengirimkan permintaan ke titik akhir token. Isi permintaan berisi data yang dikodekan url formulir berikut:
- grant_type: "kata sandi"
- nama pengguna: <email pengguna>
- kata sandi: <kata sandi>
Berikut adalah kode JavaScript yang mengirim permintaan AJAX:
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);
Jika permintaan berhasil, server otorisasi mengembalikan token akses di isi respons. Perhatikan bahwa kami menyimpan token dalam penyimpanan sesi, untuk digunakan nanti saat mengirim permintaan ke API. Tidak seperti beberapa bentuk autentikasi (seperti autentikasi berbasis cookie), browser tidak akan secara otomatis menyertakan token akses dalam permintaan berikutnya. Aplikasi harus melakukannya secara eksplisit. Itu hal yang baik, karena membatasi kerentanan CSRF.
Permintaan HTTP:
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!
Anda dapat melihat bahwa permintaan berisi kredensial pengguna. Anda harus menggunakan HTTPS untuk memberikan keamanan lapisan transportasi.
Respons HTTP:
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"
}
Untuk keterbacaan, saya mengindentasi JSON dan memotong token akses, yang cukup panjang.
Properti access_token
, token_type
, dan expires_in
ditentukan oleh spesifikasi OAuth2. Properti lain (userName
, .issued
, dan .expires
) hanya untuk tujuan informasi. Anda dapat menemukan kode yang menambahkan properti tambahan tersebut TokenEndpoint
dalam metode , dalam file /Providers/ApplicationOAuthProvider.cs.
Mengirim Permintaan Terautentikasi
Sekarang setelah kita memiliki token pembawa, kita dapat membuat permintaan terautentikasi ke API. Ini dilakukan dengan mengatur header Otorisasi dalam permintaan. Klik lagi tombol Panggil API untuk melihat ini.
Permintaan HTTP:
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
Respons HTTP:
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."
Keluar
Karena browser tidak menyimpan kredensial atau token akses, keluar hanyalah masalah "melupakan" token, dengan menghapusnya dari penyimpanan sesi:
self.logout = function () {
sessionStorage.removeItem(tokenKey)
}
Memahami Templat Proyek Akun Individual
Saat Anda memilih Akun Individual di templat proyek Aplikasi Web ASP.NET, proyek tersebut meliputi:
- Server otorisasi OAuth2.
- Titik akhir API Web untuk mengelola akun pengguna
- Model EF untuk menyimpan akun pengguna.
Berikut adalah kelas aplikasi utama yang mengimplementasikan fitur-fitur ini:
-
AccountController
. Menyediakan titik akhir API Web untuk mengelola akun pengguna. TindakanRegister
ini adalah satu-satunya yang kami gunakan dalam tutorial ini. Metode lain di kelas mendukung pengaturan ulang kata sandi, login sosial, dan fungsionalitas lainnya. -
ApplicationUser
, ditentukan dalam /Models/IdentityModels.cs. Kelas ini adalah model EF untuk akun pengguna dalam database keanggotaan. -
ApplicationUserManager
, ditentukan dalam /App_Start/IdentityConfig.cs Kelas ini berasal dari UserManager dan melakukan operasi pada akun pengguna, seperti membuat pengguna baru, memverifikasi kata sandi, dan sebagainya, dan secara otomatis mempertahankan perubahan pada database. -
ApplicationOAuthProvider
. Objek ini dicolokkan ke middleware OWIN, dan memproses peristiwa yang diangkat oleh middleware. Ini berasal dari OAuthAuthorizationServerProvider.
Mengonfigurasi Server Otorisasi
Dalam StartupAuth.cs, kode berikut mengonfigurasi server otorisasi OAuth2.
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);
Properti TokenEndpointPath
adalah jalur URL ke titik akhir server otorisasi. Itulah URL yang digunakan aplikasi untuk mendapatkan token pembawa.
Properti Provider
menentukan penyedia yang terhubung ke middleware OWIN, dan memproses peristiwa yang dinaikkan oleh middleware.
Berikut adalah alur dasar ketika aplikasi ingin mendapatkan token:
- Untuk mendapatkan token akses, aplikasi mengirimkan permintaan ke ~/Token.
- Middleware OAuth memanggil
GrantResourceOwnerCredentials
penyedia. - Penyedia memanggil
ApplicationUserManager
untuk memvalidasi kredensial dan membuat identitas klaim. - Jika berhasil, penyedia membuat tiket autentikasi, yang digunakan untuk menghasilkan token.
Middleware OAuth tidak tahu apa-apa tentang akun pengguna. Penyedia berkomunikasi antara middleware dan identitas ASP.NET. Untuk informasi selengkapnya tentang menerapkan server otorisasi, lihat Server Otorisasi OWIN OAuth 2.0.
Mengonfigurasi API Web untuk menggunakan Token Pembawa
Dalam metode , WebApiConfig.Register
kode berikut menyiapkan autentikasi untuk alur API Web:
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
Kelas HostAuthenticationFilter memungkinkan autentikasi menggunakan token pembawa.
Metode SuppressDefaultHostAuthentication memberi tahu API Web untuk mengabaikan autentikasi apa pun yang terjadi sebelum permintaan mencapai alur API Web, baik oleh IIS atau oleh middleware OWIN. Dengan begitu, kita dapat membatasi API Web untuk mengautentikasi hanya menggunakan token pembawa.
Catatan
Secara khusus, bagian MVC aplikasi Anda mungkin menggunakan autentikasi formulir, yang menyimpan kredensial dalam cookie. Autentikasi berbasis cookie memerlukan penggunaan token anti-pemalsuan, untuk mencegah serangan CSRF. Itu masalah untuk API web, karena tidak ada cara mudah bagi API web untuk mengirim token anti-pemalsuan ke klien. (Untuk latar belakang lebih lanjut tentang masalah ini, lihat Mencegah Serangan CSRF di API Web.) Memanggil SuppressDefaultHostAuthentication memastikan bahwa API Web tidak rentan terhadap serangan CSRF dari kredensial yang disimpan dalam cookie.
Ketika klien meminta sumber daya yang dilindungi, inilah yang terjadi di alur API Web:
- Filter HostAuthentication memanggil middleware OAuth untuk memvalidasi token.
- Middleware mengonversi token menjadi identitas klaim.
- Pada titik ini, permintaan diautentikasi tetapi tidak diotorisasi.
- Filter otorisasi memeriksa identitas klaim. Jika klaim mengotorisasi pengguna untuk sumber daya tersebut, permintaan akan diotorisasi. Secara default, atribut [Otorisasi] akan mengotorisasi permintaan apa pun yang diautentikasi. Namun, Anda dapat mengotorisasi berdasarkan peran atau oleh klaim lain. Untuk informasi selengkapnya, lihat Autentikasi dan Otorisasi di API Web.
- Jika langkah-langkah sebelumnya berhasil, pengontrol mengembalikan sumber daya yang dilindungi. Jika tidak, klien menerima kesalahan 401 (Tidak Sah).
Sumber Tambahan
- Identitas ASP.NET
- Memahami Fitur Keamanan di Templat SPA untuk VS2013 RC. Posting blog MSDN oleh Hongye Sun.
- Membedah Templat Akun Individual API Web–Bagian 2: Akun Lokal. Posting blog oleh Dominick Baier.
-
Autentikasi host dan API Web dengan OWIN. Penjelasan yang baik dari
SuppressDefaultHostAuthentication
danHostAuthenticationFilter
oleh Brock Allen. - Menyesuaikan informasi profil dalam identitas ASP.NET dalam templat VS 2013. Posting blog MSDN oleh Pranav Rastogi.
-
Manajemen seumur hidup per permintaan untuk kelas UserManager di ASP.NET Identity. Posting blog MSDN oleh Suhas Joshi, dengan penjelasan yang baik tentang
UserManager
kelas.