diff --git a/server/Program.cs b/server/Program.cs index cbcbf05..2199e1f 100644 --- a/server/Program.cs +++ b/server/Program.cs @@ -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(); builder.Services.AddHostedService(provider => provider.GetRequiredService()); diff --git a/server/server.csproj b/server/server.csproj index 17b1ba6..42d0ef1 100644 --- a/server/server.csproj +++ b/server/server.csproj @@ -18,6 +18,7 @@ + diff --git a/server/src/Controllers/DataController.cs b/server/src/Controllers/DataController.cs index e656ace..735744d 100644 --- a/server/src/Controllers/DataController.cs +++ b/server/src/Controllers/DataController.cs @@ -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(); /// - /// 创建数据库表 + /// [TODO:description] /// - /// 插入的记录数 - [EnableCors("Development")] - [HttpPost("CreateTable")] - public IResult CreateTables() + /// [TODO:parameter] + /// [TODO:parameter] + /// [TODO:return] + [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); } /// - /// 删除数据库表 + /// [TODO:description] /// - /// 插入的记录数 - [EnableCors("Development")] - [HttpDelete("DropTables")] - public IResult DropTables() + /// [TODO:return] + [HttpGet("TestAuth")] + [Authorize] + public IActionResult TestAuth() { - using var db = new Database.AppDataConnection(); - db.DropAllTables(); - return TypedResults.Ok(); - } - - /// - /// 获取所有用户 - /// - /// 用户列表 - [HttpGet("AllUsers")] - public IResult AllUsers() - { - using var db = new Database.AppDataConnection(); - var ret = db.User.ToList(); - return TypedResults.Ok(ret); + return Ok("Authenticated!"); } /// /// 注册新用户 /// /// 用户名 + /// [TODO:parameter] + /// [TODO:parameter] /// 操作结果 [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); } } diff --git a/server/src/Database.cs b/server/src/Database.cs index e54d831..6d2fd97 100644 --- a/server/src/Database.cs +++ b/server/src/Database.cs @@ -1,3 +1,4 @@ +using DotNext; using LinqToDB; using LinqToDB.Data; using LinqToDB.Mapping; @@ -20,6 +21,46 @@ public class User /// [NotNull] public required string Name { get; set; } + + /// + /// 用户的电子邮箱 + /// + [NotNull] + public required string EMail { get; set; } + + /// + /// [TODO:description] + /// + [NotNull] + public required string Password { get; set; } + + /// + /// [TODO:description] + /// + [NotNull] + public required UserPermission Permission { get; set; } + + /// + /// [TODO:description] + /// + [Nullable] + public Guid BoardID { get; set; } + + /// + /// [TODO:description] + /// + public enum UserPermission + { + /// + /// [TODO:description] + /// + Admin, + + /// + /// [TODO:description] + /// + Normal, + } } /// @@ -31,13 +72,47 @@ public class Board /// FPGA 板子的唯一标识符 /// [PrimaryKey] - public Guid Id { get; set; } = Guid.NewGuid(); + public Guid ID { get; set; } = Guid.NewGuid(); /// /// FPGA 板子的名称 /// [NotNull] public required string BoardName { get; set; } + + /// + /// [TODO:description] + /// + [NotNull] + public required string IpAddr { get; set; } + + /// + /// [TODO:description] + /// + [NotNull] + public required int Port { get; set; } + + /// + /// [TODO:description] + /// + [NotNull] + public required BoardStatus Status { get; set; } + + /// + /// [TODO:description] + /// + public enum BoardStatus + { + /// + /// [TODO:description] + /// + Busy, + + /// + /// [TODO:description] + /// + Available, + } } /// @@ -45,6 +120,8 @@ public class Board /// 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 /// 添加一个新的用户到数据库 /// /// 用户的名称 + /// [TODO:parameter] + /// [TODO:parameter] /// 插入的记录数 - 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); } + /// + /// [TODO:description] + /// + /// [TODO:parameter] + /// [TODO:parameter] + /// [TODO:return] + public Result> 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()); + } + /// /// 添加一块新的 FPGA 板子到数据库 /// /// FPGA 板子的名称 + /// [TODO:Param] + /// [TODO:Param] /// 插入的记录数 - 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); } + /// + /// [TODO:description] + /// + /// [TODO:return] + public Optional 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); + } + } + /// /// 用户表 /// diff --git a/src/views/UserView.vue b/src/views/UserView.vue index fe1bcee..14fdf70 100644 --- a/src/views/UserView.vue +++ b/src/views/UserView.vue @@ -1,16 +1,13 @@ - + - +