flower-story/Server/Controller/BaseController.cs
2023-05-23 22:08:56 +08:00

136 lines
4.2 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Blahblah.FlowerStory.Server.Data;
using Blahblah.FlowerStory.Server.Data.Model;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Security.Cryptography;
using System.Text;
namespace Blahblah.FlowerStory.Server.Controller;
/// <summary>
/// 基础服务抽象类
/// </summary>
public abstract class BaseController : ControllerBase
{
private const string Salt = "Blah blah, o! Flower story, intimately help you record every bit of the garden.";
/// <summary>
/// 自定义认证头的关键字
/// </summary>
protected const string AuthHeader = "X-Auth";
/// <summary>
/// 禁用用户
/// </summary>
protected const int UserDisabled = -1;
/// <summary>
/// 普通用户
/// </summary>
protected const int UserCommon = 0;
/// <summary>
/// 管理员用户
/// </summary>
protected const int UserAdmin = 99;
/// <summary>
/// 数据库对象
/// </summary>
protected readonly FlowerDatabase database;
/// <summary>
/// 日志对象
/// </summary>
protected readonly ILogger<BaseController>? logger;
/// <summary>
/// 构造基础服务类
/// </summary>
/// <param name="database">数据库对象</param>
/// <param name="logger">日志对象</param>
protected BaseController(FlowerDatabase database, ILogger<BaseController>? logger = null)
{
this.database = database;
this.logger = logger;
}
/// <summary>
/// 计算密码的 hash
/// </summary>
/// <param name="password">密码原文</param>
/// <param name="id">用户 id</param>
/// <returns>密码 hash值为 SHA256(password+id+salt)</returns>
/// <exception cref="ArgumentNullException"></exception>
protected string HashPassword(string password, string id)
{
if (string.IsNullOrEmpty(password))
{
throw new ArgumentNullException(nameof(password));
}
if (string.IsNullOrEmpty(id))
{
throw new ArgumentNullException(nameof(id));
}
var data = Encoding.UTF8.GetBytes($"{password}{id}{Salt}");
var hash = SHA256.HashData(data);
return Convert.ToHexString(hash);
}
/// <summary>
/// 检查当前会话权限
/// </summary>
/// <param name="level">需要大于等于该权限,默认为 0 - UserCommon</param>
/// <returns>若检查失败,第一个参数返回异常 ActionResult。第二个参数返回会话对应的用户对象</returns>
protected (ActionResult? Result, UserItem? User) CheckPermission(int? level = 0)
{
if (!Request.Headers.TryGetValue(AuthHeader, out var h))
{
logger?.LogWarning("request with no {auth} header", AuthHeader);
return (BadRequest(), null);
}
string hash = h.ToString();
var token = database.Tokens.Find(hash);
if (token == null)
{
logger?.LogWarning("token \"{hash}\" not found", hash);
return (Unauthorized(), null);
}
if (token.ExpireDate < DateTimeOffset.UtcNow)
{
logger?.LogWarning("token \"{hash}\" has expired after {date}", hash, token.ExpireDate);
return (Unauthorized(), null);
}
var user = database.Users.Find(token.UserId);
if (user == null)
{
logger?.LogWarning("user not found with id {id}", token.UserId);
return (NotFound(), null);
}
if (user.Level < level)
{
logger?.LogWarning("user \"{id}\" level ({level}) lower than required ({required})", user.UserId, user.Level, level);
return (Forbid(), user);
}
token.ActiveDateUnixTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
database.Tokens.Update(token);
return (null, user);
}
/// <summary>
/// 保存数据库变动并输出日志
/// </summary>
/// <returns></returns>
protected int SaveDatabase()
{
var count = database.SaveChanges();
if (count > 0)
{
logger?.LogInformation("{number} of entries written to database.", count);
}
else
{
logger?.LogWarning("no data written to database.");
}
return count;
}
}