1
0

feat(core): First try on ASP.NET

This commit is contained in:
Ca2didi 2024-10-15 19:42:14 +08:00
parent 554589a7df
commit f2a312aabc
14 changed files with 285 additions and 100 deletions

View File

@ -8,6 +8,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flawless.Client.Avanonia",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flawless.Server", "Flawless.Server\Flawless.Server.csproj", "{17EA1F4E-7D4E-485D-BDB5-18BBC3FB8DF9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flawless.Test.ConsoleApplication", "Flawless.Test.ConsoleApplication\Flawless.Test.ConsoleApplication.csproj", "{29FF9E82-23A4-4F3C-82B6-7ABC72D5990D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -30,5 +32,9 @@ Global
{17EA1F4E-7D4E-485D-BDB5-18BBC3FB8DF9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{17EA1F4E-7D4E-485D-BDB5-18BBC3FB8DF9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{17EA1F4E-7D4E-485D-BDB5-18BBC3FB8DF9}.Release|Any CPU.Build.0 = Release|Any CPU
{29FF9E82-23A4-4F3C-82B6-7ABC72D5990D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{29FF9E82-23A4-4F3C-82B6-7ABC72D5990D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{29FF9E82-23A4-4F3C-82B6-7ABC72D5990D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{29FF9E82-23A4-4F3C-82B6-7ABC72D5990D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,4 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AClaimsPrincipal_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fbf5ce3f8ae0647439d514bb1a3c7f96b13600_003F20_003Fabeaf9ae_003FClaimsPrincipal_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AClaimTypes_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fbf5ce3f8ae0647439d514bb1a3c7f96b13600_003Fbd_003F4cde67a5_003FClaimTypes_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AStringValue_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fee39d1e9346e41aa9d44f0e1b1c6630f76268_003F49_003Fb92346b2_003FStringValue_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>

View File

@ -7,23 +7,14 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.28.2" />
<PackageReference Include="Grpc.Net.Client" Version="2.66.0" />
<PackageReference Include="Grpc.Tools" Version="2.67.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Protobuf Include="Protos\greet.proto">
<GrpcServices>Server</GrpcServices>
<Access>Public</Access>
<ProtoCompile>True</ProtoCompile>
<CompileOutputs>True</CompileOutputs>
<OutputDir>obj\Debug\net8.0\</OutputDir>
<Generator>MSBuild:Compile</Generator>
</Protobuf>
<Protobuf Include="..\Flawless.Shared\Protos\*.proto" GrpcServices="Client"/>
<PackageReference Include="Google.Protobuf" Version="3.28.2" />
<PackageReference Include="Grpc.Net.Client" Version="2.66.0" />
<PackageReference Include="Grpc.Tools" Version="2.67.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<Protobuf Remove="..\Flawless.Shared\Protos\**" />
</ItemGroup>
</Project>

View File

@ -1,21 +0,0 @@
syntax = "proto3";
option csharp_namespace = "Flawless.Server";
package greet;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply);
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings.
message HelloReply {
string message = 1;
}

View File

@ -1,5 +0,0 @@
namespace Flawless.Client;
public class TestFlight
{
}

View File

@ -7,11 +7,13 @@
</PropertyGroup>
<ItemGroup>
<Protobuf Include="Protos\greet.proto" GrpcServices="Server"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.10" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<Protobuf Include="..\Flawless.Shared\Protos\*.proto" GrpcServices="Server"/>
<PackageReference Include="Grpc.AspNetCore" Version="2.57.0"/>
<Protobuf Update="..\Flawless.Shared\Protos\auth.proto">
<Link>Protos\auth.proto</Link>
</Protobuf>
</ItemGroup>
</Project>

View File

@ -1,16 +1,51 @@
using Flawless.Server.Services;
using Flawless.Server.Utility;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
var builder = WebApplication.CreateBuilder(args);
bool init = true;
while (init)
{
init = false;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddGrpc(x =>
{
});
// Add services to the container.
builder.Services.AddGrpc();
builder.Services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(o =>
{
o.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
RequireExpirationTime = true,
ValidateAudience = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = AuthUtility.SecurityKey,
ValidIssuer = AuthUtility.Issuer,
ValidAudience = AuthUtility.Audience,
ClockSkew = TimeSpan.Zero,
};
});
var app = builder.Build();
builder.Services.AddAuthorization();
// Configure the HTTP request pipeline.
app.MapGrpcService<GreeterService>();
app.MapGet("/",
() =>
"Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909");
using var app = builder.Build();
app.Run();
// Enable call router
app.UseRouting();
// Enable authentication
app.UseAuthentication();
app.UseAuthorization();
// Configure the HTTP request pipeline.
app.MapGrpcService<AuthService>();
app.MapGet("/",
() => "Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909");
app.Run();
}

View File

@ -1,21 +0,0 @@
syntax = "proto3";
option csharp_namespace = "Flawless.Server";
package greet;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply);
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings.
message HelloReply {
string message = 1;
}

View File

@ -0,0 +1,58 @@
using Flawless.Api;
using Flawless.Server.Utility;
using Google.Protobuf.WellKnownTypes;
using Grpc.Core;
using Microsoft.AspNetCore.Authorization;
namespace Flawless.Server.Services;
public class AuthService : Auth.AuthBase
{
private ILogger<AuthService> _logger;
public AuthService(ILogger<AuthService> logger)
{
_logger = logger;
}
public override Task<AuthResult> GainToken(AuthRequest request, ServerCallContext context)
{
if (request.UserName != "admin")
{
return Task.FromResult(new AuthResult()
{
Token = "",
Result = -1,
Message = "Invalid username or password"
});
}
var token = AuthUtility.GenerateJwtToken(request.UserName, request.Expires);
_logger.LogInformation($"User '{request.UserName}' has been login in.'");
return Task.FromResult(new AuthResult
{
Token = token,
Result = 0
});
}
[Authorize]
public override Task<AuthUserInfo> GetUserInfo(Empty request, ServerCallContext context)
{
return Task.FromResult(new AuthUserInfo
{
UserName = context.GetHttpContext().User.Identity?.Name ?? string.Empty,
IsSystemAdmin = true,
UserId = 1000
});
}
[Authorize]
public override Task<Empty> Validate(Empty request, ServerCallContext context)
{
return Task.FromResult(new Empty());
}
}

View File

@ -1,22 +0,0 @@
using Grpc.Core;
using Flawless.Server;
namespace Flawless.Server.Services;
public class GreeterService : Greeter.GreeterBase
{
private readonly ILogger<GreeterService> _logger;
public GreeterService(ILogger<GreeterService> logger)
{
_logger = logger;
}
public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
{
return Task.FromResult(new HelloReply
{
Message = "Hello " + request.Name
});
}
}

View File

@ -0,0 +1,72 @@
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.IdentityModel.Tokens;
namespace Flawless.Server.Utility;
public static class AuthUtility
{
private static JwtSecurityTokenHandler _tokenHandler = new();
private static SymmetricSecurityKey? _key;
public static string GenerateSecret(
string randomRange = "abcdefghijklmnopqrstuvwxyz1234567890!@#$%^&*()_+=-",
int length = 256 / 8)
{
var rng = Random.Shared;
String ran = "";
for (int i = 0; i < length; i++)
{
int x = rng.Next(randomRange.Length);
ran += randomRange[x];
}
return ran;
}
public static string JwtSecret { get; private set; } = GenerateSecret();
public static string Issuer { get; private set; } = Environment.GetEnvironmentVariable("issuer") ?? "jwt";
public static string Audience { get; private set; } = Environment.GetEnvironmentVariable("audience") ?? "jwt";
public static SymmetricSecurityKey SecurityKey
{
get
{
if (_key == null) _key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(JwtSecret));
return _key;
}
}
public static void ResetKey(string issuer, string audience, string? keySecret = null)
{
JwtSecret = keySecret ?? GenerateSecret();
Issuer = issuer;
Audience = audience;
_key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(JwtSecret));
}
public static string GenerateJwtToken(string username, uint expires)
{
var credentials = new SigningCredentials(SecurityKey, SecurityAlgorithms.HmacSha256Signature);
var claims = new List<Claim>
{
new (ClaimTypes.Name, username),
};
var token = _tokenHandler.CreateJwtSecurityToken(
issuer: Issuer,
audience: Audience,
subject: new ClaimsIdentity(claims),
expires: DateTime.Now.AddSeconds(expires),
issuedAt: DateTime.Now,
notBefore: DateTime.Now,
signingCredentials: credentials);
return _tokenHandler.WriteToken(token);
}
}

View File

@ -0,0 +1,34 @@
syntax = "proto3";
option csharp_namespace = "Flawless.Api";
import "google/protobuf/empty.proto";
service Auth {
rpc GainToken(AuthRequest) returns (AuthResult);
rpc GetUserInfo(google.protobuf.Empty) returns (AuthUserInfo);
rpc Validate(google.protobuf.Empty) returns (google.protobuf.Empty);
}
message RegisterAuthRequest {
string user_name = 1;
string password = 2;
}
message AuthRequest {
string user_name = 1;
string password = 2;
uint32 expires = 3;
}
message AuthResult {
int32 result = 1;
string message = 2;
string token = 3;
}
message AuthUserInfo {
uint64 user_id = 1;
string user_name = 2;
uint64 last_login = 3;
bool is_system_admin = 4;
}

View File

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.28.2" />
<PackageReference Include="Grpc.Core.Api" Version="2.66.0" />
<PackageReference Include="Grpc.Net.Client" Version="2.66.0" />
<PackageReference Include="Grpc.Tools" Version="2.67.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<Protobuf Include="..\Flawless.Shared\Protos\*.proto" GrpcServices="Client"/>
</ItemGroup>
</Project>

View File

@ -0,0 +1,31 @@
// See https://aka.ms/new-console-template for more information
using Flawless.Api;
using Google.Protobuf.WellKnownTypes;
using Grpc.Core;
using Grpc.Net.Client;
var path = "http://localhost:5150";
var rpcChannel = GrpcChannel.ForAddress(path);
var authService = new Auth.AuthClient(rpcChannel);
var result = await authService.GainTokenAsync(new AuthRequest()
{
UserName = "admin",
Expires = 10,
Password = "password"
});
if (result.Result == 0)
{
Console.WriteLine($"Token granted: {result.Token}");
// Thread.Sleep(5 * 1000);
var userInfo = await authService.GetUserInfoAsync(new Empty(), new Metadata()
{
{ "Authorization", $"Bearer {result.Token}" }
});
Console.WriteLine($"UserName: {userInfo.UserName}\nUID: {userInfo.UserId}\nIs Admin: {userInfo.IsSystemAdmin}");
}