feat: backend add auth method

This commit is contained in:
SikongJueluo 2025-07-10 19:39:00 +08:00
parent c6c3f1cc41
commit d6167ac286
No known key found for this signature in database
5 changed files with 235 additions and 51 deletions

View File

@ -1,9 +1,12 @@
using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.FileProviders;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json; using Newtonsoft.Json;
using NLog; using NLog;
using NLog.Web; using NLog.Web;
using NSwag.Generation.Processors.Security;
using server.Services; using server.Services;
// Early init of NLog to allow startup and exception logging, before host is built // Early init of NLog to allow startup and exception logging, before host is built
@ -38,6 +41,27 @@ try
options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
}); });
// Add JWT Token Authorization
builder.Services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
RequireExpirationTime = true,
ValidIssuer = "dlut.edu.cn",
ValidAudience = "dlut.edu.cn",
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes("my secret key 1234567890my secret key 1234567890")),
};
options.Authority = "http://localhost:5000";
options.RequireHttpsMetadata = false;
});
// Add CORS policy // Add CORS policy
if (builder.Environment.IsDevelopment()) if (builder.Environment.IsDevelopment())
{ {
@ -59,7 +83,7 @@ try
}); });
// Add Swagger // Add Swagger
builder.Services.AddOpenApiDocument(options => builder.Services.AddSwaggerDocument(options =>
{ {
options.PostProcess = document => options.PostProcess = document =>
{ {
@ -81,8 +105,19 @@ try
// } // }
}; };
}; };
// Authorization
options.AddSecurity("Bearer", new NSwag.OpenApiSecurityScheme
{
Description = "请输入token,格式为 Bearer xxxxxxxx注意中间必须有空格",
Name = "Authorization",
In = NSwag.OpenApiSecurityApiKeyLocation.Header,
Type = NSwag.OpenApiSecuritySchemeType.ApiKey,
});
options.OperationProcessors.Add(new OperationSecurityScopeProcessor("Bearer"));
}); });
// 添加 HTTP 视频流服务 // 添加 HTTP 视频流服务
builder.Services.AddSingleton<HttpVideoStreamService>(); builder.Services.AddSingleton<HttpVideoStreamService>();
builder.Services.AddHostedService(provider => provider.GetRequiredService<HttpVideoStreamService>()); builder.Services.AddHostedService(provider => provider.GetRequiredService<HttpVideoStreamService>());

View File

@ -18,6 +18,7 @@
<PackageReference Include="DotNext.Threading" Version="5.19.1" /> <PackageReference Include="DotNext.Threading" Version="5.19.1" />
<PackageReference Include="Honoo.IO.Hashing.Crc" Version="1.3.3" /> <PackageReference Include="Honoo.IO.Hashing.Crc" Version="1.3.3" />
<PackageReference Include="linq2db.AspNet" Version="5.4.1" /> <PackageReference Include="linq2db.AspNet" Version="5.4.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.7" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.4" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.4" />
<PackageReference Include="Microsoft.AspNetCore.SpaProxy" Version="9.0.4" /> <PackageReference Include="Microsoft.AspNetCore.SpaProxy" Version="9.0.4" />
<PackageReference Include="Microsoft.OpenApi" Version="1.6.23" /> <PackageReference Include="Microsoft.OpenApi" Version="1.6.23" />

View File

