首页 > 其他分享 >Blazor SSR/WASM IDS/OIDC 单点登录授权实例4 - 部署服务端/独立WASM端授权

Blazor SSR/WASM IDS/OIDC 单点登录授权实例4 - 部署服务端/独立WASM端授权

时间:2024-01-23 22:36:12浏览次数:33  
标签:Console await ctx IDS WASM WriteLine using var 授权

目录:

  1. OpenID 与 OAuth2 基础知识
  2. Blazor wasm Google 登录
  3. Blazor wasm Gitee 码云登录
  4. Blazor SSR/WASM IDS/OIDC 单点登录授权实例1-建立和配置IDS身份验证服务
  5. Blazor SSR/WASM IDS/OIDC 单点登录授权实例2-登录信息组件wasm
  6. Blazor SSR/WASM IDS/OIDC 单点登录授权实例3-服务端管理组件
  7. Blazor SSR/WASM IDS/OIDC 单点登录授权实例4 - 部署服务端/独立WASM端授权
  8. Blazor SSR/WASM IDS/OIDC 单点登录授权实例5 - Blazor hybird app 端授权
  9. Blazor SSR/WASM IDS/OIDC 单点登录授权实例5 - Winform 端授权

源码

BlazorOIDC/Server

BlazorOIDC.Server 项目

部署

部署步骤跟平常blazor一样, 这里就不复述了, demo 部署后的域名是 ids2.app.es

新建独立WASM工程

[附带]控制台授权测试

添加工程 ConsoleOIDC

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup> 

    <ItemGroup>
        <FrameworkReference Include="Microsoft.AspNetCore.App"></FrameworkReference>
        <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
        <PackageReference Include="Serilog.Extensions.Logging" Version="8.0.0" />
        <PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
        <PackageReference Include="IdentityModel.OidcClient" Version="5.2.1" />
    </ItemGroup>
    
</Project>

代码

using IdentityModel.Client;
using IdentityModel.OidcClient;
using Newtonsoft.Json.Linq;
using Serilog;

namespace ConsoleOIDC;

public class Program
{
    static string authority = "https://localhost:5001/";
    //static string authority = "https://ids2.app1.es/"; //真实环境
    static string api = $"{authority}WeatherForecast";
    static string clientId = "Blazor5002";

    static OidcClient? _oidcClient;
    static HttpClient _apiClient = new HttpClient { BaseAddress = new Uri(api) };

    public static void Main(string[] args) => MainAsync().GetAwaiter().GetResult();

    public static async Task MainAsync()
    {
        Console.WriteLine("+-----------------------+");
        Console.WriteLine("|  Sign in with OIDC    |");
        Console.WriteLine("+-----------------------+");
        Console.WriteLine("");

        await Login();
    }

    private static async Task Login()
    {
        // 使用环回地址上的可用端口创建重定向 URI。
        // 要求 OP 允许 127.0.0.1 上的随机端口 - 否则设置静态端口

        var browser = new SystemBrowser(5002);
        var redirectUri = string.Format($"http://localhost:{browser.Port}/authentication/login-callback");
        var redirectLogoutUri = string.Format($"http://localhost:{browser.Port}/authentication/logout-callback");

        var options = new OidcClientOptions
        {
            Authority = authority,
            ClientId = clientId,
            RedirectUri = redirectUri,
            PostLogoutRedirectUri = redirectLogoutUri,
            Scope = "BlazorWasmIdentity.ServerAPI openid profile",
            //Scope = "Blazor7.ServerAPI openid profile",
            Browser = browser,
            Policy = new Policy { RequireIdentityTokenSignature = false }

        };

        var serilog = new LoggerConfiguration()
            .MinimumLevel.Error()
            .Enrich.FromLogContext()
            .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message}{NewLine}{Exception}{NewLine}")
            .CreateLogger();

        options.LoggerFactory.AddSerilog(serilog);

