JWT Authentication ពន្យល់ងាយៗ ពី Header ដល់ Signature ASP.NET Core
JWT Authentication ពន្យល់ងាយៗ៖ ពី Header ដល់ Signature
នៅពេលអ្នកសរសេរ API ឲ្យ Frontend (React, Vue, Mobile App) ហៅប្រើ សំណួរដំបូងដែលតែងតែកើតឡើងគឺ៖ «តើ Server ដឹងយ៉ាងម៉េចថាអ្នកណាកំពុងហៅ API នេះ?»
ចម្លើយដ៏ពេញនិយមបំផុតសព្វថ្ងៃគឺ JWT (JSON Web Token)។ ក្នុងអត្ថបទនេះ យើងនឹងរៀន៖
- ភាពខុសគ្នារវាង Authentication និង Authorization
- រចនាសម្ព័ន្ធ JWT ទាំង ៣ ផ្នែក៖ Header, Payload, Signature
- Flow ពេញលេញពី Login រហូតដល់ហៅ Protected API
- របៀបអនុវត្តជាក់ស្តែងក្នុង ASP.NET Core
- Security Best Practices ដែលអ្នកមិនគួររំលង
1. Authentication ទល់នឹង Authorization
ពាក្យទាំងពីរនេះស្តាប់ទៅស្រដៀងគ្នា ប៉ុន្តែមានន័យខុសគ្នាទាំងស្រុង៖
| Authentication | Authorization | |
|---|---|---|
| សំណួរ | «តើអ្នកជានរណា?» | «តើអ្នកមានសិទ្ធិធ្វើអ្វីខ្លះ?» |
| ឧទាហរណ៍ | Login ដោយ Email + Password | មានតែ Admin ទេដែលអាចលុប User |
| HTTP Status ពេលបរាជ័យ | 401 Unauthorized |
403 Forbidden |
“[!TIP]
ចាំងាយៗ៖ Authentication ដូចការបង្ហាញអត្តសញ្ញាណប័ណ្ណនៅមាត់ទ្វារអគារ។ Authorization ដូចកាតចូលដែលកំណត់ថាអ្នកអាចចូលជាន់ណាខ្លះ។
2. JWT ជាអ្វី?
JWT គឺជា String មួយដែល Server បង្កើតឡើងបន្ទាប់ពី User Login ជោគជ័យ។ Client រក្សាទុក Token នេះ ហើយផ្ញើវាមកជាមួយរាល់ Request បន្ទាប់ៗ ដើម្បីបញ្ជាក់ថា «ខ្ញុំបាន Login រួចហើយ»។
ចំណុចពិសេសរបស់ JWT គឺវាជា Stateless — មានន័យថា Server មិនចាំបាច់រក្សាទុក Session ក្នុង Memory ឬ Database ទេ។ ព័ត៌មានដែលត្រូវការទាំងអស់ស្ថិតនៅក្នុង Token ខ្លួនឯង ហើយ Server គ្រាន់តែផ្ទៀងផ្ទាត់ Signature ប៉ុណ្ណោះ។
JWT មួយមើលទៅដូចនេះ (បំបែកជា ៣ ផ្នែកដោយសញ្ញា .)៖
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjMiLCJuYW1lIjoiRGFyYSIsInJvbGUiOiJBZG1pbiJ9.Xk3p9Qz...
└─────────── Header ──────────────┘ └──────────────────── Payload ────────────────────────────┘ └─ Signature ─┘3. រចនាសម្ព័ន្ធ JWT ទាំង ៣ ផ្នែក
🔹 Header
ប្រាប់ថា Token នេះប្រើ Algorithm អ្វីសម្រាប់ចុះហត្ថលេខា៖
{
"alg": "HS256",
"typ": "JWT"
}🔹 Payload
ផ្ទុកទិន្នន័យអំពី User ដែលហៅថា Claims៖
{
"sub": "123",
"name": "Dara",
"role": "Admin",
"iat": 1759641600,
"exp": 1759642500
}Claims ដែលគេប្រើញឹកញាប់៖
sub(Subject) — ID របស់ Useriat(Issued At) — ពេលវេលាបង្កើត Tokenexp(Expiration) — ពេលវេលាដែល Token ផុតកំណត់iss/aud— អ្នកចេញ Token និងអ្នកដែល Token នេះសម្រាប់
“[!WARNING]
Payload ត្រឹមតែត្រូវបាន Base64Url Encode ប៉ុណ្ណោះ — មិនមែន Encrypt ទេ! អ្នកណាក៏អាចយក Token ទៅ Decode មើលបានដែរ (សាកល្បងនៅ jwt.io)។ កុំដាក់ Password, លេខកាតធនាគារ ឬទិន្នន័យសម្ងាត់ក្នុង Payload ជាដាច់ខាត។
🔹 Signature
នេះជាផ្នែកដែលធ្វើឲ្យ JWT មានសុវត្ថិភាព។ Server យក Header និង Payload មកបញ្ចូលគ្នា រួចចុះហត្ថលេខាដោយប្រើ Secret Key ដែលមានតែ Server ទេដែលដឹង៖
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secretKey
)ប្រសិនបើ Hacker ព្យាយាមកែ Payload (ឧ. ប្តូរ "role": "User" ទៅជា "Admin") នោះ Signature នឹងលែងត្រូវគ្នា ហើយ Server នឹងបដិសេធ Token នោះភ្លាមៗ។
4. JWT Authentication Flow
┌──────────┐ ┌──────────┐
│ Client │ │ Server │
└────┬─────┘ └────┬─────┘
│ ① POST /api/auth/login │
│ { email, password } │
│ ───────────────────────────────────────────▶ │
│ │ ② ផ្ទៀងផ្ទាត់ Password
│ │ ③ បង្កើត JWT + Sign ដោយ Secret Key
│ ④ 200 OK { accessToken: "eyJ..." } │
│ ◀─────────────────────────────────────────── │
│ │
│ ⑤ GET /api/profile │
│ Authorization: Bearer eyJ... │
│ ───────────────────────────────────────────▶ │
│ │ ⑥ Validate Signature
│ │ + ពិនិត្យ exp, iss, aud
│ ⑦ 200 OK { profile data } │
│ ◀─────────────────────────────────────────── │
│ │
│ (Token ខុស ឬផុតកំណត់ → 401 Unauthorized) │“[!NOTE]
Token ត្រូវផ្ញើក្នុង HTTP Header ឈ្មោះAuthorizationដោយមានពាក្យBearerនៅពីមុខ៖Authorization: Bearer <token>
5. អនុវត្តក្នុង ASP.NET Core
ជំហានទី ១៖ ដំឡើង Package
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearerជំហានទី ២៖ កំណត់ Config ក្នុង appsettings.json
{
"Jwt": {
"Issuer": "https://api.myapp.com",
"Audience": "https://myapp.com",
"Key": "ប្រើ-Key-វែងយ៉ាងហោចណាស់-32-តួអក្សរ-ហើយទុកក្នុង-Secret-Manager"
}
}“[!CAUTION]
កុំ Commit Secret Key ពិតប្រាកដចូល Git។ ក្នុង Production សូមប្រើ Environment Variables, User Secrets ឬ Azure Key Vault។
ជំហានទី ៣៖ Configure Authentication ក្នុង Program.cs
using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = builder.Configuration["Jwt:Issuer"],
ValidAudience = builder.Configuration["Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]!)),
ClockSkew = TimeSpan.FromSeconds(30)
};
});
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseAuthentication(); // ត្រូវតែនៅមុន UseAuthorization
app.UseAuthorization();ជំហានទី ៤៖ បង្កើត Token ពេល Login
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.IdentityModel.Tokens;
public class TokenService(IConfiguration config)
{
public string CreateToken(User user)
{
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, user.Id.ToString()),
new Claim(JwtRegisteredClaimNames.Email, user.Email),
new Claim(ClaimTypes.Role, user.Role),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config["Jwt:Key"]!));
var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: config["Jwt:Issuer"],
audience: config["Jwt:Audience"],
claims: claims,
expires: DateTime.UtcNow.AddMinutes(15),
signingCredentials: credentials);
return new JwtSecurityTokenHandler().WriteToken(token);
}
}ជំហានទី ៥៖ ការពារ Endpoint (Authentication + Authorization)
// User ណាដែល Login រួចអាចចូលបាន (Authentication)
app.MapGet("/api/profile", (ClaimsPrincipal user) =>
{
var userId = user.FindFirstValue(ClaimTypes.NameIdentifier);
return Results.Ok(new { userId });
})
.RequireAuthorization();
// មានតែ Role "Admin" ទេដែលអាចលុបបាន (Authorization)
app.MapDelete("/api/users/{id:int}", (int id) => Results.NoContent())
.RequireAuthorization(policy => policy.RequireRole("Admin"));ប្រសិនបើប្រើ Controller អ្នកអាចប្រើ Attribute [Authorize] ឬ [Authorize(Roles = "Admin")] ជំនួសវិញបាន។
6. Security Best Practices
“[!IMPORTANT]
JWT មានសុវត្ថិភាពលុះត្រាតែអ្នកប្រើវាត្រឹមត្រូវ។ ខាងក្រោមជាចំណុចសំខាន់ៗ៖
- ✅ កំណត់
expឲ្យខ្លី (ឧ. ១៥ នាទី) សម្រាប់ Access Token ហើយប្រើ Refresh Token ដើម្បីស្នើ Token ថ្មី - ✅ ប្រើ HTTPS ជានិច្ច — Token ដែលលួចបាន = អត្តសញ្ញាណដែលលួចបាន
- ✅ Validate គ្រប់ចំណុច៖ Signature,
exp,iss,aud - ✅ ប្រើ Secret Key វែង និងរឹងមាំ ឬប្រើ Asymmetric Algorithm (
RS256) នៅពេលមាន Service ច្រើន - ✅ ពិចារណាទុក Token ក្នុង
HttpOnlyCookie ដើម្បីកាត់បន្ថយហានិភ័យ XSS ជាជាងទុកក្នុងlocalStorage - ❌ កុំដាក់ទិន្នន័យសម្ងាត់ក្នុង Payload
- ❌ កុំទទួលយក
alg: noneឬអនុញ្ញាតឲ្យ Client ជ្រើសរើស Algorithm ដោយខ្លួនឯង
“[!WARNING]
ដោយសារ JWT ជា Stateless វា មិនអាច Revoke (លុបចោល) ភ្លាមៗបានទេ មុនពេលវាផុតកំណត់។ ប្រសិនបើប្រព័ន្ធរបស់អ្នកត្រូវការ Logout ភ្លាមៗ ឬបិទ Account អ្នកអាចប្រើ Token Blacklist (ឧ. ទុកjtiក្នុង Redis) ឬកំណត់expឲ្យខ្លី។
7. JWT ទល់នឹង Session-based Authentication
| លក្ខណៈ | Session (Cookie) | JWT |
|---|---|---|
| កន្លែងទុកទិន្នន័យ | Server (Memory / Redis / DB) | ក្នុង Token ខ្លួនឯង |
| Scalability | ត្រូវការ Shared Session Store | ងាយ Scale (Stateless) |
| Revoke ភ្លាមៗ | ✅ ងាយ | ⚠️ ពិបាក |
| Mobile App / Microservices | មិនសូវស័ក្តិសម | ✅ ស័ក្តិសមណាស់ |
| ទំហំ Request | តូច (Session ID) | ធំជាង (Token ពេញ) |
“[!TIP]
គ្មានវិធីណាល្អជាងគេដាច់ខាតទេ។ សម្រាប់ Web App តែមួយធម្មតា Session នៅតែល្អណាស់។ JWT ភ្លឺស្វាងនៅពេលអ្នកមាន SPA, Mobile App ឬ Microservices ច្រើនដែលត្រូវផ្ទៀងផ្ទាត់ User ដូចគ្នា។
8. សង្ខេប
- Authentication = អ្នកជានរណា | Authorization = អ្នកមានសិទ្ធិធ្វើអ្វី
- JWT មាន ៣ ផ្នែក៖ Header (Algorithm), Payload (Claims), Signature (ការពារការកែប្រែ)
- Payload អាចអានបាន ដោយអ្នកណាក៏បាន — Signature គ្រាន់តែការពារកុំឲ្យ កែប្រែ ប៉ុណ្ណោះ
- ក្នុង ASP.NET Core ប្រើ
AddJwtBearer()+RequireAuthorization()គឺអាចការពារ API បានយ៉ាងងាយ - កំណត់
expខ្លី ប្រើ HTTPS និង Refresh Token ដើម្បីសុវត្ថិភាពល្អបំផុត
💬 សំណួរសម្រាប់អ្នក៖ តើ Project របស់អ្នកប្រើ JWT ឬ Session? ហើយអ្នកទុក Token នៅកន្លែងណា? ចែករំលែកនៅក្នុង Comment ខាងក្រោម!
📌 អត្ថបទបន្ទាប់៖ Refresh Token Rotation ក្នុង ASP.NET Core — របៀបធ្វើឲ្យ User Login យូរដោយមិនបាត់បង់សុវត្ថិភាព