iT邦幫忙

2023 iThome 鐵人賽

DAY 14
0
Cloud Native

The Journey of ASP.NET and Beyond系列 第 14

Web API Practice: User Management

  • 分享至 

  • xImage
  •  

Begin your journey by creating an account. Registration is a quick and simple process that opens the door to a world of possibilities.

  是日將製作使用者相關之API實務,其含使用者之註冊、使用者之驗證、使用者之登入等,然而在其開始前,將先說明此次之密碼加密實務。

密碼加密 Encryption


  在註冊時,使用者輸入密碼並註冊後,通常情況並不建議以明文儲存密碼,此次實務亦然。以下將介紹此次密碼加密方式,其一,先建立檔案,名「PasswordHelper」,其二如下,其三,於program.cs中註冊,以builder.Services.AddSingleton<PasswordHelper>();

private readonly string key;
public PasswordHelper(IConfiguration configuration)
{
    // 自行創立之HashKey,於appsettings.json中
    key = configuration.GetValue<string>("HashKey");
}
// 進行密碼加密
public string HashPassword(string password)
{
    // 將key轉換為 UTF-8 編碼的字節陣列
    byte[] keyByte = new UTF8Encoding().GetBytes(key);
    byte[] passwordByte = new UTF8Encoding().GetBytes(password);
    // 以HMACSHA256加密
    using( var hmacSHA256 = new HMACSHA256(keyByte))
    {
        // 密碼之加密
        byte[] hashedPassword = hmacSHA256.ComputeHash(passwordByte);
        // 將加密之密碼轉換為十六進制表示形式的字符串
        return BitConverter.ToString(hashedPassword).Replace("-", "").ToLower();
    }
    
}

  在登入時,使用者輸入密碼並登入後,須將其加密在與資料庫中加密之密碼進行比對:

public bool VerifyPassword (string password, string hashedpassword)
{
    if (HashPassword(password).Equals(hashedpassword))
    {
        return true;
    }
    return false;
}

使用者控制區 User Controller


  因皆為使用者之操作事項,因此將註冊、驗證、登入等寫在同一控制區中,並使用服務區之功能,在Services之資料夾中建立UserService.cs之檔案,並於Program.cs中以builder.Services.AddScoped<UserService>();進行註冊,並於控制區中啟用該服務,其一為控制區,其二為服務區,以下不再贅述。

[ApiController]
[Route("user")]
public class UserController : ControllerBase
{
    private readonly UserService _userService;
    public UserController(UserService userService)
    {
        _userService = userService;
    }
}
public class UserService
{
    private readonly DnDContext _context;
    private readonly IMapper _mapper;
    private readonly PasswordHelper _passwordHelper;
    public UserService(DnDContext context, IMapper mapper, PasswordHelper passwordHelper)
    {
        _context = context;
        _mapper = mapper;
        _passwordHelper = passwordHelper;
    }

    JsonResult success = new JsonResult(new { status = "註冊成功" });
    JsonResult fail = new JsonResult(new { status = "註冊失敗" });
}

註冊 Sign up


  許多網站之第一步為註冊帳號,以記錄使用者之資料,並可解鎖許多網站之功能。以下為實務內容:

[HttpPost]
public async Task<IActionResult> Register(RegisterDto register)
{
    if (register == null)
    {
        return BadRequest("檢查輸入資料");
    }
    var result = await _userService.Register(register);

    return result;
}
public async Task<IActionResult> Register(RegisterDto register)
{
    // 確認無重複使用者名稱
    var user = _context.UserProfile.SingleOrDefault(e => e.Username == register.Username);
    if (user != null) return fail;
    var profile = new UserProfile()
    {
        Password = _passwordHelper.HashPassword(register.Password)
    };
    profile = _mapper.Map(register, profile);
    
    // 新增使用這資訊
    var info = new UserInfo()
    {
        Uuid = profile.Uuid
    };
    // 新增使用者驗證
    var verification = new Verification()
    {
        Uuid = profile.Uuid,
        VerificationCode = new Random().Next(0, 999999),
    };
    // 傳送驗證碼
    var verifyMail = new MailData()
    {
        Name = profile.Username,
        Email = profile.Email,
        Subject = "Verify your account",
        Body = $"Dear {profile.Username},\n" +
        $"This your Verification Code: {verification.VerificationCode}"
    };
    _mailHelper.SendMail(verifyMail);
    _context.AddRange(profile, info, verification);

    try
    {
        await _context.SaveChangesAsync();
    }
    catch (Exception ex)
    {
        return fail;
    }

    return success;

}

驗證 Verify


  註冊完後,通常應驗證使用者之資料是否為本人註冊,或以驗證碼開通帳號之各項功能。以下為實務內容:

[HttpPost("verify")]
public async Task<IActionResult> Verify(int VerificationCode)
{
    if(VerificationCode == 0)
    {
        return BadRequest();
    }
    var result = await _userService.Verify(VerificationCode);
    return result;
}
public async Task<IActionResult> Verify(int VerificationCode)
{
    var user = _context.Verifications.Include(e => e.UserProfile).SingleOrDefault(e => e.VerificationCode.Equals(VerificationCode));
    if (user == null) return not_found;

    user.UserProfile.Verified = true;
    _context.UserProfile.Update(user.UserProfile);
    try
    {
        await _context.SaveChangesAsync();
    }
    catch (Exception ex)
    {
        return fail;
    }
    return success;
}

登入 Sign in


  完成註冊,並通過驗證,便可進行登入,登入可以使用者之身分進行各項事務,如創建角色之功能。以下為實務內容,使用者登入後便可獲得bearer Token以便進行驗證、授權:

[HttpPost("login")]
public async Task<IActionResult> Login(LoginDto login)
{
    if (login == null)
    {
        return BadRequest();
    }
    var result = await _userService.Login(login);
    return result;
}
public async Task<IActionResult> Login(LoginDto login)
{
    var unverified = new JsonResult(new { status = "unverified" });
    var user = _context.UserProfile.SingleOrDefault(e => e.Email.Equals(login.Email));
    // 確認使用者存在
    if (user == null) return not_found;
    // 確認使用者已驗證
    if (!user.Verified) return unverified;
    // 比對使用者密碼
    if (!_passwordHelper.VerifyPassword(login.Password, user.Password)) return fail;
    var token = _jwtHelper.GenerateToken(user.Uuid, user.Username, "user");
    var result = new JsonResult(new {status = "success", token});
    return result;
}

上一篇
Web API Practice: Essentials For the Initial
下一篇
Web API Practice: Character Sheet API
系列文
The Journey of ASP.NET and Beyond30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言