功能需求
OAuth2 驗證
使用 Bearer Token 作為身份驗證方式。雙因素驗證 (2FA)
用戶啟用 2FA 後,必須輸入 OTP(一次性密碼)以完成身份驗證。數據存儲
使用 EF6 操作用戶及其 2FA 狀態。
1. 準備環境
建立 ASP.NET Web API 專案
- 選擇
.NET Framework
的 Web API 模板。
- 選擇
安裝 NuGet 套件
EntityFramework
:數據庫 ORM。Microsoft.Owin.Security.OAuth
:支持 OAuth2 驗證。OtpNet
:生成和驗證 OTP。
2. 資料結構
用戶模型
csharpusing System.ComponentModel.DataAnnotations;
public class User
{
[Key]
public string UserId { get; set; } // 用戶唯一 ID
[Required]
public string PasswordHash { get; set; } // 密碼哈希
public string SecretKey { get; set; } // 2FA 密鑰
public bool TwoFactorEnabled { get; set; } // 是否啟用 2FA
}
DbContext 類
csharpusing System.Data.Entity;
public class TwoFactorDbContext : DbContext
{
public TwoFactorDbContext() : base("name=TwoFactorDb") // Web.config 中的連接字符串
{
}
public DbSet<User> Users { get; set; } // 用戶表
}
3. 配置 OAuth2 Bearer Token
Startup.cs
csharpusing Microsoft.Owin;
using Microsoft.Owin.Security.OAuth;
using Owin;
using System;
[assembly: OwinStartup(typeof(YourNamespace.Startup))]
public class Startup
{
public void Configuration(IAppBuilder app)
{
var OAuthOptions = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true, // 開發環境允許 HTTP(生產環境應使用 HTTPS)
TokenEndpointPath = new PathString("/token"), // Token 發行端點
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
Provider = new OAuthProvider() // 自定義驗證邏輯
};
app.UseOAuthAuthorizationServer(OAuthOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
OAuthProvider
csharpusing Microsoft.Owin.Security;
using Microsoft.Owin.Security.OAuth;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
public class OAuthProvider : OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated(); // 不進行客戶端驗證
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
using (var dbContext = new TwoFactorDbContext())
{
var user = dbContext.Users.SingleOrDefault(u => u.UserId == context.UserName);
if (user == null || !VerifyPassword(user.PasswordHash, context.Password))
{
context.SetError("invalid_grant", "The username or password is incorrect.");
return;
}
// 檢查是否啟用了 2FA
if (user.TwoFactorEnabled)
{
context.SetError("2fa_required", "Two-factor authentication is required.");
context.Response.Headers.Add("X-2FA", new[] { "true" }); // 標記需要 2FA
return;
}
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, user.UserId));
context.Validated(new AuthenticationTicket(identity, new AuthenticationProperties()));
}
}
private bool VerifyPassword(string storedPasswordHash, string inputPassword)
{
// 替換為你的密碼哈希驗證邏輯
return storedPasswordHash == inputPassword; // 示例,請勿直接使用明文驗證
}
}
4. 2FA 流程
生成和驗證 OTP
使用 OtpNet 生成和驗證 OTP:
csharpusing OtpNet;
public class TwoFactorService
{
// 生成隨機密鑰
public string GenerateSecretKey()
{
var key = KeyGeneration.GenerateRandomKey(20);
return Base32Encoding.ToString(key); // 編碼為 Base32 格式
}
// 生成 OTP
public string GenerateOtp(string secretKey)
{
var totp = new Totp(Base32Encoding.ToBytes(secretKey));
return totp.ComputeTotp(); // 基於時間生成 OTP
}
// 驗證 OTP
public bool ValidateOtp(string secretKey, string userInput)
{
var totp = new Totp(Base32Encoding.ToBytes(secretKey));
return totp.VerifyTotp(userInput, out _); // 驗證用戶輸入
}
}
2FA API
用於驗證 OTP 並發放完整的 Bearer Token:
csharpusing System.Linq;
using System.Web.Http;
[RoutePrefix("api/twofactor")]
public class TwoFactorController : ApiController
{
private readonly TwoFactorService _twoFactorService = new TwoFactorService();
[HttpPost]
[Route("validate")]
public IHttpActionResult ValidateTwoFactor([FromBody] TwoFactorRequest request)
{
using (var dbContext = new TwoFactorDbContext())
{
var user = dbContext.Users.SingleOrDefault(u => u.UserId == request.UserId);
if (user == null)
{
return BadRequest("User not found.");
}
if (!_twoFactorService.ValidateOtp(user.SecretKey, request.Otp))
{
return BadRequest("Invalid OTP.");
}
// 發放完整的 Bearer Token
var identity = new ClaimsIdentity(OAuthDefaults.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, user.UserId));
var ticket = new AuthenticationTicket(identity, new AuthenticationProperties());
var accessToken = Startup.OAuthOptions.AccessTokenFormat.Protect(ticket);
return Ok(new { AccessToken = accessToken });
}
}
}
public class TwoFactorRequest
{
public string UserId { get; set; }
public string Otp { get; set; }
}
5. API 流程
獲取初步 Token:
- URL:
POST /token
- 如果用戶啟用了 2FA,會返回
2fa_required
錯誤及X-2FA
標頭。
- URL:
驗證 OTP:
- URL:
POST /api/twofactor/validate
- 提交
UserId
和 OTP,獲取完整的 Bearer Token。
- URL:
使用 Token 訪問受保護的 API。
6. 改進建議
密碼哈希
使用安全哈希(如 PBKDF2 或 BCrypt)存儲用戶密碼。HTTPS 強制
生產環境中強制使用 HTTPS 確保數據傳輸安全。Token 有效期管理
配置 Refresh Token 或縮短 Access Token 有效期以提升安全性。