加入專案檔案。
This commit is contained in:
parent
1d21aed01d
commit
f198f55935
30
.dockerignore
Normal file
30
.dockerignore
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
**/.classpath
|
||||||
|
**/.dockerignore
|
||||||
|
**/.env
|
||||||
|
**/.git
|
||||||
|
**/.gitignore
|
||||||
|
**/.project
|
||||||
|
**/.settings
|
||||||
|
**/.toolstarget
|
||||||
|
**/.vs
|
||||||
|
**/.vscode
|
||||||
|
**/*.*proj.user
|
||||||
|
**/*.dbmdl
|
||||||
|
**/*.jfm
|
||||||
|
**/azds.yaml
|
||||||
|
**/bin
|
||||||
|
**/charts
|
||||||
|
**/docker-compose*
|
||||||
|
**/Dockerfile*
|
||||||
|
**/node_modules
|
||||||
|
**/npm-debug.log
|
||||||
|
**/obj
|
||||||
|
**/secrets.dev.yaml
|
||||||
|
**/values.dev.yaml
|
||||||
|
LICENSE
|
||||||
|
README.md
|
||||||
|
!**/.gitignore
|
||||||
|
!.git/HEAD
|
||||||
|
!.git/config
|
||||||
|
!.git/packed-refs
|
||||||
|
!.git/refs/heads/**
|
25
TCM_API.sln
Normal file
25
TCM_API.sln
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.9.34728.123
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TCM_API", "TCM_API\TCM_API.csproj", "{DB530787-F8A3-40D7-833F-0A7A9940E52C}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{DB530787-F8A3-40D7-833F-0A7A9940E52C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{DB530787-F8A3-40D7-833F-0A7A9940E52C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{DB530787-F8A3-40D7-833F-0A7A9940E52C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{DB530787-F8A3-40D7-833F-0A7A9940E52C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {76FA0356-8019-4761-B625-C7E084F29225}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
5
TCM_API/Authorization/AllowAnonymousAttribute.cs
Normal file
5
TCM_API/Authorization/AllowAnonymousAttribute.cs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
namespace TCM_API.Authorization;
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Method)]
|
||||||
|
public class AllowAnonymousAttribute : Attribute
|
||||||
|
{ }
|
25
TCM_API/Authorization/AuthorizeAttribute.cs
Normal file
25
TCM_API/Authorization/AuthorizeAttribute.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
namespace TCM_API.Authorization;
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using TCM_API.Entities;
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
|
||||||
|
public class AuthorizeAttribute : Attribute, IAuthorizationFilter
|
||||||
|
{
|
||||||
|
public void OnAuthorization(AuthorizationFilterContext context)
|
||||||
|
{
|
||||||
|
// skip authorization if action is decorated with [AllowAnonymous] attribute
|
||||||
|
var allowAnonymous = context.ActionDescriptor.EndpointMetadata.OfType<AllowAnonymousAttribute>().Any();
|
||||||
|
if (allowAnonymous)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// authorization
|
||||||
|
var user = (User?)context.HttpContext.Items["User"];
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
// not logged in or role not authorized
|
||||||
|
context.Result = new JsonResult(new { message = "Unauthorized" }) { StatusCode = StatusCodes.Status401Unauthorized };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
TCM_API/Authorization/JwtMiddleware.cs
Normal file
26
TCM_API/Authorization/JwtMiddleware.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
namespace TCM_API.Authorization;
|
||||||
|
|
||||||
|
using TCM_API.Services;
|
||||||
|
|
||||||
|
public class JwtMiddleware
|
||||||
|
{
|
||||||
|
private readonly RequestDelegate _next;
|
||||||
|
|
||||||
|
public JwtMiddleware(RequestDelegate next)
|
||||||
|
{
|
||||||
|
_next = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Invoke(HttpContext context, IUserService userService, IJwtUtils jwtUtils)
|
||||||
|
{
|
||||||
|
var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
|
||||||
|
var userId = jwtUtils.ValidateJwtToken(token);
|
||||||
|
if (userId != null)
|
||||||
|
{
|
||||||
|
// attach user to context on successful jwt validation
|
||||||
|
context.Items["User"] = userService.GetById(userId.Value);
|
||||||
|
}
|
||||||
|
var stop = "1";
|
||||||
|
await _next(context);
|
||||||
|
}
|
||||||
|
}
|
111
TCM_API/Authorization/JwtUtils.cs
Normal file
111
TCM_API/Authorization/JwtUtils.cs
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
namespace TCM_API.Authorization;
|
||||||
|
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Text;
|
||||||
|
using TCM_API.Entities;
|
||||||
|
using TCM_API.Helpers;
|
||||||
|
|
||||||
|
public interface IJwtUtils
|
||||||
|
{
|
||||||
|
public string GenerateJwtToken(User user);
|
||||||
|
public int? ValidateJwtToken(string? token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class JwtUtils : IJwtUtils
|
||||||
|
{
|
||||||
|
private readonly AppSettings _appSettings;
|
||||||
|
|
||||||
|
public JwtUtils(IOptions<AppSettings> appSettings)
|
||||||
|
{
|
||||||
|
_appSettings = appSettings.Value;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(_appSettings.Secret))
|
||||||
|
throw new Exception("JWT secret not configured");
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GenerateJwtToken(User user)
|
||||||
|
{
|
||||||
|
// generate token that is valid for 7 days
|
||||||
|
var tokenHandler = new JwtSecurityTokenHandler();
|
||||||
|
var key = Encoding.ASCII.GetBytes(_appSettings.Secret!);
|
||||||
|
var tokenDescriptor = new SecurityTokenDescriptor
|
||||||
|
{
|
||||||
|
Subject = new ClaimsIdentity(new[]
|
||||||
|
{
|
||||||
|
new Claim("id", user.id.ToString()),
|
||||||
|
new Claim("firstname", user.firstname ?? ""), // 加入 firstname
|
||||||
|
new Claim("lastname", user.lastname ?? ""), // 加入 lastname
|
||||||
|
new Claim("level", user.level ?? "") // 加入 lastname
|
||||||
|
}),
|
||||||
|
Expires = DateTime.UtcNow.AddDays(7),
|
||||||
|
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
|
||||||
|
};
|
||||||
|
var token = tokenHandler.CreateToken(tokenDescriptor);
|
||||||
|
return tokenHandler.WriteToken(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int? ValidateJwtToken(string? token)
|
||||||
|
{
|
||||||
|
if (token == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var tokenHandler = new JwtSecurityTokenHandler();
|
||||||
|
var key = Encoding.ASCII.GetBytes(_appSettings.Secret!);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
tokenHandler.ValidateToken(token, new TokenValidationParameters
|
||||||
|
{
|
||||||
|
ValidateIssuerSigningKey = true,
|
||||||
|
IssuerSigningKey = new SymmetricSecurityKey(key),
|
||||||
|
ValidateIssuer = false,
|
||||||
|
ValidateAudience = false,
|
||||||
|
// set clockskew to zero so tokens expire exactly at token expiration time (instead of 5 minutes later)
|
||||||
|
ClockSkew = TimeSpan.Zero
|
||||||
|
}, out SecurityToken validatedToken);
|
||||||
|
|
||||||
|
var jwtToken = (JwtSecurityToken)validatedToken;
|
||||||
|
var userId = int.Parse(jwtToken.Claims.First(x => x.Type == "id").Value);
|
||||||
|
|
||||||
|
// return user id from JWT token if validation successful
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// return null if validation fails
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//0523
|
||||||
|
public bool ValidateToken(string token)
|
||||||
|
{
|
||||||
|
var tokenHandler = new JwtSecurityTokenHandler();
|
||||||
|
var jwtSecret = "your_jwt_secret"; // JWT 密钥,应与生成令牌时使用的密钥相匹配
|
||||||
|
|
||||||
|
var validationParameters = new TokenValidationParameters
|
||||||
|
{
|
||||||
|
ValidateIssuerSigningKey = true,
|
||||||
|
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSecret)),
|
||||||
|
ValidateIssuer = false,
|
||||||
|
ValidateAudience = false,
|
||||||
|
ValidateLifetime = true,
|
||||||
|
ClockSkew = TimeSpan.Zero // 设置为零以确保令牌过期时立即失效
|
||||||
|
};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SecurityToken validatedToken;
|
||||||
|
tokenHandler.ValidateToken(token, validationParameters, out validatedToken);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
93
TCM_API/Controllers/UsersController.cs
Normal file
93
TCM_API/Controllers/UsersController.cs
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
namespace WebApi.Controllers;
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using NuGet.Common;
|
||||||
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
|
using TCM_API.Authorization;
|
||||||
|
using TCM_API.Models;
|
||||||
|
using TCM_API.Services;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Authorize]
|
||||||
|
[Route("[controller]")]
|
||||||
|
public class UsersController : ControllerBase
|
||||||
|
{
|
||||||
|
private IUserService _userService;
|
||||||
|
|
||||||
|
public UsersController(IUserService userService)
|
||||||
|
{
|
||||||
|
_userService = userService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[AllowAnonymous]
|
||||||
|
[HttpPost("authenticate")]
|
||||||
|
public IActionResult Authenticate(AuthenticateRequest model)
|
||||||
|
{
|
||||||
|
var response = _userService.Authenticate(model);
|
||||||
|
|
||||||
|
if (response == null)
|
||||||
|
return BadRequest(new { message = "Username or password is incorrect" });
|
||||||
|
|
||||||
|
// 将令牌添加到响应头中
|
||||||
|
Response.Headers.Add("Authorization", "Bearer " + response.Token);
|
||||||
|
|
||||||
|
// 将令牌保存在Cookie或其他适当的位置
|
||||||
|
Response.Cookies.Append("token", response.Token);
|
||||||
|
return Ok(response);
|
||||||
|
// 重定向到另一个页面
|
||||||
|
//return RedirectToAction("/Park_spaces/Parking_spaces_total_table");
|
||||||
|
//return RedirectToAction("Parking_spaces_total_table", "Park_spaces");
|
||||||
|
}
|
||||||
|
[HttpGet("token_check")]
|
||||||
|
public IActionResult Token()
|
||||||
|
{
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[HttpGet("token_check_user")]
|
||||||
|
public IActionResult GetUserData()
|
||||||
|
{
|
||||||
|
var tokenStr = HttpContext.Request.Headers["Authorization"].ToString().Replace("Bearer ", "");
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(tokenStr))
|
||||||
|
{
|
||||||
|
return Unauthorized("Token is missing or invalid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var tokenHandler = new JwtSecurityTokenHandler();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var token = tokenHandler.ReadJwtToken(tokenStr);
|
||||||
|
|
||||||
|
// 轉換 payload 為字典
|
||||||
|
var payloadData = token.Payload
|
||||||
|
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value?.ToString());
|
||||||
|
|
||||||
|
// 回傳 payload 作為 JSON
|
||||||
|
return Ok(payloadData);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return BadRequest($"Error parsing token: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public IActionResult GetAll()
|
||||||
|
{
|
||||||
|
var users = _userService.GetAll();
|
||||||
|
return Ok(users);
|
||||||
|
}
|
||||||
|
|
||||||
|
//[HttpPost("create_manage")]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
118
TCM_API/Controllers/lamiter/Company_detail_tableController.cs
Normal file
118
TCM_API/Controllers/lamiter/Company_detail_tableController.cs
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using TCM_API.Models;
|
||||||
|
using TCM_API.Entities;
|
||||||
|
using TCM_API.Services;
|
||||||
|
using TCM_API.Authorization;
|
||||||
|
using TCM_API.ViewModels;
|
||||||
|
|
||||||
|
namespace TCM_API.Controllers.lamiter
|
||||||
|
{
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
[ApiController]
|
||||||
|
//[Authorize]
|
||||||
|
public class Company_detail_tableController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly SqlContext _context;
|
||||||
|
|
||||||
|
public Company_detail_tableController(SqlContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region 獲取所有企業
|
||||||
|
/// <summary>
|
||||||
|
/// 獲取所有企業
|
||||||
|
/// </summary>
|
||||||
|
[HttpGet("get_all_campany")]
|
||||||
|
public async Task<ActionResult<object>> GetCompany_all_deatil()
|
||||||
|
{
|
||||||
|
|
||||||
|
var data = await(from c in _context.company_detail_table
|
||||||
|
select new
|
||||||
|
{
|
||||||
|
campant_name = c.campant_name,
|
||||||
|
guid = c.guid
|
||||||
|
|
||||||
|
}).ToListAsync();
|
||||||
|
return Ok( data);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 獲取單一企業
|
||||||
|
[HttpGet("get_campany-{guid}")]
|
||||||
|
public async Task<ActionResult<object>> GetCompany_deatil(string guid)
|
||||||
|
{
|
||||||
|
var data = await _context.company_detail_table.FindAsync(guid);
|
||||||
|
if (data == null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region 新增企業
|
||||||
|
/// <summary>
|
||||||
|
/// 新增企業
|
||||||
|
/// </summary>
|
||||||
|
[HttpPost("Add_campany")]
|
||||||
|
public async Task<ActionResult<Company_detail_table>> PostCompany_detail_table_1(Add_company add_company)
|
||||||
|
{
|
||||||
|
// 創建Guid
|
||||||
|
Guid new_guid = Guid.NewGuid();
|
||||||
|
|
||||||
|
|
||||||
|
//創建 公司詳細
|
||||||
|
Company_detail_table company_detail_table = new Company_detail_table();
|
||||||
|
company_detail_table.guid = new_guid.ToString();
|
||||||
|
company_detail_table.data_create_time = DateTime.Now;
|
||||||
|
company_detail_table.campant_name = add_company.lastname;
|
||||||
|
|
||||||
|
//創建 公司帳號
|
||||||
|
User user = new User();
|
||||||
|
user.firstname = "company";
|
||||||
|
user.lastname = add_company.lastname;
|
||||||
|
user.email = add_company.email;
|
||||||
|
user.password = add_company.password;
|
||||||
|
user.username = add_company.username;
|
||||||
|
user.level = "8";
|
||||||
|
user.guid = new_guid.ToString();
|
||||||
|
|
||||||
|
_context.company_detail_table.Add(company_detail_table);
|
||||||
|
_context.user_table.Add(user);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
catch (DbUpdateException)
|
||||||
|
{
|
||||||
|
if (Company_detail_tableExists(company_detail_table.guid))
|
||||||
|
{
|
||||||
|
return Conflict();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private bool Company_detail_tableExists(string id)
|
||||||
|
{
|
||||||
|
return _context.company_detail_table.Any(e => e.guid == id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
25
TCM_API/Dockerfile
Normal file
25
TCM_API/Dockerfile
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
|
||||||
|
USER app
|
||||||
|
WORKDIR /app
|
||||||
|
EXPOSE 8080
|
||||||
|
EXPOSE 8081
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
|
||||||
|
ARG BUILD_CONFIGURATION=Release
|
||||||
|
WORKDIR /src
|
||||||
|
COPY ["TCM_API/TCM_API.csproj", "TCM_API/"]
|
||||||
|
RUN dotnet restore "./TCM_API/TCM_API.csproj"
|
||||||
|
COPY . .
|
||||||
|
WORKDIR "/src/TCM_API"
|
||||||
|
RUN dotnet build "./TCM_API.csproj" -c $BUILD_CONFIGURATION -o /app/build
|
||||||
|
|
||||||
|
FROM build AS publish
|
||||||
|
ARG BUILD_CONFIGURATION=Release
|
||||||
|
RUN dotnet publish "./TCM_API.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
|
||||||
|
|
||||||
|
FROM base AS final
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=publish /app/publish .
|
||||||
|
ENTRYPOINT ["dotnet", "TCM_API.dll"]
|
20
TCM_API/Entities/User.cs
Normal file
20
TCM_API/Entities/User.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
namespace TCM_API.Entities;
|
||||||
|
|
||||||
|
using MessagePack;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
public class User
|
||||||
|
{
|
||||||
|
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public int id { get; set; }
|
||||||
|
public string? firstname { get; set; }
|
||||||
|
public string? lastname { get; set; }
|
||||||
|
public string? email { get; set; }
|
||||||
|
public string? username { get; set; }
|
||||||
|
public string? level { get; set; }
|
||||||
|
public string? guid { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public string? password { get; set; }
|
||||||
|
}
|
6
TCM_API/Helpers/AppSettings.cs
Normal file
6
TCM_API/Helpers/AppSettings.cs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
namespace TCM_API.Helpers;
|
||||||
|
|
||||||
|
public class AppSettings
|
||||||
|
{
|
||||||
|
public string? Secret { get; set; }
|
||||||
|
}
|
12
TCM_API/Models/AuthenticateRequest.cs
Normal file
12
TCM_API/Models/AuthenticateRequest.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
namespace TCM_API.Models;
|
||||||
|
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
public class AuthenticateRequest
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
public string? Username { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public string? Password { get; set; }
|
||||||
|
}
|
26
TCM_API/Models/AuthenticateResponse.cs
Normal file
26
TCM_API/Models/AuthenticateResponse.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
namespace TCM_API.Models;
|
||||||
|
|
||||||
|
using TCM_API.Entities;
|
||||||
|
|
||||||
|
public class AuthenticateResponse
|
||||||
|
{
|
||||||
|
public int id { get; set; }
|
||||||
|
public string? firstname { get; set; }
|
||||||
|
public string? lastname { get; set; }
|
||||||
|
public string? username { get; set; }
|
||||||
|
public string? email { get; set; }
|
||||||
|
public string? level { get; set; }
|
||||||
|
public string Token { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public AuthenticateResponse(User user, string token)
|
||||||
|
{
|
||||||
|
id = user.id;
|
||||||
|
firstname = user.firstname;
|
||||||
|
lastname = user.lastname;
|
||||||
|
username = user.username;
|
||||||
|
level = user.level;
|
||||||
|
email = user.email;
|
||||||
|
Token = token;
|
||||||
|
}
|
||||||
|
}
|
6
TCM_API/Models/TEST_01.cs
Normal file
6
TCM_API/Models/TEST_01.cs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
namespace TCM_API.Models
|
||||||
|
{
|
||||||
|
public class TEST_01
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
14
TCM_API/Models/lamiter/Company_detail_table.cs
Normal file
14
TCM_API/Models/lamiter/Company_detail_table.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace TCM_API.Models
|
||||||
|
{
|
||||||
|
public class Company_detail_table
|
||||||
|
{
|
||||||
|
public string? campant_name { get; set; }
|
||||||
|
public string? guid { get; set; }
|
||||||
|
public DateTime? data_create_time { get; set; }
|
||||||
|
|
||||||
|
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public int id { get; set; }
|
||||||
|
}
|
||||||
|
}
|
146
TCM_API/Program.cs
Normal file
146
TCM_API/Program.cs
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using TCM_API.Authorization;
|
||||||
|
using TCM_API.Helpers;
|
||||||
|
using System.Configuration;
|
||||||
|
using TCM_API.Services;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
using System.Text;
|
||||||
|
using Microsoft.OpenApi.Models;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
//在 ASP.NET Core 中啟用 CORS (跨原始來源要求)
|
||||||
|
builder.Services.AddCors();
|
||||||
|
// Add services to the container.
|
||||||
|
builder.Services.AddControllers();
|
||||||
|
|
||||||
|
// 連線PostgreSQL資料庫
|
||||||
|
var connectionString = "Server=leovip125.ddns.net;UserID=postgres;Password=vip125;Database=TCM;port=5432;Search Path=public;CommandTimeout=1800";
|
||||||
|
builder.Services.AddDbContext<SqlContext>(opt => opt.UseNpgsql(connectionString));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//身分驗證
|
||||||
|
//add services to DI container
|
||||||
|
{
|
||||||
|
var services = builder.Services;
|
||||||
|
services.AddCors();
|
||||||
|
services.AddControllers();
|
||||||
|
|
||||||
|
// configure strongly typed settings object
|
||||||
|
services.Configure<AppSettings>(builder.Configuration.GetSection("AppSettings"));
|
||||||
|
|
||||||
|
// 配置JWT身份验证
|
||||||
|
var jwtSettings = builder.Configuration.GetSection("AppSettings").Get<AppSettings>();
|
||||||
|
|
||||||
|
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||||
|
.AddJwtBearer(options =>
|
||||||
|
{
|
||||||
|
options.TokenValidationParameters = new TokenValidationParameters
|
||||||
|
{
|
||||||
|
ValidateIssuer = false,
|
||||||
|
ValidateAudience = false,
|
||||||
|
ValidateIssuerSigningKey = true,
|
||||||
|
//ValidIssuer = "your_issuer",
|
||||||
|
// ValidAudience = "your_audience",
|
||||||
|
ClockSkew = TimeSpan.Zero,
|
||||||
|
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.Secret))
|
||||||
|
};
|
||||||
|
});
|
||||||
|
services.AddSwaggerGen(c =>
|
||||||
|
{
|
||||||
|
c.SwaggerDoc("v1", new OpenApiInfo { Title = "TCM_API", Version = "v1" });
|
||||||
|
|
||||||
|
// Configure Swagger to use JWT authentication
|
||||||
|
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
|
||||||
|
{
|
||||||
|
Description = "JWT Authorization header using the Bearer scheme",
|
||||||
|
Name = "Authorization",
|
||||||
|
In = ParameterLocation.Header,
|
||||||
|
Type = SecuritySchemeType.ApiKey,
|
||||||
|
Scheme = "Bearer"
|
||||||
|
});
|
||||||
|
|
||||||
|
// 将JWT令牌作为所有端点的要求添加到Swagger文档
|
||||||
|
//ˇc.OperationFilter<SecurityRequirementsOperationFilter>();
|
||||||
|
c.AddSecurityRequirement(new OpenApiSecurityRequirement
|
||||||
|
{
|
||||||
|
{
|
||||||
|
new OpenApiSecurityScheme
|
||||||
|
{
|
||||||
|
Reference = new OpenApiReference
|
||||||
|
{
|
||||||
|
Type = ReferenceType.SecurityScheme,
|
||||||
|
Id = "Bearer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new string[] { }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// configure DI for application services
|
||||||
|
services.AddScoped<IJwtUtils, JwtUtils>();
|
||||||
|
services.AddScoped<IUserService, UserService>();
|
||||||
|
|
||||||
|
// 注册 HttpClient 服务
|
||||||
|
services.AddHttpClient();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add services to the container.
|
||||||
|
|
||||||
|
builder.Services.AddControllers();
|
||||||
|
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||||
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
|
builder.Services.AddSwaggerGen();
|
||||||
|
|
||||||
|
var app = builder.Build();
|
||||||
|
|
||||||
|
|
||||||
|
//身分驗證
|
||||||
|
// configure HTTP request pipeline
|
||||||
|
{
|
||||||
|
// global cors policy
|
||||||
|
app.UseCors(x => x
|
||||||
|
.AllowAnyOrigin()
|
||||||
|
.AllowAnyMethod()
|
||||||
|
.AllowAnyHeader());
|
||||||
|
|
||||||
|
// custom jwt auth middleware
|
||||||
|
app.UseMiddleware<JwtMiddleware>();
|
||||||
|
|
||||||
|
app.MapControllers();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure the HTTP request pipeline.
|
||||||
|
if (app.Environment.IsDevelopment())
|
||||||
|
{
|
||||||
|
app.UseSwagger();
|
||||||
|
//app.UseSwaggerUI();
|
||||||
|
app.UseSwaggerUI(c =>
|
||||||
|
{
|
||||||
|
c.SwaggerEndpoint("/swagger/v1/swagger.json", "TCM_API");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//在 ASP.NET Core 中啟用 CORS (跨原始來源要求)
|
||||||
|
// Shows UseCors with CorsPolicyBuilder.
|
||||||
|
app.UseCors(builder =>
|
||||||
|
{
|
||||||
|
builder.AllowAnyOrigin()
|
||||||
|
.AllowAnyMethod()
|
||||||
|
.AllowAnyHeader();
|
||||||
|
});
|
||||||
|
|
||||||
|
app.UseHttpsRedirection();
|
||||||
|
|
||||||
|
app.UseAuthentication();
|
||||||
|
|
||||||
|
app.UseAuthorization();
|
||||||
|
|
||||||
|
app.MapControllers();
|
||||||
|
|
||||||
|
app.Run();
|
||||||
|
|
52
TCM_API/Properties/launchSettings.json
Normal file
52
TCM_API/Properties/launchSettings.json
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"profiles": {
|
||||||
|
"http": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "swagger",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
},
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"applicationUrl": "http://localhost:5291"
|
||||||
|
},
|
||||||
|
"https": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "swagger",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
},
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"applicationUrl": "https://localhost:7226;http://localhost:5291"
|
||||||
|
},
|
||||||
|
"IIS Express": {
|
||||||
|
"commandName": "IISExpress",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "swagger",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Container (Dockerfile)": {
|
||||||
|
"commandName": "Docker",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_HTTPS_PORTS": "8081",
|
||||||
|
"ASPNETCORE_HTTP_PORTS": "8080"
|
||||||
|
},
|
||||||
|
"publishAllPorts": true,
|
||||||
|
"useSSL": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||||
|
"iisSettings": {
|
||||||
|
"windowsAuthentication": false,
|
||||||
|
"anonymousAuthentication": true,
|
||||||
|
"iisExpress": {
|
||||||
|
"applicationUrl": "http://localhost:9106",
|
||||||
|
"sslPort": 44325
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
41
TCM_API/Services/SqlContext.cs
Normal file
41
TCM_API/Services/SqlContext.cs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.Extensions.Configuration.Json;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using TCM_API.Models;
|
||||||
|
using TCM_API.Entities;
|
||||||
|
|
||||||
|
|
||||||
|
namespace TCM_API.Services
|
||||||
|
{
|
||||||
|
public class SqlContext : DbContext
|
||||||
|
{
|
||||||
|
|
||||||
|
public SqlContext(DbContextOptions<SqlContext> options) : base(options)
|
||||||
|
{
|
||||||
|
//連接PostgreSQL
|
||||||
|
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
|
||||||
|
AppContext.SetSwitch("Npgsql.DisableDateTimeInfinityConversions", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//public DbSet<E_table_v> e_table_v { get; set; } = null!;
|
||||||
|
//public DbSet<Test_0525> test_0525 { get; set; } = null!; //<Test_0330> Model名稱 test_0330 SQL名稱
|
||||||
|
public DbSet<User> user_table { get; set; } = null!;
|
||||||
|
public DbSet<Company_detail_table> company_detail_table { get; set; } = null!;
|
||||||
|
protected override void OnModelCreating(ModelBuilder builder)
|
||||||
|
{
|
||||||
|
|
||||||
|
base.OnModelCreating(builder);
|
||||||
|
//builder.Entity<Test_0525>().HasKey(o => new { o.user_id }); //Primary Key
|
||||||
|
builder.Entity<User>().HasKey(o => new { o.id }); //Primary Key
|
||||||
|
builder.Entity<Company_detail_table>().HasKey(o => new { o.guid });
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
74
TCM_API/Services/UserService.cs
Normal file
74
TCM_API/Services/UserService.cs
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
namespace TCM_API.Services;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using TCM_API.Authorization;
|
||||||
|
using TCM_API.Entities;
|
||||||
|
using TCM_API.Models;
|
||||||
|
|
||||||
|
public interface IUserService
|
||||||
|
{
|
||||||
|
AuthenticateResponse? Authenticate(AuthenticateRequest model);
|
||||||
|
IEnumerable<User> GetAll();
|
||||||
|
User? GetById(int id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UserService : IUserService
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
// users hardcoded for simplicity, store in a db with hashed passwords in production applications
|
||||||
|
private List<User> user_test = new List<User>
|
||||||
|
{
|
||||||
|
new User { Id = 1, FirstName = "Test", LastName = "User", Username = "test", Password = "test" },
|
||||||
|
new User { Id = 2, FirstName = "Test", LastName = "User", Username = "admin", Password = "admin" }
|
||||||
|
};
|
||||||
|
|
||||||
|
public DbSet<User> user_test { get; set; } = null!;
|
||||||
|
|
||||||
|
|
||||||
|
public List<User> GetUsers ()
|
||||||
|
{
|
||||||
|
return _dbContext.user_test.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
private readonly IJwtUtils _jwtUtils;
|
||||||
|
|
||||||
|
public UserService(IJwtUtils jwtUtils, SqlContext dbContext)
|
||||||
|
{
|
||||||
|
_jwtUtils = jwtUtils;
|
||||||
|
_dbContext = dbContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private readonly SqlContext _dbContext;
|
||||||
|
|
||||||
|
|
||||||
|
public AuthenticateResponse? Authenticate(AuthenticateRequest model)
|
||||||
|
{
|
||||||
|
var user = _dbContext.user_table.SingleOrDefault(
|
||||||
|
x => (x.username == model.Username || x.email == model.Username)
|
||||||
|
&& x.password == model.Password);
|
||||||
|
|
||||||
|
// return null if user not found
|
||||||
|
if (user == null) return null;
|
||||||
|
|
||||||
|
// authentication successful so generate jwt token
|
||||||
|
var token = _jwtUtils.GenerateJwtToken(user);
|
||||||
|
|
||||||
|
return new AuthenticateResponse(user, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public IEnumerable<User> GetAll()
|
||||||
|
{
|
||||||
|
return _dbContext.user_table;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User? GetById(int id)
|
||||||
|
{
|
||||||
|
return _dbContext.user_table.FirstOrDefault(x => x.id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
30
TCM_API/TCM_API.csproj
Normal file
30
TCM_API/TCM_API.csproj
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<UserSecretsId>6b0f798d-70cd-4d60-b552-6e6488f58399</UserSecretsId>
|
||||||
|
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.10" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="8.0.10" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.10" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.11">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.6" />
|
||||||
|
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="8.0.7" />
|
||||||
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.10" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
||||||
|
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.3.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Services\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
6
TCM_API/TCM_API.http
Normal file
6
TCM_API/TCM_API.http
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
@TCM_API_HostAddress = http://localhost:5291
|
||||||
|
|
||||||
|
GET {{TCM_API_HostAddress}}/weatherforecast/
|
||||||
|
Accept: application/json
|
||||||
|
|
||||||
|
###
|
13
TCM_API/ViewModels/Add_company.cs
Normal file
13
TCM_API/ViewModels/Add_company.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
namespace TCM_API.ViewModels
|
||||||
|
{
|
||||||
|
public class Add_company
|
||||||
|
{
|
||||||
|
public string? guid { get; set; }
|
||||||
|
public string? firstname { get; set; }
|
||||||
|
public string? lastname { get; set; }
|
||||||
|
public string? email { get; set; }
|
||||||
|
public string? username { get; set; }
|
||||||
|
public string? password { get; set; }
|
||||||
|
public string? level { get; set; }
|
||||||
|
}
|
||||||
|
}
|
8
TCM_API/appsettings.Development.json
Normal file
8
TCM_API/appsettings.Development.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
TCM_API/appsettings.json
Normal file
12
TCM_API/appsettings.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"AppSettings": {
|
||||||
|
"Secret": "TCM token test jwt lamiter local 1234567"
|
||||||
|
},
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*"
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user