@ -1,5 +1,9 @@
using Microsoft.AspNetCore.Cors; using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
namespace server.Controllers; namespace server.Controllers;
@ -13,57 +17,70 @@ public class DataController : ControllerBase
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
/// <summary> /// <summary>
/// 创建数据库表 /// [TODO:description]
/// </summary> /// </summary>
/// <returns>插入的记录数</returns> /// <param name="name">[TODO:parameter]</param>
[EnableCors("Development")] /// <param name="password">[TODO:parameter]</param>
[HttpPost("CreateTable")] /// <returns>[TODO:return]</returns>
public IResult CreateTables() [HttpPost("login")]
public IActionResult Login(string name, string password)
{ {
// 验证用户密码
using var db = new Database.AppDataConnection(); using var db = new Database.AppDataConnection();
db.CreateAllTables(); var ret = db.CheckUserPassword(name, password);
return TypedResults.Ok(); if (!ret.IsSuccessful) return StatusCode(StatusCodes.Status500InternalServerError);
if (!ret.Value.HasValue) return BadRequest($"TODO");
var user = ret.Value.Value;
// 生成 JWT
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes("my secret key 1234567890my secret key 1234567890");
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, user.Name),
new Claim(ClaimTypes.Email, user.EMail),
}),
Expires = DateTime.UtcNow.AddHours(1),
SigningCredentials = new SigningCredentials(
new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature),
Audience = "dlut.edu.cn",
Issuer = "dlut.edu.cn",
};
var token = tokenHandler.CreateToken(tokenDescriptor);
var jwt = tokenHandler.WriteToken(token);
return Ok(jwt);
} }
/// <summary> /// <summary>
/// 删除数据库表 /// [TODO:description]
/// </summary> /// </summary>
/// <returns>插入的记录数</returns> /// <returns>[TODO:return]</returns>
[EnableCors("Development")] [HttpGet("TestAuth")]
[HttpDelete("DropTables")] [Authorize]
public IResult DropTables() public IActionResult TestAuth()
{ {
using var db = new Database.AppDataConnection(); return Ok("Authenticated!");
db.DropAllTables();
return TypedResults.Ok();
}
/// <summary>
/// 获取所有用户
/// </summary>
/// <returns>用户列表</returns>
[HttpGet("AllUsers")]
public IResult AllUsers()
{
using var db = new Database.AppDataConnection();
var ret = db.User.ToList();
return TypedResults.Ok(ret);
} }
/// <summary> /// <summary>
/// 注册新用户 /// 注册新用户
/// </summary> /// </summary>
/// <param name="name">用户名</param> /// <param name="name">用户名</param>
/// <param name="email">[TODO:parameter]</param>
/// <param name="password">[TODO:parameter]</param>
/// <returns>操作结果</returns> /// <returns>操作结果</returns>
[HttpPost("SignUpUser")] [HttpPost("SignUpUser")]
public IResult SignUpUser(string name) public IActionResult SignUpUser(string name, string email, string password)
{ {
if (name.Length > 255) if (name.Length > 255)
return TypedResults.BadRequest("Name Couln't over 255 characters"); return BadRequest("Name Couln't over 255 characters");
using var db = new Database.AppDataConnection(); using var db = new Database.AppDataConnection();
var ret = db.AddUser(name); var ret = db.AddUser(name, email, password);
return TypedResults.Ok(ret); return Ok(ret);
} }
} }

View File

