228 lines
7.7 KiB
C#
228 lines
7.7 KiB
C#
using Blahblah.FlowerStory.Server.Data;
|
|
using Blahblah.FlowerStory.Server.Data.Model;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
|
|
namespace Blahblah.FlowerStory.Server.Controller
|
|
{
|
|
/// <summary>
|
|
/// 用户会话相关服务
|
|
/// </summary>
|
|
[ApiController]
|
|
[Produces("application/json")]
|
|
[Route("users")]
|
|
public partial class UserController : BaseController
|
|
{
|
|
/// <summary>
|
|
/// 构造用户会话服务
|
|
/// </summary>
|
|
/// <param name="db">数据库对象</param>
|
|
/// <param name="logger">日志对象</param>
|
|
public UserController(FlowerDatabase db, ILogger<UserController> logger) : base(db, logger)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// 用户登录
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// 提交示例:
|
|
///
|
|
/// POST /users/auth
|
|
/// {
|
|
/// "id": "blahblah",
|
|
/// "password": "pwd123"
|
|
/// }
|
|
///
|
|
/// </remarks>
|
|
/// <param name="login">登录参数</param>
|
|
/// <returns>成功登录则返回自定义认证头</returns>
|
|
/// <response code="200">返回自定义认证头</response>
|
|
/// <response code="401">认证失败</response>
|
|
/// <response code="404">未找到用户</response>
|
|
[Route("auth")]
|
|
[Consumes("application/json")]
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
[HttpPost]
|
|
public ActionResult Authenticate([FromBody] LoginParamter login)
|
|
{
|
|
#if DEBUG
|
|
logger?.LogInformation("user \"{user}\" try to login with password \"{password}\"", login.Id, login.Password);
|
|
#endif
|
|
var user = database.Users.FirstOrDefault(u => u.UserId == login.Id);
|
|
if (user == null)
|
|
{
|
|
logger?.LogWarning("user \"{user}\" not found", login.Id);
|
|
return NotFound();
|
|
}
|
|
|
|
// comput password hash with salt
|
|
string hash = HashPassword(login.Password, login.Id);
|
|
if (hash != user.Password)
|
|
{
|
|
logger?.LogWarning("hash result {hash}, unauthorized with hash {password}", hash, user.Password);
|
|
return Unauthorized();
|
|
}
|
|
|
|
// record the session
|
|
// TODO: singleton token
|
|
var now = DateTimeOffset.UtcNow;
|
|
var expires = 1200; // 20 minutes
|
|
var token = new TokenItem
|
|
{
|
|
Id = Guid.NewGuid().ToString("N"),
|
|
UserId = user.Id,
|
|
LogonDateUnixTime = now.ToUnixTimeMilliseconds(),
|
|
ActiveDateUnixTime = now.ToUnixTimeMilliseconds(),
|
|
ExpireDateUnixTime = now.AddSeconds(expires).ToUnixTimeMilliseconds(),
|
|
ExpireSeconds = expires,
|
|
ClientApp = "browser", // TODO: support app later
|
|
ClientAgent = Request.Headers.UserAgent
|
|
};
|
|
database.Tokens.Add(token);
|
|
|
|
user.ActiveDateUnixTime = token.ActiveDateUnixTime;
|
|
database.Users.Update(user);
|
|
SaveDatabase();
|
|
|
|
Response.Headers.Add(AuthHeader, token.Id);
|
|
return Ok();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 注册用户
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// 提交示例:
|
|
///
|
|
/// POST /users/register
|
|
/// {
|
|
/// "id": "blahblah",
|
|
/// "password": "pwd123",
|
|
/// "userName": "Blah blah",
|
|
/// "email": "blah@example.com",
|
|
/// "mobile": "18012345678"
|
|
/// }
|
|
///
|
|
/// </remarks>
|
|
/// <param name="user">注册参数</param>
|
|
/// <returns>成功注册则返回已注册的用户对象</returns>
|
|
/// <response code="200">返回已注册的用户对象</response>
|
|
/// <response code="500">用户重复或其他服务器错误</response>
|
|
[Route("register")]
|
|
[Consumes("application/json")]
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
|
[HttpPost]
|
|
public ActionResult<UserItem> Register([FromBody] UserParameter user)
|
|
{
|
|
#if DEBUG
|
|
logger?.LogInformation("user register, {user}", user);
|
|
#endif
|
|
var u = database.Users.FirstOrDefault(u => u.UserId == user.Id);
|
|
if (u != null)
|
|
{
|
|
logger?.LogWarning("duplicate user \"{id}\"", user.Id);
|
|
return Problem("duplicateUser", "users/register", 500);
|
|
}
|
|
|
|
var now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
|
var item = new UserItem
|
|
{
|
|
UserId = user.Id,
|
|
Password = HashPassword(user.Password, user.Id),
|
|
Level = UserCommon,
|
|
RegisterDateUnixTime = now,
|
|
ActiveDateUnixTime = now,
|
|
Name = user.UserName,
|
|
Email = user.Email,
|
|
Mobile = user.Mobile
|
|
};
|
|
database.Users.Add(item);
|
|
SaveDatabase();
|
|
|
|
return Ok(item);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 修改用户
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// 提交示例:
|
|
///
|
|
/// POST /users/update
|
|
/// {
|
|
/// "userName": "Blah blah",
|
|
/// "email": "blah@example.com",
|
|
/// "mobile": "18012345678"
|
|
/// }
|
|
///
|
|
/// </remarks>
|
|
/// <param name="update">修改参数</param>
|
|
/// <returns>修改成功则返回已修改的用户对象</returns>
|
|
/// <response code="200">返回已修改的用户对象</response>
|
|
/// <response code="400">认证头未找到</response>
|
|
/// <response code="401">服务器未找到登录会话</response>
|
|
/// <response code="403">用户权限不足</response>
|
|
/// <response code="404">未找到关联用户</response>
|
|
[Route("update")]
|
|
[Consumes("application/json")]
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
|
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
[HttpPost]
|
|
public ActionResult<UserItem> Update([FromBody] UpdateParameter update)
|
|
{
|
|
#if DEBUG
|
|
logger?.LogInformation("user update, {user}", update);
|
|
#endif
|
|
var (result, user) = CheckPermission();
|
|
if (result != null)
|
|
{
|
|
return result;
|
|
}
|
|
if (user == null)
|
|
{
|
|
return NotFound();
|
|
}
|
|
|
|
var now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
|
user.ActiveDateUnixTime = now;
|
|
user.Name = update.UserName;
|
|
user.Email = update.Email;
|
|
user.Mobile = update.Mobile;
|
|
database.Users.Update(user);
|
|
SaveDatabase();
|
|
|
|
return Ok(user);
|
|
}
|
|
|
|
//#if DEBUG
|
|
/// <summary>
|
|
/// 获取所有用户
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
[Route("query")]
|
|
[HttpGet]
|
|
public ActionResult<UserItem[]> GetUsers()
|
|
{
|
|
return Ok(database.Users.ToArray());
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取所有 token
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
[Route("tokens")]
|
|
[HttpGet]
|
|
public ActionResult<TokenItem[]> GetTokens()
|
|
{
|
|
return Ok(database.Tokens.ToArray());
|
|
}
|
|
//#endif
|
|
}
|
|
}
|