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;
///
/// 基础服务抽象类
///
public abstract class BaseController : ControllerBase
{
private const string Salt = "Blah blah, o! Flower story, intimately help you record every bit of the garden.";
///
/// 自定义认证头的关键字
///
protected const string AuthHeader = "X-Auth";
///
/// 禁用用户
///
protected const int UserDisabled = -1;
///
/// 普通用户
///
protected const int UserCommon = 0;
///
/// 管理员用户
///
protected const int UserAdmin = 99;
///
/// 数据库对象
///
protected readonly FlowerDatabase database;
///
/// 日志对象
///
protected readonly ILogger? logger;
///
/// 构造基础服务类
///
/// 数据库对象
/// 日志对象
protected BaseController(FlowerDatabase database, ILogger? logger = null)
{
this.database = database;
this.logger = logger;
}
///
/// 计算密码的 hash
///
/// 密码原文
/// 用户 id
/// 密码 hash,值为 SHA256(password+id+salt)
///
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);
}
///
/// 检查当前会话权限
///
/// 需要大于等于该权限,默认为 0 - UserCommon
/// 若检查失败,第一个参数返回异常 ActionResult。第二个参数返回会话对应的用户对象
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);
}
///
/// 保存数据库变动并输出日志
///
///
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;
}
}