@ -1,3 +1,4 @@
using DotNext;
using LinqToDB; using LinqToDB;
using LinqToDB.Data; using LinqToDB.Data;
using LinqToDB.Mapping; using LinqToDB.Mapping;
@ -20,6 +21,46 @@ public class User
/// </summary> /// </summary>
[NotNull] [NotNull]
public required string Name { get; set; } public required string Name { get; set; }
/// <summary>
/// 用户的电子邮箱
/// </summary>
[NotNull]
public required string EMail { get; set; }
/// <summary>
/// [TODO:description]
/// </summary>
[NotNull]
public required string Password { get; set; }
/// <summary>
/// [TODO:description]
/// </summary>
[NotNull]
public required UserPermission Permission { get; set; }
/// <summary>
/// [TODO:description]
/// </summary>
[Nullable]
public Guid BoardID { get; set; }
/// <summary>
/// [TODO:description]
/// </summary>
public enum UserPermission
{
/// <summary>
/// [TODO:description]
/// </summary>
Admin,
/// <summary>
/// [TODO:description]
/// </summary>
Normal,
}
} }
/// <summary> /// <summary>
@ -31,13 +72,47 @@ public class Board
/// FPGA 板子的唯一标识符 /// FPGA 板子的唯一标识符
/// </summary> /// </summary>
[PrimaryKey] [PrimaryKey]
public Guid Id { get; set; } = Guid.NewGuid(); public Guid ID { get; set; } = Guid.NewGuid();
/// <summary> /// <summary>
/// FPGA 板子的名称 /// FPGA 板子的名称
/// </summary> /// </summary>
[NotNull] [NotNull]
public required string BoardName { get; set; } public required string BoardName { get; set; }
/// <summary>
/// [TODO:description]
/// </summary>
[NotNull]
public required string IpAddr { get; set; }
/// <summary>
/// [TODO:description]
/// </summary>
[NotNull]
public required int Port { get; set; }
/// <summary>
/// [TODO:description]
/// </summary>
[NotNull]
public required BoardStatus Status { get; set; }
/// <summary>
/// [TODO:description]
/// </summary>
public enum BoardStatus
{
/// <summary>
/// [TODO:description]
/// </summary>
Busy,
/// <summary>
/// [TODO:description]
/// </summary>
Available,
}
} }
/// <summary> /// <summary>
@ -45,6 +120,8 @@ public class Board
/// </summary> /// </summary>
public class AppDataConnection : DataConnection public class AppDataConnection : DataConnection
{ {
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
static readonly LinqToDB.DataOptions options = static readonly LinqToDB.DataOptions options =
new LinqToDB.DataOptions() new LinqToDB.DataOptions()
.UseSQLite($"Data Source={Environment.CurrentDirectory}/Database.sqlite"); .UseSQLite($"Data Source={Environment.CurrentDirectory}/Database.sqlite");
@ -77,30 +154,87 @@ public class AppDataConnection : DataConnection
/// 添加一个新的用户到数据库 /// 添加一个新的用户到数据库
/// </summary> /// </summary>
/// <param name="name">用户的名称</param> /// <param name="name">用户的名称</param>
/// <param name="email">[TODO:parameter]</param>
/// <param name="password">[TODO:parameter]</param>
/// <returns>插入的记录数</returns> /// <returns>插入的记录数</returns>
public int AddUser(string name) public int AddUser(string name, string email, string password)
{ {
var user = new User() var user = new User()
{ {
Name = name Name = name,
EMail = email,
Password = password,
Permission = Database.User.UserPermission.Normal,
}; };
return this.Insert(user); return this.Insert(user);
} }
/// <summary>
/// [TODO:description]
/// </summary>
/// <param name="name">[TODO:parameter]</param>
/// <param name="password">[TODO:parameter]</param>
/// <returns>[TODO:return]</returns>
public Result<Optional<User>> CheckUserPassword(string name, string password)
{
var user = this.User.Where((user) => user.Name == name).ToArray();
if (user.Length > 1)
{
logger.Error($"TODO");
return new(new Exception($""));
}
if (user[0].Password == password) return new(user[0]);
else return new(Optional.Null<User>());
}
/// <summary> /// <summary>
/// 添加一块新的 FPGA 板子到数据库 /// 添加一块新的 FPGA 板子到数据库
/// </summary> /// </summary>
/// <param name="name">FPGA 板子的名称</param> /// <param name="name">FPGA 板子的名称</param>
/// <param name="ipAddr">[TODO:Param]</param>
/// <param name="port">[TODO:Param]</param>
/// <returns>插入的记录数</returns> /// <returns>插入的记录数</returns>
public int AddBoard(string name) public int AddBoard(string name, string ipAddr, int port)
{ {
var board = new Board() var board = new Board()
{ {
BoardName = name BoardName = name,
IpAddr = ipAddr,
Port = port,
Status = Database.Board.BoardStatus.Available,
}; };
return this.Insert(board); return this.Insert(board);
} }
/// <summary>
/// [TODO:description]
/// </summary>
/// <returns>[TODO:return]</returns>
public Optional<Board> GetAvailableBoard()
{
var boards = this.Board.Where(
(board) => board.Status == Database.Board.BoardStatus.Available
).ToArray();
if (boards.Length < 0)
{
logger.Warn($"TODO");
return new(null);
}
else
{
var board = boards[0];
board.Status = Database.Board.BoardStatus.Busy;
this.Board
.Where(target => target.ID == board.ID)
.Set(target => target.Status, board.Status)
.Update();
return new(board);
}
}
/// <summary> /// <summary>
/// 用户表 /// 用户表
/// </summary> /// </summary>

View File

@ -1,16 +1,13 @@
<template> <template>
<header></header> <div class="min-h-screen bg-base-100 container mx-auto p-6 space-y-6">
<main class="relative"> <ul class="menu bg-base-200 w-56 gap-2 rounded-2xl">
<div class="w-screen h-screen flex items-center justify-center"> <li><a>Item 1</a></li>
<UploadCard /> <li><a>Item 2</a></li>
</div> <li><a>Item 3</a></li>
</main> </ul>
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts"></script>
import UploadCard from "@/components/UploadCard.vue";
</script>
<style scoped> <style scoped></style>
@import "../assets/main.css";
</style>