рдмреНрд▓реЗрдЬрд╝рд░ рдХреНрд▓рд╛рдЗрдВрдЯ рд╕рд╛рдЗрдб рдСрдирд▓рд╛рдЗрди рд╕реНрдЯреЛрд░: рднрд╛рдЧ 1 - рдкреНрд░рд╛рдзрд┐рдХрд░рдг oidc (oauth2) + рдкрд╣рдЪрд╛рди рд╕рд░реНрд╡рд░ 4


рдирдорд╕реНрдХрд╛рд░, рд╣реЗрдмреНрд░! рддреЛ рд╣рд╛рдБ, рдЕрдкрдиреЗ рдкрд┐рдЫрд▓реЗ рд▓реЗрдЦ рдореЗрдВ рдореИрдВрдиреЗ рдмреНрд▓реЗрдЬрд╝рд░ рд╡рд╛рд╕рдо рдкрд░ рдЯреЛрдбреЛ рд╕реВрдЪреА рдмрдирд╛рдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХреА рдФрд░ рд╕рдВрддреБрд╖реНрдЯ рдерд╛ред рдЕрдм рдореИрдВрдиреЗ рдЗрд╕реЗ рдЧрдВрднреАрд░рддрд╛ рд╕реЗ рд▓реЗрдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рдХрд┐рдпрд╛ред рдореИрдВ рдПрдХ рд╕рд╛рдзрд╛рд░рдг рдХрд╛рд▓реНрдкрдирд┐рдХ рдСрдирд▓рд╛рдЗрди рд╕реНрдЯреЛрд░ рдХреЗ рд▓рд┐рдП рдмреНрд▓реЗрдЬрд╝рд░ рдкрд░ рдПрдХ рд╕рд╛рдзрд╛рд░рдг рдПрд╕рдкреАрдП рдпреВрдЖрдИ рдмрдирд╛рдКрдВрдЧрд╛ред рдЙрдкрдпреЛрдЧ рд╡рд┐рдХрд▓реНрдк рдХрд╛ рдореБрдХрд╛рдмрд▓рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЬрд┐рддрдирд╛ рд╕рдВрднрд╡ рд╣реЛ рдЙрддрдирд╛ рдХрд░реАрдмред рдЖрд░рдВрдн рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдореИрдВ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛рдУрдВ рдХреА рднреВрдорд┐рдХрд╛ рдФрд░ рдЙрдирдХреЗ рдкреГрдердХреНрдХрд░рдг рдХреЗ рд░рд┐рдХреЙрд░реНрдб рдХреЛ рднреВрдорд┐рдХрд╛рдУрдВ рджреНрд╡рд╛рд░рд╛ рджрд░реНрдЬ рдХрд░реВрдБрдЧрд╛, рддрд╛рдХрд┐ рд╡реНрдпрд╡рд╕реНрдерд╛рдкрдХ рдФрд░ рдирд┐рдпрдорд┐рдд рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЛ рдереЛрдбрд╝рд╛ рдЕрд▓рдЧ рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рджрд┐рдЦрд╛рдИ рджреЗред рдореИрдВрдиреЗ рдбреЙрдХрдЯрд░ рдЫрд╡рд┐рдпреЛрдВ рдореЗрдВ рднреА рдпрд╣ рд╕рдм рдПрдХрддреНрд░ рдХрд┐рдпрд╛ рдФрд░ рдЗрд╕реЗ рдбреЙрдХрдЯрд░ рд░рдЬрд┐рд╕реНрдЯреНрд░реА рдореЗрдВ рдЕрдкрд▓реЛрдб рдХрд┐рдпрд╛ред рд╡рд┐рд╡рд░рдг рдХреЗ рд▓рд┐рдП, рдмрд┐рд▓реНрд▓реА рдореЗрдВ рдЖрдкрдХрд╛ рд╕реНрд╡рд╛рдЧрдд рд╣реИред

рд╕рд╛рдордЧреНрд░реА




рд╕рдВрджрд░реНрдн


рд╕реНрд░реЛрдд рдХреЛрдб
рдбреЙрдХрдЯрд░ рд░рдЬрд┐рд╕реНрдЯреНрд░реА рдЫрд╡рд┐рдпрд╛рдВ

