浏览代码

Authorization, added retry policy and circuit breaker.

tags/v1.1.0^2
Nemanja Grkovic 3 年前
父节点
当前提交
26ffa8b8f1

+ 16
- 0
GrpcShared/DTO/TokenMessage.cs 查看文件

@@ -0,0 +1,16 @@
using ProtoBuf;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GrpcShared.DTO
{
[ProtoContract]
public class TokenMessage
{
[ProtoMember(1)]
public string? Token { get; set; }
}
}

+ 20
- 0
GrpcShared/DTO/User/UserInfoResponse.cs 查看文件

@@ -0,0 +1,20 @@
using ProtoBuf;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GrpcShared.DTO.User
{
[ProtoContract]
public class UserInfoResponse
{
[ProtoMember(1)]
public string? email { get; set; }
[ProtoMember(2)]
public string? id { get; set; }
[ProtoMember(3)]
public string? display_name { get; set; }
}
}

+ 2
- 0
GrpcShared/GrpcShared.csproj 查看文件

@@ -8,7 +8,9 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="protobuf-net.BuildTools" Version="3.1.17">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

+ 4
- 1
GrpcShared/Interfaces/IAuthService.cs 查看文件

@@ -1,4 +1,6 @@
using GrpcShared.DTO.Auth;
using GrpcShared.DTO;
using GrpcShared.DTO.Auth;
using GrpcShared.DTO.User;
using ProtoBuf.Grpc.Configuration;

namespace GrpcShared.Interfaces
@@ -8,6 +10,7 @@ namespace GrpcShared.Interfaces
{
Task<TokenResponse> GetAccessToken(TokenRequest code);
Task<CodeRequest> GetAuthParams();
Task<UserInfoResponse> GetUserInfo(TokenMessage token);
//Task<ClientSecrets> GetClientSecrets();
}
}

+ 1
- 0
IdentityProvider/IdentityProvider.csproj 查看文件

@@ -15,6 +15,7 @@
<PackageReference Include="protobuf-net.Grpc.AspNetCore" Version="1.0.152" />
<PackageReference Include="protobuf-net.Grpc.AspNetCore.Reflection" Version="1.0.152" />
<PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.22.0" />
</ItemGroup>

<ItemGroup>

+ 2
- 0
IdentityProvider/Program.cs 查看文件

@@ -5,6 +5,7 @@ using ProtoBuf.Grpc.Server;
using Microsoft.Extensions.Options;
using GrpcShared.DTO.Auth;
using SpotifyService.Services;
using Blazored.SessionStorage;

var builder = WebApplication.CreateBuilder(args);
#if DEBUG
@@ -36,6 +37,7 @@ builder.Services.AddEndpointsApiExplorer();
builder.Services.AddGrpc();
builder.Services.AddCodeFirstGrpc();
builder.Services.AddCodeFirstGrpcReflection();
builder.Services.AddBlazoredSessionStorage();
//call spotify api
builder.Services.AddHttpClient();


+ 32
- 0
IdentityProvider/Services/AuthService.cs 查看文件

@@ -1,11 +1,17 @@
//using IdentityProvider.Protos.AuthService;
using Blazored.SessionStorage;
using Grpc.Net.Client;
using GrpcShared;
using GrpcShared.DTO;
using GrpcShared.DTO.Auth;
using GrpcShared.DTO.User;
using GrpcShared.Interfaces;
using Microsoft.Extensions.Options;
using Microsoft.Net.Http.Headers;
using Newtonsoft.Json;
using System.Diagnostics;
using System.IdentityModel.Tokens.Jwt;
using System.IO;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
@@ -17,6 +23,7 @@ namespace IdentityProvider.Services
private readonly ILogger<AuthService> _logger;
private readonly CodeRequest _params;
private readonly IHttpClientFactory _httpClientFactory;
//private ISessionStorageService _sessionStorageService;
public AuthService(ILogger<AuthService> logger, IOptions<CodeRequest> options, IHttpClientFactory httpClientFactory)
{
_logger = logger;
@@ -74,5 +81,30 @@ namespace IdentityProvider.Services
return await Task.FromResult(authParams);
}