        _oidcClient = new OidcClient(options);
        var result = await _oidcClient.LoginAsync(new LoginRequest());

        ShowResult(result);
        await CallApi(result.AccessToken);
        await NextSteps(result);
    }

    private static void ShowResult(LoginResult result, bool showToken = false)
    {
        if (result.IsError)
        {
            Console.WriteLine("\n\nError:\n{0}", result.Error);
            return;
        }

        Console.WriteLine("\n\nClaims:");
        foreach (var claim in result.User.Claims)
        {
            Console.WriteLine("{0}: {1}", claim.Type, claim.Value);
        }

        if (showToken)
        {
            Console.WriteLine($"\nidentity token: {result.IdentityToken}");
            Console.WriteLine($"access token:   {result.AccessToken}");
            Console.WriteLine($"refresh token:  {result?.RefreshToken ?? "none"}");
        }
    }

    private static async Task NextSteps(LoginResult result)
    {
        var currentAccessToken = result.AccessToken;
        var currentRefreshToken = result.RefreshToken;

        var menu = "  x...exit  c...call api   ";
        if (currentRefreshToken != null) menu += "r...refresh token   ";

        while (true)
        {
            Console.WriteLine("\n\n");

            Console.Write(menu);
            var key = Console.ReadKey();

            if (key.Key == ConsoleKey.X) return;
            if (key.Key == ConsoleKey.C) await CallApi(currentAccessToken);
            if (key.Key == ConsoleKey.R)
            {
                var refreshResult = await _oidcClient.RefreshTokenAsync(currentRefreshToken);
                if (refreshResult.IsError)
                {
                    Console.WriteLine($"Error: {refreshResult.Error}");
                }
                else
                {
                    currentRefreshToken = refreshResult.RefreshToken;
                    currentAccessToken = refreshResult.AccessToken;

                    Console.WriteLine("\n\n");
                    Console.WriteLine($"access token:   {refreshResult.AccessToken}");
                    Console.WriteLine($"refresh token:  {refreshResult?.RefreshToken ?? "none"}");
                }
            }
        }
    }

    private static async Task CallApi(string currentAccessToken)
    {
        try
        {
            _apiClient.SetBearerToken(currentAccessToken);
            var response = await _apiClient.GetAsync("");

            if (response.IsSuccessStatusCode)
            {
                var str = await response.Content.ReadAsStringAsync();
                Console.WriteLine($"Response: {str}");
                var json = JArray.Parse(await response.Content.ReadAsStringAsync());
                Console.WriteLine("\n\n");
                Console.WriteLine(json);
            }
            else
            {
                Console.WriteLine($"Error: {response.ReasonPhrase}");
            }
        }
        catch (Exception e)
        {
            Console.WriteLine($"Error: {e.Message}");

        }
    }
}

添加 SystemBrowser.cs

using IdentityModel.OidcClient.Browser;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleOIDC;

public class SystemBrowser : IBrowser
{
    public int Port { get; }
    private readonly string _path;

    public SystemBrowser(int? port = null, string path = null)
    {
        _path = path;

        if (!port.HasValue)
        {
            Port = GetRandomUnusedPort();
        }
        else
        {
            Port = port.Value;
        }
    }

    private int GetRandomUnusedPort()
    {
        var listener = new TcpListener(IPAddress.Loopback, 0);
        listener.Start();
        var port = ((IPEndPoint)listener.LocalEndpoint).Port;
        listener.Stop();
        return port;
    }

    public async Task<BrowserResult> InvokeAsync(BrowserOptions options, CancellationToken cancellationToken = default)
    {
        using (var listener = new LoopbackHttpListener(Port, _path))
        {
            OpenBrowser(options.StartUrl);

            try
            {
                var result = await listener.WaitForCallbackAsync();
                if (string.IsNullOrWhiteSpace(result))
                {
                    return new BrowserResult { ResultType = BrowserResultType.UnknownError, Error = "Empty response." };
                }

                return new BrowserResult { Response = result, ResultType = BrowserResultType.Success };
            }
            catch (TaskCanceledException ex)
            {
                return new BrowserResult { ResultType = BrowserResultType.Timeout, Error = ex.Message };
            }
            catch (Exception ex)
            {
                return new BrowserResult { ResultType = BrowserResultType.UnknownError, Error = ex.Message };
            }
        }
    }

