feat: backend add auth method
This commit is contained in:
		@@ -1,9 +1,12 @@
 | 
			
		||||
using System.Text;
 | 
			
		||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
 | 
			
		||||
using Microsoft.AspNetCore.Http.Features;
 | 
			
		||||
using Microsoft.Extensions.FileProviders;
 | 
			
		||||
using Microsoft.IdentityModel.Tokens;
 | 
			
		||||
using Newtonsoft.Json;
 | 
			
		||||
using NLog;
 | 
			
		||||
using NLog.Web;
 | 
			
		||||
 | 
			
		||||
using NSwag.Generation.Processors.Security;
 | 
			
		||||
using server.Services;
 | 
			
		||||
 | 
			
		||||
// Early init of NLog to allow startup and exception logging, before host is built
 | 
			
		||||
@@ -38,6 +41,27 @@ try
 | 
			
		||||
        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
 | 
			
		||||
    if (builder.Environment.IsDevelopment())
 | 
			
		||||
    {
 | 
			
		||||
@@ -59,7 +83,7 @@ try
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // Add Swagger
 | 
			
		||||
    builder.Services.AddOpenApiDocument(options =>
 | 
			
		||||
    builder.Services.AddSwaggerDocument(options =>
 | 
			
		||||
    {
 | 
			
		||||
        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 视频流服务
 | 
			
		||||
    builder.Services.AddSingleton<HttpVideoStreamService>();
 | 
			
		||||
    builder.Services.AddHostedService(provider => provider.GetRequiredService<HttpVideoStreamService>());
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,7 @@
 | 
			
		||||
    <PackageReference Include="DotNext.Threading" Version="5.19.1" />
 | 
			
		||||
    <PackageReference Include="Honoo.IO.Hashing.Crc" Version="1.3.3" />
 | 
			
		||||
    <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.SpaProxy" Version="9.0.4" />
 | 
			
		||||
    <PackageReference Include="Microsoft.OpenApi" Version="1.6.23" />
 | 
			
		||||
 
 | 
			
		||||
@@ -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.IdentityModel.Tokens;
 | 
			
		||||
 | 
			
		||||
namespace server.Controllers;
 | 
			
		||||
 | 
			
		||||
@@ -13,57 +17,70 @@ public class DataController : ControllerBase
 | 
			
		||||
    private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 创建数据库表
 | 
			
		||||
    /// [TODO:description]
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>插入的记录数</returns>
 | 
			
		||||
    [EnableCors("Development")]
 | 
			
		||||
    [HttpPost("CreateTable")]
 | 
			
		||||
    public IResult CreateTables()
 | 
			
		||||
    /// <param name="name">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="password">[TODO:parameter]</param>
 | 
			
		||||
    /// <returns>[TODO:return]</returns>
 | 
			
		||||
    [HttpPost("login")]
 | 
			
		||||
    public IActionResult Login(string name, string password)
 | 
			
		||||
    {
 | 
			
		||||
        // 验证用户密码
 | 
			
		||||
        using var db = new Database.AppDataConnection();
 | 
			
		||||
        db.CreateAllTables();
 | 
			
		||||
        return TypedResults.Ok();
 | 
			
		||||
        var ret = db.CheckUserPassword(name, password);
 | 
			
		||||
        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>
 | 
			
		||||
    /// 删除数据库表
 | 
			
		||||
    /// [TODO:description]
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>插入的记录数</returns>
 | 
			
		||||
    [EnableCors("Development")]
 | 
			
		||||
    [HttpDelete("DropTables")]
 | 
			
		||||
    public IResult DropTables()
 | 
			
		||||
    /// <returns>[TODO:return]</returns>
 | 
			
		||||
    [HttpGet("TestAuth")]
 | 
			
		||||
    [Authorize]
 | 
			
		||||
    public IActionResult TestAuth()
 | 
			
		||||
    {
 | 
			
		||||
        using var db = new Database.AppDataConnection();
 | 
			
		||||
        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);
 | 
			
		||||
        return Ok("Authenticated!");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 注册新用户
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="name">用户名</param>
 | 
			
		||||
    /// <param name="email">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="password">[TODO:parameter]</param>
 | 
			
		||||
    /// <returns>操作结果</returns>
 | 
			
		||||
    [HttpPost("SignUpUser")]
 | 
			
		||||
    public IResult SignUpUser(string name)
 | 
			
		||||
    public IActionResult SignUpUser(string name, string email, string password)
 | 
			
		||||
    {
 | 
			
		||||
        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();
 | 
			
		||||
        var ret = db.AddUser(name);
 | 
			
		||||
        return TypedResults.Ok(ret);
 | 
			
		||||
        var ret = db.AddUser(name, email, password);
 | 
			
		||||
        return Ok(ret);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
using DotNext;
 | 
			
		||||
using LinqToDB;
 | 
			
		||||
using LinqToDB.Data;
 | 
			
		||||
using LinqToDB.Mapping;
 | 
			
		||||
@@ -20,6 +21,46 @@ public class User
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [NotNull]
 | 
			
		||||
    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>
 | 
			
		||||
@@ -31,13 +72,47 @@ public class Board
 | 
			
		||||
    /// FPGA 板子的唯一标识符
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [PrimaryKey]
 | 
			
		||||
    public Guid Id { get; set; } = Guid.NewGuid();
 | 
			
		||||
    public Guid ID { get; set; } = Guid.NewGuid();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// FPGA 板子的名称
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [NotNull]
 | 
			
		||||
    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>
 | 
			
		||||
@@ -45,6 +120,8 @@ public class Board
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class AppDataConnection : DataConnection
 | 
			
		||||
{
 | 
			
		||||
    private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
 | 
			
		||||
 | 
			
		||||
    static readonly LinqToDB.DataOptions options =
 | 
			
		||||
        new LinqToDB.DataOptions()
 | 
			
		||||
            .UseSQLite($"Data Source={Environment.CurrentDirectory}/Database.sqlite");
 | 
			
		||||
@@ -77,30 +154,87 @@ public class AppDataConnection : DataConnection
 | 
			
		||||
    /// 添加一个新的用户到数据库
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="name">用户的名称</param>
 | 
			
		||||
    /// <param name="email">[TODO:parameter]</param>
 | 
			
		||||
    /// <param name="password">[TODO:parameter]</param>
 | 
			
		||||
    /// <returns>插入的记录数</returns>
 | 
			
		||||
    public int AddUser(string name)
 | 
			
		||||
    public int AddUser(string name, string email, string password)
 | 
			
		||||
    {
 | 
			
		||||
        var user = new User()
 | 
			
		||||
        {
 | 
			
		||||
            Name = name
 | 
			
		||||
            Name = name,
 | 
			
		||||
            EMail = email,
 | 
			
		||||
            Password = password,
 | 
			
		||||
            Permission = Database.User.UserPermission.Normal,
 | 
			
		||||
        };
 | 
			
		||||
        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>
 | 
			
		||||
    /// 添加一块新的 FPGA 板子到数据库
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="name">FPGA 板子的名称</param>
 | 
			
		||||
    /// <param name="ipAddr">[TODO:Param]</param>
 | 
			
		||||
    /// <param name="port">[TODO:Param]</param>
 | 
			
		||||
    /// <returns>插入的记录数</returns>
 | 
			
		||||
    public int AddBoard(string name)
 | 
			
		||||
    public int AddBoard(string name, string ipAddr, int port)
 | 
			
		||||
    {
 | 
			
		||||
        var board = new Board()
 | 
			
		||||
        {
 | 
			
		||||
            BoardName = name
 | 
			
		||||
            BoardName = name,
 | 
			
		||||
            IpAddr = ipAddr,
 | 
			
		||||
            Port = port,
 | 
			
		||||
            Status = Database.Board.BoardStatus.Available,
 | 
			
		||||
        };
 | 
			
		||||
        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>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,13 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <header></header>
 | 
			
		||||
  <main class="relative">
 | 
			
		||||
    <div class="w-screen h-screen flex items-center justify-center">
 | 
			
		||||
      <UploadCard />
 | 
			
		||||
    </div>
 | 
			
		||||
  </main>
 | 
			
		||||
  <div class="min-h-screen bg-base-100 container mx-auto p-6 space-y-6">
 | 
			
		||||
    <ul class="menu bg-base-200 w-56 gap-2 rounded-2xl">
 | 
			
		||||
      <li><a>Item 1</a></li>
 | 
			
		||||
      <li><a>Item 2</a></li>
 | 
			
		||||
      <li><a>Item 3</a></li>
 | 
			
		||||
    </ul>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import UploadCard from "@/components/UploadCard.vue";
 | 
			
		||||
</script>
 | 
			
		||||
<script setup lang="ts"></script>
 | 
			
		||||
 | 
			
		||||
<style scoped>
 | 
			
		||||
@import "../assets/main.css";
 | 
			
		||||
</style>
 | 
			
		||||
<style scoped></style>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user