public async Task<UserInfoResponse> GetUserInfo(TokenMessage tokenM)
{
//var des = JsonConvert.DeserializeObject<TokenMessage>(tokenM);
//var tokenStorage = _sessionStorageService.GetItemAsync<string>("token");

//hc.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authToken);
//var response = hc.GetAsync(userInfoUrl).Result;
//dynamic userInfo = response.Content.ReadAsAsync().Result;
//return userInfo;

var http = _httpClientFactory.CreateClient();
http.BaseAddress = new Uri("https://api.spotify.com/v1/me");
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokenM.Token);
var response = http.GetAsync(http.BaseAddress).Result;
var userInfo = JsonConvert.DeserializeObject<UserInfoResponse>(await response.Content.ReadAsStringAsync())!;
return userInfo;

//http.DefaultRequestHeaders.Add(HeaderNames.Authorization, "Bearer " + token.Token);

//var response = await http.GetAsync(http.BaseAddress + "me");

//var user = JsonConvert.DeserializeObject<UserInfoResponse>(await response.Content.ReadAsStringAsync());

//return await Task.FromResult(user!);
}
}
}

+ 1
- 1
IdentityProvider/appsettings.json 查看文件

@@ -14,7 +14,7 @@
"AuthParams": {
"ClientId": "83e1d09876b049c4bb1953185a4b3bfb",
"RedirectURI": "https://localhost:44342/callback",
"Scope": "user-read-currently-playing user-read-email user-library-modify user-top-read",
"Scope": "user-read-currently-playing user-read-email user-library-modify user-top-read user-read-private",
"ClientSecret": "ea752433d0774fad87fab5c1ee788c8d"
}
}

+ 1
- 0
NemAnCore/NemAnBlazor.csproj 查看文件

@@ -10,6 +10,7 @@
<PackageReference Include="Blazored.SessionStorage" Version="2.2.0" />
<PackageReference Include="Grpc.Net.Client" Version="2.47.0" />
<PackageReference Include="Grpc.Net.Client.Web" Version="2.47.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="6.0.8" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="6.0.7" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="6.0.7" PrivateAssets="all" />
<PackageReference Include="protobuf-net.Grpc" Version="1.0.171" />

+ 24
- 5
NemAnCore/Pages/FetchData.razor 查看文件

@@ -1,32 +1,51 @@
@page "/search"
@using Grpc.Core
@using GrpcShared.DTO
@using GrpcShared.DTO.Search
@using GrpcShared.DTO.Track.MultipleTrack
@using GrpcShared.DTO.Track.SingleTrack
@using NemAnBlazor.Services.Interfaces
@using System.Diagnostics
@*@inject HttpClient Http*@
@inject Blazored.SessionStorage.ISessionStorageService sessionStorage
@inject ITrackClientService SearchService
@inject IAuthClientService AuthService



<PageTitle>Search</PageTitle>

<h1>Search</h1>

<button class="btn btn-primary" @onclick="Click">Click me</button>