рд▓рд╛рдВрдЪ


рдпрд╣ рдЖрд╡рд╢реНрдпрдХ рд╣реИ рдХрд┐ рдЖрдкрдХреЗ рдкрд╛рд╕ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА docker compose ( tyk ) рдХреЗ рд╕рд╛рде docker рд╕реНрдерд╛рдкрд┐рдд рд╣реИ рдФрд░ рдЗрдВрдЯрд░рдиреЗрдЯ рдЬреБрдбрд╝рд╛ рд╣реБрдЖ рд╣реИ рдХреНрдпреЛрдВрдХрд┐ рдЖрдкрдХреЛ рдЗрди рдЪрд┐рддреНрд░реЛрдВ рдХреЛ рдбрд╛рдЙрдирд▓реЛрдб рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрдЧреАред

рдорд╛рдЗрдХреНрд░реЛрд╕реЙрдлрд╝реНрдЯ рдХреЗ рд▓рд┐рдП рдЖрд╡рд╢реНрдпрдХ рдкреНрд░рдорд╛рдг рдкрддреНрд░ рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП, .net рдХреЛрд░ рдХреЛ рд╕реНрдерд╛рдкрд┐рдд рдХрд░рдиреЗ рдФрд░ рдЗрди рдХрдорд╛рдВрдб рдХреЛ Windows PowerShell рдореЗрдВ рдЪрд▓рд╛рдиреЗ рдХреЗ рд▓рд┐рдПред

dotnet --info dotnet dev-certs https --trust dotnet dev-certs https -ep $env:APPDATA\ASP.NET\Https\blazor-eshop-api.pfx -p 1234Qwert dotnet dev-certs https -ep $env:APPDATA\ASP.NET\Https\blazor-eshop-spa-angular.pfx -p 1234Qwert dotnet dev-certs https -ep $env:APPDATA\ASP.NET\Https\blazor-eshop-spa-blazor.pfx -p 1234Qwert dotnet dev-certs https -ep $env:APPDATA\ASP.NET\Https\blazor-eshop-sso.pfx -p 1234Qwert 


рдкреНрд░реЛрдЬреЗрдХреНрдЯ рд╢реБрд░реВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдЖрдкрдХреЛ docker-compose.yml рдлрд╝рд╛рдЗрд▓ рдбрд╛рдЙрдирд▓реЛрдб рдХрд░рдиреА рд╣реЛрдЧреА (рдпрд╛ рдЙрд╕реА рдирд╛рдо рд╕реЗ рдлрд╝рд╛рдЗрд▓ рдореЗрдВ рдЗрд╕рдХреА рд╕рд╛рдордЧреНрд░реА рдХреА рдкреНрд░рддрд┐рд▓рд┐рдкрд┐ рдмрдирд╛рдПрдБ) рдФрд░ docker-compose рдХрдорд╛рдВрдб рдХреЛ рдЙрд╕ рдирд┐рд░реНрджреЗрд╢рд┐рдХрд╛ рдореЗрдВ рдЪрд▓рд╛рдПрдБ рдЬрд╣рд╛рдБ рдпрд╣ рдлрд╝рд╛рдЗрд▓ рд╕реНрдерд┐рдд рд╣реИред рд╕реВрдХреНрд╖реНрдорджрд░реНрд╢реА рдкрддреЗ рд╕реБрди рд░рд╣реЗ рд╣реИрдВ
https: // localhost: 8000
https: // localhost: 8001
https: // localhost: 8002
рдФрд░ https: // localhost: 8003 ред

рдХрд┐рд╕реА рдмреНрд░рд╛рдЙрдЬрд╝рд░ рдореЗрдВ WASM рдХреНрд▓рд╛рдЗрдВрдЯ рдХреЛ рдЕрдзрд┐рдХреГрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд▓рд╛рдЗрдмреНрд░реЗрд░реА


Www.nuget.org/packages/Sotsera.Blazor.Oidc рд╕реНрдерд╛рдкрд┐рдд рдХрд░реЗрдВ рдФрд░ рдЬреАрд╡рди рдХрд╛ рдЖрдирдВрдж рд▓реЗрдВ

