feat(core): First try on ASP.NET
This commit is contained in:
parent
554589a7df
commit
f2a312aabc
@ -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
|
||||
|
||||
4
Flawless-Version-Control.sln.DotSettings.user
Normal file
4
Flawless-Version-Control.sln.DotSettings.user
Normal 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>
|
||||
@ -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>
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
namespace Flawless.Client;
|
||||
|
||||
public class TestFlight
|
||||
{
|
||||
}
|
||||
@ -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>
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
58
Flawless.Server/Services/AuthService.cs
Normal file
58
Flawless.Server/Services/AuthService.cs
Normal 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());
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
});
|
||||
}
|
||||
}
|
||||
72
Flawless.Server/Utility/AuthUtility.cs
Normal file
72
Flawless.Server/Utility/AuthUtility.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
34
Flawless.Shared/Protos/auth.proto
Normal file
34
Flawless.Shared/Protos/auth.proto
Normal 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;
|
||||
}
|
||||
@ -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>
|
||||
31
Flawless.Test.ConsoleApplication/Program.cs
Normal file
31
Flawless.Test.ConsoleApplication/Program.cs
Normal 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}");
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user