Saya menjawab pertanyaan ini: Bagaimana cara mengamankan API Web ASP.NET 4 tahun yang lalu menggunakan HMAC.
Sekarang, banyak hal berubah dalam keamanan, terutama JWT semakin populer. Di sini, saya akan mencoba menjelaskan cara menggunakan JWT dengan cara paling sederhana dan dasar yang saya bisa, jadi kita tidak akan tersesat dari hutan OWIN, Oauth2, ASP.NET Identity ... :).
Jika Anda tidak tahu token JWT, Anda perlu melihatnya sedikit di:
https://tools.ietf.org/html/rfc7519
Pada dasarnya, token JWT terlihat seperti:
<base64-encoded header>.<base64-encoded claims>.<base64-encoded signature>
Contoh:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6ImN1b25nIiwibmJmIjoxNDc3NTY1NzI0LCJleHAiOjE0Nzc1NjY5MjQsImlhdCI6MTQ3NzU2NTcyNH0.6MzD1VwA5AcOcajkFyKhLYybr3h13iZjDyHm9zysDFQ
Token JWT memiliki tiga bagian:
- Header: Format JSON yang disandikan dalam Base64
- Klaim: Format JSON yang disandikan di Base64.
- Tanda tangan: Dibuat dan ditandatangani berdasarkan Header dan Klaim yang disandikan di Base64.
Jika Anda menggunakan situs web jwt.io dengan token di atas, Anda dapat mendekode token dan melihatnya seperti di bawah ini:
Secara teknis, JWT menggunakan tanda tangan yang ditandatangani dari header dan klaim dengan algoritma keamanan yang ditentukan dalam header (contoh: HMACSHA256). Oleh karena itu, JWT harus ditransfer melalui HTTP jika Anda menyimpan informasi sensitif apa pun dalam klaim.
Sekarang, untuk menggunakan otentikasi JWT, Anda tidak benar-benar membutuhkan middleware OWIN jika Anda memiliki sistem Web Api yang lama. Konsep sederhananya adalah bagaimana menyediakan token JWT dan bagaimana memvalidasi token ketika permintaan datang. Itu dia.
Kembali ke demo, agar token JWT tetap ringan, saya hanya menyimpan username
dan expiration time
di JWT. Tetapi dengan cara ini, Anda harus membangun kembali identitas lokal baru (kepala sekolah) untuk menambahkan lebih banyak informasi seperti: peran .. jika Anda ingin melakukan otorisasi peran. Tetapi, jika Anda ingin menambahkan lebih banyak informasi ke JWT, terserah Anda: sangat fleksibel.
Alih-alih menggunakan middleware OWIN, Anda cukup memberikan titik akhir JWT token dengan menggunakan aksi dari controller:
public class TokenController : ApiController
{
// This is naive endpoint for demo, it should use Basic authentication
// to provide token or POST request
[AllowAnonymous]
public string Get(string username, string password)
{
if (CheckUser(username, password))
{
return JwtManager.GenerateToken(username);
}
throw new HttpResponseException(HttpStatusCode.Unauthorized);
}
public bool CheckUser(string username, string password)
{
// should check in the database
return true;
}
}
Ini adalah tindakan naif; dalam produksi, Anda harus menggunakan permintaan POST atau titik akhir Otentikasi Dasar untuk memberikan token JWT.
Bagaimana cara menghasilkan token username
?
Anda dapat menggunakan paket NuGet yang dipanggil System.IdentityModel.Tokens.Jwt
dari Microsoft untuk menghasilkan token, atau bahkan paket lain jika Anda mau. Dalam demo, saya gunakan HMACSHA256
dengan SymmetricKey
:
/// <summary>
/// Use the below code to generate symmetric Secret Key
/// var hmac = new HMACSHA256();
/// var key = Convert.ToBase64String(hmac.Key);
/// </summary>
private const string Secret = "db3OIsj+BXE9NZDy0t8W3TcNekrF+2d/1sFnWG4HnV8TZY30iTOdtVWJG8abWvB1GlOgJuQZdcF2Luqm/hccMw==";
public static string GenerateToken(string username, int expireMinutes = 20)
{
var symmetricKey = Convert.FromBase64String(Secret);
var tokenHandler = new JwtSecurityTokenHandler();
var now = DateTime.UtcNow;
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.Name, username)
}),
Expires = now.AddMinutes(Convert.ToInt32(expireMinutes)),
SigningCredentials = new SigningCredentials(
new SymmetricSecurityKey(symmetricKey),
SecurityAlgorithms.HmacSha256Signature)
};
var stoken = tokenHandler.CreateToken(tokenDescriptor);
var token = tokenHandler.WriteToken(stoken);
return token;
}
Titik akhir untuk memberikan token JWT telah selesai. Sekarang, bagaimana memvalidasi JWT ketika permintaan datang? Dalam demo saya telah membangun
JwtAuthenticationAttribute
yang mewarisi dari IAuthenticationFilter
(lebih detail tentang filter otentikasi di sini ).
Dengan atribut ini, Anda dapat mengautentikasi tindakan apa pun: Anda hanya perlu menempatkan atribut ini pada tindakan itu.
public class ValueController : ApiController
{
[JwtAuthentication]
public string Get()
{
return "value";
}
}
Anda juga dapat menggunakan middleware OWIN atau DelegateHander jika Anda ingin memvalidasi semua permintaan yang masuk untuk WebAPI Anda (tidak khusus untuk Kontroler atau tindakan)
Di bawah ini adalah metode inti dari filter otentikasi:
private static bool ValidateToken(string token, out string username)
{
username = null;
var simplePrinciple = JwtManager.GetPrincipal(token);
var identity = simplePrinciple.Identity as ClaimsIdentity;
if (identity == null)
return false;
if (!identity.IsAuthenticated)
return false;
var usernameClaim = identity.FindFirst(ClaimTypes.Name);
username = usernameClaim?.Value;
if (string.IsNullOrEmpty(username))
return false;
// More validate to check whether username exists in system
return true;
}
protected Task<IPrincipal> AuthenticateJwtToken(string token)
{
string username;
if (ValidateToken(token, out username))
{
// based on username to get more information from database
// in order to build local identity
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, username)
// Add more claims if needed: Roles, ...
};
var identity = new ClaimsIdentity(claims, "Jwt");
IPrincipal user = new ClaimsPrincipal(identity);
return Task.FromResult(user);
}
return Task.FromResult<IPrincipal>(null);
}
Alur kerjanya adalah, menggunakan perpustakaan JWT (paket NuGet di atas) untuk memvalidasi token JWT dan kemudian kembali ClaimsPrincipal
. Anda dapat melakukan lebih banyak validasi seperti memeriksa apakah pengguna ada di sistem Anda dan menambahkan validasi khusus lainnya jika Anda mau. Kode untuk memvalidasi token JWT dan mendapatkan kembali pokok:
public static ClaimsPrincipal GetPrincipal(string token)
{
try
{
var tokenHandler = new JwtSecurityTokenHandler();
var jwtToken = tokenHandler.ReadToken(token) as JwtSecurityToken;
if (jwtToken == null)
return null;
var symmetricKey = Convert.FromBase64String(Secret);
var validationParameters = new TokenValidationParameters()
{
RequireExpirationTime = true,
ValidateIssuer = false,
ValidateAudience = false,
IssuerSigningKey = new SymmetricSecurityKey(symmetricKey)
};
SecurityToken securityToken;
var principal = tokenHandler.ValidateToken(token, validationParameters, out securityToken);
return principal;
}
catch (Exception)
{
//should write log
return null;
}
}
Jika token JWT divalidasi dan kepala sekolah kembali, Anda harus membangun identitas lokal baru dan memasukkan lebih banyak informasi ke dalamnya untuk memeriksa otorisasi peran.
Ingatlah untuk menambahkan config.Filters.Add(new AuthorizeAttribute());
(otorisasi default) pada lingkup global untuk mencegah permintaan anonim ke sumber daya Anda.
Anda dapat menggunakan tukang pos untuk menguji demo:
Permintaan token (naif seperti yang saya sebutkan di atas, hanya untuk demo):
GET http://localhost:{port}/api/token?username=cuong&password=1
Masukkan token JWT di header untuk permintaan resmi, contoh:
GET http://localhost:{port}/api/value
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6ImN1b25nIiwibmJmIjoxNDc3NTY1MjU4LCJleHAiOjE0Nzc1NjY0NTgsImlhdCI6MTQ3NzU2NTI1OH0.dSwwufd4-gztkLpttZsZ1255oEzpWCJkayR_4yvNL1s
Demo diletakkan di sini: https://github.com/cuongle/WebApi.Jwt
hmac = new HMACSHA256();var key = Convert.ToBase64String(hmac.Key);
Saya telah berhasil mencapainya dengan upaya minimal (sesederhana dengan ASP.NET Core).
Untuk itu saya menggunakan
Startup.cs
file danMicrosoft.Owin.Security.Jwt
perpustakaan OWIN .Agar aplikasi berhasil,
Startup.cs
kita perlu mengubahWeb.config
:Begini caranya
Startup.cs
:Banyak dari Anda menggunakan ASP.NET Core saat ini, sehingga Anda dapat melihatnya tidak berbeda jauh dari yang kami miliki di sana.
Itu benar-benar membuat saya bingung pertama, saya mencoba menerapkan penyedia kustom, dll. Tapi saya tidak berharap itu menjadi sangat sederhana.
OWIN
hanya bebatuan!Hanya satu hal lagi - setelah saya mengaktifkan pustaka OWIN Startup
NSWag
berhenti bekerja untuk saya (mis. Beberapa dari Anda mungkin ingin secara otomatis membuat proxy HTTP skrip naskah untuk aplikasi Angular).Solusinya juga sangat sederhana - saya diganti
NSWag
denganSwashbuckle
dan tidak memiliki masalah lebih lanjut.Oke, sekarang bagikan
ConfigHelper
kode:Aspek penting lainnya - Saya mengirim JWT Token melalui tajuk Otorisasi , sehingga kode skrip mencari saya sebagai berikut:
(kode di bawah ini dihasilkan oleh NSWag )
Lihat bagian tajuk -
"Authorization": "Bearer " + localStorage.getItem('token')
sumber
I replaced NSWag with Swashbuckle and didn't have any further issues.
Apakah Swashbuckle memiliki kemampuan untuk menghasilkan file skrip atau apakah itu sesuatu yang Anda tambahkan sendiri?Berikut adalah implementasi Otentikasi berbasis Klaim yang sangat minimal dan aman menggunakan token JWT dalam API Web Inti ASP.NET.
pertama-tama, Anda perlu mengekspos titik akhir yang mengembalikan token JWT dengan klaim yang ditetapkan untuk pengguna:
sekarang Anda perlu Menambahkan Otentikasi ke layanan Anda di
ConfigureServices
dalam startup.cs Anda untuk menambahkan otentikasi JWT sebagai layanan otentikasi default Anda seperti ini:sekarang Anda dapat menambahkan kebijakan ke layanan otorisasi Anda seperti ini:
ALTERNATIF , Anda juga dapat (tidak perlu) mengisi semua klaim Anda dari basis data karena ini hanya akan berjalan satu kali saat startup aplikasi dan menambahkannya ke kebijakan seperti ini:
sekarang Anda dapat menempatkan filter Kebijakan pada salah satu metode yang Anda ingin diotorisasi seperti ini:
Semoga ini membantu
sumber
Saya pikir Anda harus menggunakan server pihak 3d untuk mendukung token JWT dan tidak ada dukungan JWT out of the box di WEB API 2.
Namun ada proyek OWIN untuk mendukung beberapa format token yang ditandatangani (bukan JWT). Ini berfungsi sebagai protokol OAuth yang diperkecil untuk menyediakan hanya bentuk otentikasi sederhana untuk situs web.
Anda dapat membaca lebih lanjut misalnya di sini .
Ini agak panjang, tetapi sebagian besar adalah rincian dengan pengontrol dan ASP.NET Identity yang mungkin tidak Anda perlukan sama sekali. Yang terpenting adalah
Di sana Anda dapat membaca cara mengatur titik akhir (mis. "/ Token") yang dapat Anda akses dari frontend (dan perincian tentang format permintaan).
Langkah-langkah lain memberikan perincian tentang bagaimana menghubungkan titik akhir itu ke database, dll. Dan Anda dapat memilih bagian yang Anda butuhkan.
sumber
Dalam kasus saya, JWT dibuat oleh API terpisah sehingga ASP.NET hanya perlu mendekode dan memvalidasinya. Berbeda dengan jawaban yang diterima, kami menggunakan RSA yang merupakan algoritma non-simetris, sehingga
SymmetricSecurityKey
kelas yang disebutkan di atas tidak akan berfungsi.Inilah hasilnya.
sumber