@code {
protected override async Task OnInitializedAsync()
{
var token = await sessionStorage.GetItemAsync<string>("token");

//SearchRequest request = new() { Query = "aitch", Type = "track", Token = token };
//SearchResponse searchResponse = await SearchService.GetListSearchAsync(request);

MultipleTrackRequest mreq = new() { Ids = new List<string>(){"3JAeYOjyJodI4PRs44lx2l", "6clZa1yrZe7pJrYFUcD9KW"}, Token = token };
MultipleTrackResponse multipleTrackResponse = await SearchService.GetListMultipleTrackAsync(mreq);


//MultipleTrackRequest mreq = new() { Ids = new List<string>(){"3JAeYOjyJodI4PRs44lx2l", "6clZa1yrZe7pJrYFUcD9KW"}, Token = token };
//MultipleTrackResponse multipleTrackResponse = await SearchService.GetListMultipleTrackAsync(mreq);

//SingleTrackRequest singleTrackRequest = new() { Id = "3JAeYOjyJodI4PRs44lx2l", Token = token };
//SingleTrackResponse singleTrackResponse = await SearchService.GetListSingleTrackAsync(singleTrackRequest);
}

private async Task Click(){

var token = await sessionStorage.GetItemAsync<string>("token");
TokenMessage tm = new() { Token = token };

if ((await AuthService.GetUserInfo(tm)) != null)
{
SearchRequest request = new() { Query = "aitch", Type = "track", Token = token };
SearchResponse searchResponse = await SearchService.GetListSearchAsync(request);
}
}


}

+ 35
- 1
NemAnCore/Services/AuthClientService.cs 查看文件

@@ -4,6 +4,11 @@ using GrpcShared.Interfaces;
using NemAnBlazor.Services.Interfaces;
using ProtoBuf.Grpc.Client;
using GrpcShared;
using GrpcShared.DTO.User;
using GrpcShared.DTO;
using System.Security.Claims;
using Microsoft.AspNetCore.Components.Authorization;
using Blazored.SessionStorage;

namespace NemAnBlazor.Services
{
@@ -24,6 +29,35 @@ namespace NemAnBlazor.Services
{
return await _serviceClient.GetAuthParams();
}

public async Task<UserInfoResponse> GetUserInfo(TokenMessage token)
{
return await _serviceClient.GetUserInfo(token);
}

//public override async Task<AuthenticationState> GetAuthenticationStateAsync()
//{
// string token = await _sessionStorage.GetItemAsync<string>("token");

// //token = "BQBMgFm6jnFNWWeZEMGIRP_f-ENPid7Kw8JubAyuWAe4JK0S1DPFGlaAdZ_Fey6ePkCnz8-cqC0oyRmrciWUy5ISUTQKDe8PTQn4iBRMYCgM0n4GnS1xAErHJcm4Vpu2TAngk-4vQUOfTQRcedNTfCaHKP4uFJgTlTI7JHGrtB-_EZLnFcZ2OQe31oFQIJ1wM3ZtvwnN";
// if (token == null) return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));

// var userInfo = await _serviceClient.GetUserInfo(new TokenMessage { Token = token });

// List<Claim> claims = new();

// claims.Add(new Claim("email", userInfo.email!));
// claims.Add(new Claim("id", userInfo.id!));
// claims.Add(new Claim("name", userInfo.display_name!));

// ClaimsIdentity identity = new(claims, "jwt");
// //ClaimsIdentity identity = new();
// ClaimsPrincipal user = new(identity);
// AuthenticationState state = new(user);

// NotifyAuthenticationStateChanged(Task.FromResult(state));

// return state;
//}
}
}

+ 3
- 0
NemAnCore/Services/Interfaces/IAuthClientService.cs 查看文件

@@ -1,5 +1,7 @@
using GrpcShared;
using GrpcShared.DTO;
using GrpcShared.DTO.Auth;
using GrpcShared.DTO.User;

namespace NemAnBlazor.Services.Interfaces
{
@@ -7,5 +9,6 @@ namespace NemAnBlazor.Services.Interfaces
{
Task<TokenResponse> GetAccessToken(TokenRequest tokenRequest);
Task<CodeRequest> GetAuthParams();
Task<UserInfoResponse> GetUserInfo(TokenMessage token);
}
}

+ 48
- 4
gRPCServer/Program.cs 查看文件

@@ -1,7 +1,11 @@
using Microsoft.AspNetCore.Server.Kestrel.Core;
using NemAnBlazor.Services.Interfaces;
using NemAnBlazor.Services;
using ProtoBuf.Grpc.Server;
using SpotifyService.Services;