    public static void OpenBrowser(string url)
    {
        try
        {
            Process.Start(url);
        }
        catch
        {
            // hack because of this: https://github.com/dotnet/corefx/issues/10361
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                url = url.Replace("&", "^&");
                Process.Start(new ProcessStartInfo("cmd", $"/c start {url}") { CreateNoWindow = true });
            }
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
            {
                Process.Start("xdg-open", url);
            }
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
            {
                Process.Start("open", url);
            }
            else
            {
                throw;
            }
        }
    }
}

public class LoopbackHttpListener : IDisposable
{
    const int DefaultTimeout = 60 * 5; // 5 mins (in seconds)

    IWebHost _host;
    TaskCompletionSource<string> _source = new TaskCompletionSource<string>();

    public string Url { get; }

    public LoopbackHttpListener(int port, string path = null)
    {
        path = path ?? string.Empty;
        if (path.StartsWith("/")) path = path.Substring(1);

        Url = $"http://localhost:{port}/{path}";

        _host = new WebHostBuilder()
            .UseKestrel()
            .UseUrls(Url)
            .Configure(Configure)
            .Build();
        _host.Start();
    }

    public void Dispose()
    {
        Task.Run(async () =>
        {
            await Task.Delay(500);
            _host.Dispose();
        });
    }

