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; } }