рдкрд╣рдЪрд╛рди рд╕рд░реНрд╡рд░ 4 рдХреЛ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХрд░реЗрдВ


рд╕рд╛рдорд╛рдиреНрдп рддреМрд░ рдкрд░, рдореИрдВрдиреЗ рдПрдХ рд░реЗрдбреА-рдореЗрдб рд╕рд░реНрд╡рд░ рд▓рд┐рдпрд╛ рдФрд░ рдмрд╕ рд╡рд╣рд╛рдВ рдЕрдкрдиреЗ рдПрд╕рдкреАрдП рдХреНрд▓рд╛рдЗрдВрдЯ рдХреЗ рд▓рд┐рдП рд╕реЗрдЯрд┐рдВрдЧреНрд╕ рдЬреЛрдбрд╝реАрдВред рдкрд╣рдЪрд╛рди рд╕рд░реНрд╡рд░ 4 рдХреА рд╕реНрдерд╛рдкрдирд╛ рд╕реНрд╡рдпрдВ рдЗрд╕ рд▓реЗрдЦ рдХреЗ рджрд╛рдпрд░реЗ рд╕реЗ рдкрд░реЗ рд╣реИ рдХреНрдпреЛрдВрдХрд┐ рдпрд╣ рдмреНрд▓реЗрдЬрд╝рд░ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рд╣реИред рдпрджрд┐ рдЖрдк рд░реБрдЪрд┐ рд░рдЦрддреЗ рд╣реИрдВ, рддреЛ рдЖрдк рдореЗрд░реЗ рд╕реНрд░реЛрддреЛрдВ рдореЗрдВ рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВред

рд╣рдорд╛рд░реЗ рдХреНрд▓рд╛рдЗрдВрдЯ рдХреЛ рдЙрдкрд▓рдмреНрдз рдХреНрд▓рд╛рдЗрдВрдЯ рдХреА рд╕реВрдЪреА рдореЗрдВ рдЬреЛрдбрд╝реЗрдВ

  new Client { ClientId = "spaBlazorClient", ClientName = "SPA Blazor Client", RequireClientSecret = false, RequireConsent = false, RedirectUris = new List<string> { $"{clientsUrl["SpaBlazor"]}/oidc/callbacks/authentication-redirect", $"{clientsUrl["SpaBlazor"]}/_content/Sotsera.Blazor.Oidc/silent-renew.html", $"{clientsUrl["SpaBlazor"]}", }, PostLogoutRedirectUris = new List<string> { $"{clientsUrl["SpaBlazor"]}/oidc/callbacks/logout-redirect", $"{clientsUrl["SpaBlazor"]}", }, AllowedCorsOrigins = new List<string> { $"{clientsUrl["SpaBlazor"]}", }, AllowedGrantTypes = GrantTypes.Code, AllowedScopes = { "openid", "profile", "email", "api" }, AllowOfflineAccess = true, RefreshTokenUsage = TokenUsage.ReUse } 

