Bảo mật
Authentication (Xác thực)
JWT (JSON Web Token)
JWT là một chuẩn token để truyền thông tin an toàn giữa các parties dưới dạng JSON.
Cấu trúc JWT
┌─────────────────────────────────────────────────────────────────────┐
│ JWT STRUCTURE │
├─────────────────┬─────────────────┬───────────────────────────────┤
│ HEADER │ PAYLOAD │ SIGNATURE │
│ (Base64URL) │ (Base64URL) │ (Base64URL) │
├─────────────────┼─────────────────┼───────────────────────────────┤
│ { │ { │ HmacSHA256( │
│ "alg": │ "sub": │ header + "." + payload, │
│ "HS256", │ "1234567890",│ secret_key │
│ "typ": │ "name": │ ) │
│ "JWT" │ "John Doe", │ │
│ } │ "iat": │ │
│ │ 1516239022, │ │
│ │ "exp": │ │
│ │ 1516242622 │ │
│ │ } │ │
└─────────────────┴─────────────────┴───────────────────────────────┘
Cấu hình JwtBearer trong .NET Core
// Program.cs
var builder = WebApplication.CreateBuilder(args);
// Add Authentication
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = 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:SecretKey"]))
};
});
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
Tạo JWT Token
public class JwtService
{
private readonly JwtSettings _settings;
public string GenerateToken(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(_settings.SecretKey));
var credentials = new SigningCredentials(
key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: _settings.Issuer,
audience: _settings.Audience,
claims: claims,
expires: DateTime.UtcNow.AddMinutes(_settings.ExpiryMinutes),
signingCredentials: credentials);
return new JwtSecurityTokenHandler().WriteToken(token);
}
}
Authorization (Phân quyền)
[Authorize] Attribute
// Yêu cầu authenticate
[Authorize]
[HttpGet("profile")]
public IActionResult GetProfile() { }
// Yêu cầu role cụ thể
[Authorize(Roles = "Admin")]
[HttpGet("admin")]
public IActionResult GetAdminData() { }
// Yêu cầu nhiều roles (AND logic)
[Authorize(Roles = "Admin,Manager")]
[HttpGet("manage")]
public IActionResult Manage() { }
// Yếu tố OR - dùng Policy
[Authorize(Policy = "AdminOrManager")]
[HttpGet("manage")]
public IActionResult Manage() { }
Role-based Authorization
[Authorize(Roles = "Admin")]
public class AdminController : ControllerBase
{
[HttpGet("users")]
public IActionResult GetAllUsers()
{
// Only admins can access
return Ok();
}
}
Policy-based Authorization
// Đăng ký policy trong Program.cs
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AdultOnly", policy =>
policy.RequireClaim("Age", "18", "19", "20", "21", "22", "23", "24", "25"));
options.AddPolicy("PremiumUser", policy =>
policy.RequireAssertion(context =>
context.User.HasClaim(c => c.Type == "Subscription" &&
c.Value == "Premium")));
options.AddPolicy("CanDeleteProduct", policy =>
policy.RequireAssertion(context =>
context.User.IsInRole("Admin") ||
(context.User.IsInRole("Manager") &&
context.User.HasClaim(c => c.Type == "CanDelete"))));
});
// Sử dụng
[Authorize(Policy = "PremiumUser")]
[HttpGet("premium-content")]
public IActionResult GetPremiumContent() { }
CORS (Cross-Origin Resource Sharing)
Vấn đề
Browser chặn requests từ một domain khác với server (cross-origin requests) vì lý do bảo mật. CORS cho phép server chỉ định origins nào được phép truy cập.
Cấu hình CORS
// Program.cs
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowFrontend", policy =>
{
policy.WithOrigins("http://localhost:3000", "https://myapp.com")
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials(); // Chỉ dùng với specific origins
});
options.AddPolicy("AllowAll", policy =>
{
policy.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod();
});
});
var app = builder.Build();
app.UseCors("AllowFrontend");
CORS với named policy
[ApiController]
[Route("api/[controller]")]
[EnableCors("AllowFrontend")]
public class ProductsController : ControllerBase
{
[HttpGet]
public IActionResult Get() => Ok();
[HttpGet]
[DisableCors] // Disable CORS cho action cụ thể
public IActionResult GetSecret() => Ok();
}
Preflight Request
┌─────────────────────────────────────────────────────────────────────┐
│ CORS REQUEST FLOW │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Browser │
│ │ │
│ │ 1. OPTIONS /api/products │
│ │ Access-Control-Request-Method: GET │
│ │ Access-Control-Request-Headers: Content-Type │
│ ├────────────────────────────────────────────────────────► │
│ │◄────────────────────────────────────────────────────────┤ │
│ │ 2. 200 OK │
│ │ Access-Control-Allow-Origin: http://localhost:3000 │
│ │ Access-Control-Allow-Methods: GET, POST, PUT, DELETE │
│ │ Access-Control-Allow-Headers: Content-Type │
│ │ │
│ │ 3. GET /api/products │
│ ├────────────────────────────────────────────────────────► │
│ │◄────────────────────────────────────────────────────────┤ │
│ │ 4. 200 OK │
│ │ Access-Control-Allow-Origin: http://localhost:3000 │
│ │
└─────────────────────────────────────────────────────────────────────┘