using GrpcShared.Interfaces;
using Polly;
using Polly.Extensions.Http;

var builder = WebApplication.CreateBuilder(args);

@@ -23,7 +27,46 @@ builder.Services.AddHttpClient("HttpClient", c =>
{
c.BaseAddress = new Uri(SpotifyService.GLOBALS.SPOTIFYURL);
c.DefaultRequestHeaders.Add("Accept", SpotifyService.GLOBALS.MEDIATYPE);
});
})
.SetHandlerLifetime(TimeSpan.FromMinutes(5))
.AddPolicyHandler(GetRetryPolicy())
.AddPolicyHandler(GetCircuitBreaker());

IAsyncPolicy<HttpResponseMessage> GetCircuitBreaker()
{
return HttpPolicyExtensions
.HandleTransientHttpError()
//the circuit will be cut if 25% of requests fail in a 60 second window, with a minimum of 7 requests in the 60 second window,
//then the circuit should be cut for 30 seconds:
.AdvancedCircuitBreakerAsync(0.25, TimeSpan.FromSeconds(60),
7, TimeSpan.FromSeconds(30), OnBreak, OnReset, OnHalfOpen);
}

void OnHalfOpen()
{
Console.WriteLine("Circuit in test mode, one request will be allowed.");
}

void OnReset()
{
Console.WriteLine("Circuit closed, requests flow normally.");
}

void OnBreak(DelegateResult<HttpResponseMessage> result, TimeSpan ts)
{
Console.WriteLine("Circuit cut, requests will not flow.");
}

IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
return HttpPolicyExtensions
// HttpRequestException, 5XX and 408
.HandleTransientHttpError()
// 404
.OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)
// Retry two times after delay
.WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(3, retryAttempt)));
}

builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();
@@ -64,8 +107,9 @@ app.UseGrpcWeb();
app.MapRazorPages();
app.MapControllers();

//app.MapGrpcService<WeatherService>();
//app.MapGrpcService<SearchService>().EnableGrpcWeb();
//app.MapGrpcService<AuthService>();
//app.MapGrpcService<AuthService>().EnableGrpcWeb();


app.MapCodeFirstGrpcReflectionService();


+ 6
- 5
gRPCServer/Services/TrackService.cs 查看文件

@@ -1,11 +1,14 @@
using Grpc.Core;
using Google.Rpc;
using Grpc.Core;
using GrpcShared;
using GrpcShared.DTO.Search;
using GrpcShared.DTO.Track.MultipleTrack;
using GrpcShared.DTO.Track.SaveTracks;
using GrpcShared.DTO.Track.SingleTrack;
using GrpcShared.Interfaces;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Net.Http.Headers;
using NemAnBlazor.Services;
using Newtonsoft.Json;
using System.Text;
using System.Text.Json;
@@ -17,16 +20,14 @@ namespace SpotifyService.Services
public class TrackService : ITrackService
{
private readonly IHttpClientFactory _httpClientFactory;


public TrackService(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}


public async Task<SearchResponse> ListSearchAsync(SearchRequest request)
{


var client = _httpClientFactory.CreateClient("HttpClient");

client.DefaultRequestHeaders.Add(HeaderNames.Authorization, "Bearer " + request.Token );

+ 3
- 0
gRPCServer/SpotifyService.csproj 查看文件

@@ -7,10 +7,13 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Google.Api.CommonProtos" Version="2.6.0" />
<PackageReference Include="Grpc.AspNetCore" Version="2.40.0" />
<PackageReference Include="Grpc.AspNetCore.Web" Version="2.47.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="6.0.8" />
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="6.0.8" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Polly" Version="7.2.3" />
<PackageReference Include="protobuf-net.Grpc" Version="1.0.171" />
<PackageReference Include="protobuf-net.Grpc.AspNetCore" Version="1.0.152" />
<PackageReference Include="protobuf-net.Grpc.AspNetCore.Reflection" Version="1.0.152" />

正在加载...
取消
保存