JWT рдЯреЛрдХрди рдореЗрдВ рдЕрдзрд┐рдХ рднреВрдорд┐рдХрд╛рдПрдБ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рд╣рдо рдЕрдкрдиреЗ IProfileService рдХреЛ рд▓рд╛рдЧреВ рдХрд░рддреЗ рд╣реИрдВ

 using IdentityModel; using IdentityServer4.Models; using IdentityServer4.Services; using Microsoft.AspNetCore.Identity; using Microsoft.eShopOnContainers.Services.Identity.API.Models; using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Security.Claims; using System.Threading.Tasks; namespace Microsoft.eShopOnContainers.Services.Identity.API.Services { public class ProfileService : IProfileService { private readonly UserManager<ApplicationUser> _userManager; public ProfileService(UserManager<ApplicationUser> userManager) { _userManager = userManager; } async public Task GetProfileDataAsync(ProfileDataRequestContext context) { var subject = context.Subject ?? throw new ArgumentNullException(nameof(context.Subject)); var subjectId = subject.Claims.Where(x => x.Type == "sub").FirstOrDefault().Value; var user = await _userManager.FindByIdAsync(subjectId); if (user == null) throw new ArgumentException("Invalid subject identifier"); var claims = GetClaimsFromUser(user); context.IssuedClaims = claims.ToList(); var roles = await _userManager.GetRolesAsync(user); foreach (var role in roles) { context.IssuedClaims.Add(new Claim(JwtClaimTypes.Role, role)); } } async public Task IsActiveAsync(IsActiveContext context) { var subject = context.Subject ?? throw new ArgumentNullException(nameof(context.Subject)); var subjectId = subject.Claims.Where(x => x.Type == "sub").FirstOrDefault().Value; var user = await _userManager.FindByIdAsync(subjectId); context.IsActive = false; if (user != null) { if (_userManager.SupportsUserSecurityStamp) { var security_stamp = subject.Claims.Where(c => c.Type == "security_stamp").Select(c => c.Value).SingleOrDefault(); if (security_stamp != null) { var db_security_stamp = await _userManager.GetSecurityStampAsync(user); if (db_security_stamp != security_stamp) return; } } context.IsActive = !user.LockoutEnabled || !user.LockoutEnd.HasValue || user.LockoutEnd <= DateTime.Now; } } private IEnumerable<Claim> GetClaimsFromUser(ApplicationUser user) { var claims = new List<Claim> { new Claim(JwtClaimTypes.Subject, user.Id), new Claim(JwtClaimTypes.PreferredUserName, user.UserName), new Claim(JwtRegisteredClaimNames.UniqueName, user.UserName) }; if (!string.IsNullOrWhiteSpace(user.Name)) claims.Add(new Claim("name", user.Name)); if (!string.IsNullOrWhiteSpace(user.LastName)) claims.Add(new Claim("last_name", user.LastName)); if (!string.IsNullOrWhiteSpace(user.CardNumber)) claims.Add(new Claim("card_number", user.CardNumber)); if (!string.IsNullOrWhiteSpace(user.CardHolderName)) claims.Add(new Claim("card_holder", user.CardHolderName)); if (!string.IsNullOrWhiteSpace(user.SecurityNumber)) claims.Add(new Claim("card_security_number", user.SecurityNumber)); if (!string.IsNullOrWhiteSpace(user.Expiration)) claims.Add(new Claim("card_expiration", user.Expiration)); if (!string.IsNullOrWhiteSpace(user.City)) claims.Add(new Claim("address_city", user.City)); if (!string.IsNullOrWhiteSpace(user.Country)) claims.Add(new Claim("address_country", user.Country)); if (!string.IsNullOrWhiteSpace(user.State)) claims.Add(new Claim("address_state", user.State)); if (!string.IsNullOrWhiteSpace(user.Street)) claims.Add(new Claim("address_street", user.Street)); if (!string.IsNullOrWhiteSpace(user.ZipCode)) claims.Add(new Claim("address_zip_code", user.ZipCode)); if (_userManager.SupportsUserEmail) { claims.AddRange(new[] { new Claim(JwtClaimTypes.Email, user.Email), new Claim(JwtClaimTypes.EmailVerified, user.EmailConfirmed ? "true" : "false", ClaimValueTypes.Boolean) }); } if (_userManager.SupportsUserPhoneNumber && !string.IsNullOrWhiteSpace(user.PhoneNumber)) { claims.AddRange(new[] { new Claim(JwtClaimTypes.PhoneNumber, user.PhoneNumber), new Claim(JwtClaimTypes.PhoneNumberVerified, user.PhoneNumberConfirmed ? "true" : "false", ClaimValueTypes.Boolean) }); } return claims; } } } 

рдпрд╣рд╛рдБ рдХреЛрдб рдХреЗ рдЗрд╕ рдЯреБрдХрдбрд╝реЗ рдореЗрдВ рдкреВрд░реЗ рдмрд┐рдВрджреБ

 var roles = await _userManager.GetRolesAsync(user); foreach (var role in roles) { context.IssuedClaims.Add(new Claim(JwtClaimTypes.Role, role)); } 

рдФрд░ рдЗрд╕реЗ asp.net рдореЗрдВ рдЬреЛрдбрд╝реЗрдВ

 services.AddIdentityServer().AddProfileService<ProfileService>() 