    void Configure(IApplicationBuilder app)
    {
        app.Run(async ctx =>
        {
            if (ctx.Request.Method == "GET")
            {
                await SetResultAsync(ctx.Request.QueryString.Value, ctx);
            }
            else if (ctx.Request.Method == "POST")
            {
                if (!ctx.Request.ContentType.Equals("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase))
                {
                    ctx.Response.StatusCode = 415;
                }
                else
                {
                    using (var sr = new StreamReader(ctx.Request.Body, Encoding.UTF8))
                    {
                        var body = await sr.ReadToEndAsync();
                        await SetResultAsync(body, ctx);
                    }
                }
            }
            else
            {
                ctx.Response.StatusCode = 405;
            }
        });
    }

    private async Task SetResultAsync(string value, HttpContext ctx)
    {
        try
        {
            ctx.Response.StatusCode = 200;
            ctx.Response.ContentType = "text/html; charset=utf-8";
            await ctx.Response.WriteAsync("<h1>您现在可以返回应用程序.</h1>");
            await ctx.Response.Body.FlushAsync();

            _source.TrySetResult(value);
        }
        catch(Exception ex)
        {
            Console.WriteLine(ex.ToString());

            ctx.Response.StatusCode = 400;
            ctx.Response.ContentType = "text/html; charset=utf-8";
            await ctx.Response.WriteAsync("<h1>无效的请求.</h1>");
            await ctx.Response.Body.FlushAsync();
        }
    }

    public Task<string> WaitForCallbackAsync(int timeoutInSeconds = DefaultTimeout)
    {
        Task.Run(async () =>
        {
            await Task.Delay(timeoutInSeconds * 1000);
            _source.TrySetCanceled();
        });

        return _source.Task;
    }
}

标签:Console,await,ctx,IDS,WASM,WriteLine,using,var,授权
From: https://www.cnblogs.com/densen2014/p/17969485

相关文章

  • Blazor SSR/WASM IDS/OIDC 单点登录授权实例5 - Winform 端授权
    目录:OpenID与OAuth2基础知识BlazorwasmGoogle登录BlazorwasmGitee码云登录BlazorSSR/WASMIDS/OIDC单点登录授权实例1-建立和配置IDS身份验证服务BlazorSSR/WASMIDS/OIDC单点登录授权实例2-登录信息组件wasmBlazorSSR/WASMIDS/OIDC单点登录授权实例3-服......
  • Microsoft 365:如何在Azure AD中注册Application并授权相关Graph API
    51CTOBlog地址:https://blog.51cto.com/u_13969817在使用Powershell连接GraphAPI之前,首先管理员要在AzureAD中新建Application,并授权APIPermission和Credentials,本文将给大家做细节介绍:·      在AzureAD中注册Application·      授权GraphAPIPermission· ......
  • sudo提权,ansible批量给所有主机创建系统账户,授权
    sudo(superuseroranotherdo)让普通用户可以以超级管理员或其他人的身份执行命令。sudo基本流程如下:1.管理员需要先授权(修改/etc/sudoers文件)2.普通用户以sudo的形式执行命令3.可以通过sudo-l查看授权情况修改/etc/sudoers的方法如下:1.visudo(带语法检查,默认没有颜色提示)2......
  • SciTech-HybridSoftwareEngineering-Software Engineering2ndEditionISBN13:978126072
    Part1:IntroductionandSystemEngineeringChapter1:IntroductionChapter2:SoftwareProcessandMethodologyChapter3:SystemEngineeringPart2:AnalysisandArchitecturalDesignChapter4:SoftwareRequirementsElicitationChapter5:DomainModel......
  • postgres-wasm Supabase & Snaplet 开发的可以在浏览器中运行pg的工具
    postgres-wasmSupabase&Snaplet开发的可以在浏览器中运行pg的工具,比较有意思使用到的一些包runtime v86的一个模拟器buildroot 脚本,方便构建一个在v86中运行的cpu以及内存快照websockproxy 网络工具说明官方也提供了一个演示很强大,当然部分技术使用了webassembly可以方便......
  • 下一代云原生网关Higress:基于Wasm开发JWT认证插件
    什么是HigressHigress是基于阿里内部的EnvoyGateway实践沉淀、以开源Istio+Envoy为核心构建的下一代云原生网关,实现了流量网关+微服务网关+安全网关三合一的高集成能力,深度集成Dubbo、Nacos、Sentinel等微服务技术栈,能够帮助用户极大的降低网关的部署及运维成本且能力不打......
  • 在Navicat中创建oracle用户并授权
    1.创建oracle连接用户用system,密码为安装数据库时所填的口令服务名忘记的可通过命令查看当前运行的服务与实例lsnrctlstatus2.创建表空间创建成功后会在相应路径中有数据文件3.创建用户注意用户名需大写,默认表空间选择刚刚创建的表空间4.授权5.然后就可以建表啦......
  • 在oracle中用命令创建用户并授权
    1.以sysdba进入sql命令环境:sqlplus/assysdba;2.创建用户如lalcreateuserlalidentifiedby123456;3.授权GRANTCREATESESSIONTOlal;GRANT"CONNECT","RESOURCE"TOlal;4.登录测试......
  • Vantage客服我们很安全,牌照自己公司授权使用?
    今日是iFXEXPO,迪拜世博展的最后一天~不过今天真相哥要带大伙看的是,昨天参展的券商Vantage~从这次展会的赞助商展牌来看,Vantage在此次展会中的规模应该是中等偏下的,Vantage和STARTRADERPRIME以及UltimaMarkets独占一栏,而Exness可谓是这次展会的老大,,独占“C”位~话题扯远了,让我们......
  • 加密机授权报错如何排查?进入加密机后台的方式介绍
    我们在此前的文章中介绍过不少TSINGSEE青犀视频安防监控视频平台关于加密机授权操作及相关疑问解答,感兴趣的用户可以翻阅往期的文章进行了解。由于新用户咨询该方面的问题较多,今天我们再来介绍一下用户在使用过程中遇到的问题。1、如何进入加密机后台TSINGSEE青犀视频平台的永......