рдкрд░рд┐рдпреЛрдЬрдирд╛ рдирд┐рд░реНрдорд╛рдг


рдпрд╣рд╛рдВ рдореИрдВрдиреЗ ASP.NET Core рдХреЛ рд╣реЛрд╕реНрдЯ рдХрд┐рдпрд╛ рдХреНрдпреЛрдВрдХрд┐ рд╕реЗрдЯрд┐рдВрдЧреНрд╕ рдХреЛ рдЯреНрд░рд╛рдВрд╕рдлрд░ рдХрд░рдирд╛ рдореЗрд░реЗ рд▓рд┐рдП рдЗрддрдирд╛ рдЖрд╕рд╛рди рдерд╛ред рдПрдХ рдЖрд╕рд╛рди рдЫрд╡рд┐ рдХреЛ рдЗрдХрдЯреНрдард╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЖрд╕рд╛рди рд╣реИред рдпрджрд┐ рдЖрдк рдХрдВрдЯреЗрдирд░ рдХреЗ рдЕрдВрджрд░ рдЪрд╛рд╣реЗрдВ рддреЛ рдирдЧрдиреЗрдХреНрд╕ рдХреА рдореЗрдЬрдмрд╛рдиреА рднреА рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред





рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдлрд╝рд╛рдЗрд▓ рдФрд░ рдкрд░реНрдпрд╛рд╡рд░рдг рдЪрд░ рд╕реЗ рд╕реЗрдЯрд┐рдВрдЧ рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдХрд░рдирд╛


рд╕рд░реНрд╡рд░ рд╕рд╛рдЗрдб


рд╕реЗрдЯрд┐рдВрдЧреНрд╕ рдореЙрдбрд▓ рдЬреЛрдбрд╝реЗрдВ

 public class ConfigModel { public string SsoUri { get; set; } = string.Empty; public string ApiUri { get; set; } = string.Empty; } 

рдЗрд╕реЗ Startup.cs рдореЗрдВ рдкрдВрдЬреАрдХреГрдд рдХрд░реЗрдВ

 public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.AddResponseCompression(opts => { opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat( new[] { "application/octet-stream" }); }); services.Configure<ConfigModel>(Configuration); } 

рдЧреНрд░рд╛рд╣рдХ рдХреЗ рд░реВрдк рдореЗрдВ рдкрд╛рд╕ рдХрд░реЗрдВ

 using BlazorEShop.Shared.Presentation; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; namespace BlazorEShop.Spa.BlazorWasm.Server.Controllers { [Route("api/v1/config")] [ApiController] public class ConfigController : ControllerBase { private readonly IOptionsSnapshot<ConfigModel> _configuration; public ConfigController(IOptionsSnapshot<ConfigModel> configuration) { _configuration = configuration; } // GET: api/<controller> [HttpGet] public ConfigModel Get() { return _configuration.Value; } } } 

рдЧреНрд░рд╛рд╣рдХ рдкрдХреНрд╖


рд╣рдо рд╕рд░реНрд╡рд░ рд╕реЗ рд╕реЗрдЯрд┐рдВрдЧреНрд╕ рдкреНрд░рд╛рдкреНрдд рдХрд░рддреЗ рд╣реИрдВ рдФрд░ рдЙрдиреНрд╣реЗрдВ рд╣рдорд╛рд░реЗ DI рдХрдВрдЯреЗрдирд░ рдореЗрдВ рдЬреЛрдбрд╝рддреЗ рд╣реИрдВ

 using System; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using BlazorEShop.Shared.Presentation; using Microsoft.AspNetCore.Blazor.Hosting; using Microsoft.AspNetCore.Components; using Microsoft.Extensions.DependencyInjection; namespace BlazorEShop.Spa.BlazorWasm.Client { public class Program { public static void Main(string[] args) { Task.Run(async () => { ConfigModel cfg = null; var host = BlazorWebAssemblyHost.CreateDefaultBuilder().Build(); using (var scope = host.Services.CreateScope()) { var nm = scope.ServiceProvider.GetRequiredService<NavigationManager>(); var uri = nm.BaseUri; Console.WriteLine($"BASE URI: {uri}"); cfg = await GetConfig($"{(uri.EndsWith('/') ? uri : uri + "/")}api/v1/config"); } await BlazorWebAssemblyHost .CreateDefaultBuilder() .ConfigureServices(x => x.AddScoped<ConfigModel>(y => cfg)) .UseBlazorStartup<Startup>() .Build() .StartAsync() .ContinueWith((a, b) => Console.WriteLine(a.Exception), null); }); Console.WriteLine("END MAIN"); } private static async Task<ConfigModel> GetConfig(string url) { using var client = new HttpClient(); var cfg = await client .GetJsonAsync<ConfigModel>(url); return cfg; } } } 

рдЪреВрдБрдХрд┐ рдмреНрд▓реЗрдЬрд╝рд░ рд╡рд╛рд╕рдо, рдПрд╕реНрдХреНрд╡рд╛рдпрдб рд╡реЗрди рдореЗрди рдХрд╛ рд╕рдорд░реНрдерди рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИ, рдФрд░ рдЯрд╛рд╕реНрдХ рд╕реЗ рд░рд┐рдЬрд▓реНрдЯ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рдЧрддрд┐рд░реЛрдз рдХреА рдУрд░ рд▓реЗ рдЬрд╛рддрд╛ рд╣реИ рдХреНрдпреЛрдВрдХрд┐ рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдХреЗрд╡рд▓ рдПрдХ рд╣реА рдзрд╛рдЧрд╛ рд╣реЛрддрд╛ рд╣реИ, рд╣рдореЗрдВ рдЯрд╛рд╕реНрдХ рдореЗрдВ рд╕рдм рдХреБрдЫ рд▓рдкреЗрдЯрдирд╛ рдкрдбрд╝рддрд╛ рдерд╛ред (async () => {});

рдХреНрд▓рд╛рдЗрдВрдЯ рдкрдХреНрд╖ рдкрд░ oidc (oauth2) рд▓рд╛рдЗрдмреНрд░реЗрд░реА рдХреЛ рд╕рдХреНрд░рд┐рдп рдХрд░рдирд╛


рд╣рдо рд╕реЗрд╡рд╛рдУрдВ рдХреЛ рдХреЙрд▓ рдХрд░рддреЗ рд╣реИрдВред рд╣рдореЗрдВ рдЬреЛ рд╕реЗрдЯрд┐рдВрдЧреНрд╕ рдорд┐рд▓рддреА рд╣реИ, рд╡рд╣ рдХреЙрдиреНрдлрд┐рдЧрд░реЗрд╢рди рдХреЗ рдЕрдВрджрд░ рд╕рд░реНрд╡рд░ рд╕реЗ рдкреНрд░рд╛рдкреНрдд рд╣реЛрддреА рд╣реИред

 using Microsoft.AspNetCore.Components.Builder; using Microsoft.Extensions.DependencyInjection; using Sotsera.Blazor.Oidc; using System; using System.Net.Http; using System.Threading.Tasks; using BlazorEShop.Shared.Presentation; using Microsoft.AspNetCore.Components; namespace BlazorEShop.Spa.BlazorWasm.Client { public class Startup { public async void ConfigureServices(IServiceCollection services) { var provider = services.BuildServiceProvider(); var cfg = provider.GetService<ConfigModel>(); services.AddOidc(new Uri(cfg.SsoUri), (settings, siteUri) => { settings.UseDefaultCallbackUris(siteUri); settings.ClientId = "spaBlazorClient"; settings.ResponseType = "code"; settings.Scope = "openid profile email api"; settings.UseRedirectToCallerAfterAuthenticationRedirect(); settings.UseRedirectToCallerAfterLogoutRedirect(); settings.MinimumLogeLevel = Microsoft.Extensions.Logging.LogLevel.Information; settings.LoadUserInfo = true; settings.FilterProtocolClaims = true; settings.MonitorSession = true; settings.StorageType = Sotsera.Blazor.Oidc.Configuration.Model.StorageType.LocalStorage; }); } public void Configure(IComponentsApplicationBuilder app) { app.AddComponent<App>("app"); } } } 

App.razor рдХреЗ рдореБрдЦреНрдп рдШрдЯрдХ рдХреЛ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХрд░рдирд╛


App.blazor -Change рдХрд░реЗрдВ рддрд╛рдХрд┐ рдЕрдзрд┐рдХреГрдд рдФрд░ рдЕрдирдзрд┐рдХреГрдд рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдЕрд▓рдЧ-рдЕрд▓рдЧ рдкрд╛рда рджреЗрдЦреЗрдВ рдФрд░ рдкреБрд╕реНрддрдХрд╛рд▓рдп рд╕реЗ oCc рдХреЗ рд▓рд┐рдП рдорд╛рд░реНрдЧ рдЬреБрдбрд╝реЗ рд╣реБрдП рд╣реИрдВ

 @using BlazorEShop.Shared.Presentation @using Microsoft.AspNetCore.Components @using Microsoft.Extensions.DependencyInjection @using Sotsera.Blazor.Oidc @inject IUserManager UserManager <Router AppAssembly="@typeof(Program).Assembly" AdditionalAssemblies="new[] { typeof(IUserManager).Assembly }"> <Found Context="routeData"> <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"> <NotAuthorized> <h3>Sorry</h3> <p>You're not authorized to reach this page.</p> <p>You may need to log in as a different user.</p> </NotAuthorized> <Authorizing> <h3>Authentication in progress</h3> </Authorizing> </AuthorizeRouteView> </Found> <NotFound> <CascadingAuthenticationState> <LayoutView Layout="@typeof(MainLayout)"> <h3>Sorry</h3> <p>Sorry, there's nothing at this address.</p> </LayoutView> </CascadingAuthenticationState> </NotFound> </Router> 

рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рд▓реЙрдЧрд┐рди рдФрд░ рд▓реЙрдЧрдЖрдЙрдЯ


рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдкреНрд░рдмрдВрдзрди IUserManager рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдЗрд╕реЗ DI рдХрдВрдЯреЗрдирд░ рд╕реЗ рдкреНрд░рд╛рдкреНрдд рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП:

 @inject IUserManager UserManager @using Sotsera.Blazor.Oidc @using Microsoft.Extensions.DependencyInjection <AuthorizeView> <Authorized> <span class="login-display-name mr-3"> Hello, @context.User.Identity.Name! </span> <button type="button" class="btn btn-primary btn-sm" @onclick="LogoutRedirect"> Log out </button> </Authorized> <NotAuthorized> <button type="button" class="btn btn-primary btn-sm" @onclick="LoginRedirect"> Log in </button> </NotAuthorized> </AuthorizeView> @code { public async void LoginRedirect() => await UserManager.BeginAuthenticationAsync(p => p.WithRedirect()); public async void LogoutRedirect() => await UserManager.BeginLogoutAsync(p => p.WithRedirect()); } 

рдЕрдзрд┐рдХреГрдд рдФрд░ рдЧреИрд░-рдЕрдзрд┐рдХреГрдд рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛рдУрдВ рдХреЗ рд▓рд┐рдП рд╡рд┐рднрд┐рдиреНрди рд╕реВрдЪрдирд╛рдУрдВ рдХрд╛ рдкреНрд░рджрд░реНрд╢рди


рдЕрдм, рдкреНрд░рд╛рдзрд┐рдХрд░рдг рдХреЗ рдХрд┐рд╕реА рднреА рднрд╛рдЧ рдореЗрдВ, AuthorizeView рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ, рдЖрдк рдЙрди рдХреНрд╖реЗрддреНрд░реЛрдВ рдХреЛ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдЬреЛ рдХреЗрд╡рд▓ рдЕрдзрд┐рдХреГрдд рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рджреЗрдЦреЗрдВрдЧреЗред рдЖрдк рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛рдУрдВ рдХреЛ рдпрд╣ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд░реЛрд▓реНрд╕ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдХрд┐ рд╡реЗ рдХрд┐рд╕ рднреВрдорд┐рдХрд╛ рдХреЗ рд╕рд╛рде рдЗрд╕ рд╕рд╛рдордЧреНрд░реА рдХреЛ рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВред

 <AuthorizeView Roles="admin, administrator"> <Authorized> <p>User Info</p> <p>@context.User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)</p> @foreach (var c in context.User.Claims) { <p>@c.Type : @c.Value : @string.Join(";", c.Properties.Select(x => $"{x.Key} : {x.Value}"))</p> } </Authorized> <NotAuthorized> <p>       admin   administrator</p> </NotAuthorized> </AuthorizeView> 

рдкреНрд░рд╛рдзрд┐рдХрд░рдг рдФрд░ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рднреВрдорд┐рдХрд╛рдУрдВ рдХреЗ рдЖрдзрд╛рд░ рдкрд░ рд╡рд┐рд╢рд┐рд╖реНрдЯ рдкреГрд╖реНрдареЛрдВ рддрдХ рдкрд╣реБрдВрдЪ


рд╕рдм рдХреБрдЫ рдорд╛рдирдХ рд╡рд┐рд╢реЗрд╖рддрд╛ рдЕрдзрд┐рдХреГрдд рд╣реЛ рдЬрд╛рддрд╛ рд╣реИред рдмреЗрд╢рдХ, рдЖрдк рд╕рд░реНрд╡рд░ рд╕рд╛рдЗрдб рдкрд░ рднреА рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдЕрдзрд┐рдХрд╛рд░реЛрдВ рдХреА рдЬрд╛рдВрдЪ рдмреЗрд╣рддрд░ рдХрд░рддреЗ рд╣реИрдВред

рдПрдХ рдкреГрд╖реНрда рдЬреЛ рдХреЗрд╡рд▓ рдХрд┐рд╕реА рднреА рднреВрдорд┐рдХрд╛ рдХреЗ рд╕рд╛рде рдПрдХ рдЕрдзрд┐рдХреГрдд рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рджреНрд╡рд╛рд░рд╛ рдкрд╣реБрдБрдЪрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ

 @page "/user" @attribute [Authorize] <h1>     </h1> 

рдПрдХ рдкреГрд╖реНрда рдЬрд┐рд╕реЗ рдХреЗрд╡рд▓ рдПрдХ рдЕрдзрд┐рдХреГрдд рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рджреНрд╡рд╛рд░рд╛ рдПрдХреНрд╕реЗрд╕ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ рдЬрд┐рд╕рдХреЗ рдкрд╛рд╕ рд╡реНрдпрд╡рд╕реНрдерд╛рдкрдХ рдпрд╛ рдмреЙрд╕ рдХреА рднреВрдорд┐рдХрд╛ рд╣реИ

 @page "/admin" @attribute [Authorize(Roles="admin, boss")] <h1>      admin  boss</h1> 

рдПрдкреАрдЖрдИ рдХреЙрд▓


рдРрд╕рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, OidcHttpClient рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ рдЬреЛ DI рдХрдВрдЯреЗрдирд░ рд╕реЗ рдкреНрд░рд╛рдкреНрдд рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред рдпрд╣ рдЕрдиреБрд░реЛрдз рдореЗрдВ рд╡рд░реНрддрдорд╛рди рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЗ рдЯреЛрдХрди рдХреЛ рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рд░реВрдк рд╕реЗ рдиреАрдЪреЗ рд░рдЦрддрд╛ рд╣реИред рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП:

 @page "/fetchdata" @inject Sotsera.Blazor.Oidc.OidcHttpClient Http @inject BlazorEShop.Shared.Presentation.ConfigModel Config @using BlazorEShop.Shared.Presentation <h1>Weather forecast</h1> <p>This component demonstrates fetching data from the server.</p> @if (products == null) { <p><em>Loading...</em></p> } else { <table class="table"> <thead> <tr> <th>Id</th> <th>Version</th> </tr> </thead> <tbody> @foreach (var product in products.Value) { <tr> <td>@product.Id</td> <td>@product.Version</td> </tr> } </tbody> </table> } @code { private PageResultModel<ProductModel> products; protected override async Task OnInitializedAsync() { var uri = Config.ApiUri; products = await Http.GetJsonAsync<PageResultModel<ProductModel>>($"{(uri.EndsWith('/') ? uri : uri + "/")}api/v1/products?take=100&skip;=0"); } } 

Source: https://habr.com/ru/post/hi484596/


All Articles