码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • Blazor OIDC 单点登录授权实例7 - Blazor hybird app 端授权


    合集 - Blazor hybird / MAUIBlazor (25)
    1.bh002- Blazor hybrid / Maui 保存设置快速教程2023-08-212.bh002- Blazor hybrid / Maui 使用ORM和数据库快速教程2023-08-223.bh001- Blazor hybrid / Maui 使用摄像头和扫码快速教程2023-08-174.bh003- Blazor hybrid / Maui 使用蓝牙BLE快速教程2023-08-235.bh004- Blazor hybrid / Maui 使用 BootstrapBlazor UI 库快速教程2023-08-246.bh006- Blazor hybrid / Maui 使用NFC快速教程2023-08-257.如何在 Maui Blazor 使用 BlazorWebView 像本机浏览器一样下载文件?2023-03-108.【多图】发布用于App Store分发的 iOS 应用2023-07-189..NET Core 跨平台 GUI 开发 与 Blazor / MAUI 2023-08-2810.如何为面向 Windows 的 MAUI Blazor 应用程序设置窗口标题?2022-12-0311.如何在 .NET MAUI 中加载 json 文件?2022-11-1412.发布 .NET 7 MAUI / MAUI Blazor 应用到 Windows 应用商店2022-11-1313.谷歌地图之类的api要在 maui 里面使用, 需要授权0.0.0.0的IP2022-10-1314.发布 .NET MAUI / MAUI Blazor 应用 (1) - Windows2022-08-0915.MAUI Blazor 权限经验分享 (定位,使用相机)2022-08-0416.Blazor Hybrid / MAUI 简介和实战2022-05-0717.在 Mac 上开发 .NET MAUI2022-03-2618.在 M1 Mac 上开发 .NET MAUI (iOS)2022-03-2619.树莓派4b装系统到运行 Blazor Linux 本地程序全记录2023-09-1620.在 Blazor WASM 中手撸一个.NET MD5类2023-11-2521.Maui blazor ios 按设备类型设置是否启用safeArea01-0422.BootstrapBlazor 模板适配移动设备使用笔记02-0823.ZXing Blazor 扫码组件 , ssr/wasm通用2020-10-0124.mac arm 调试 maui 提示 iOS 构建没有“net8.0-ios/iossimulator-x64”的目标02-15
    25.Blazor OIDC 单点登录授权实例7 - Blazor hybird app 端授权04-10
    收起

    目录:

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

    (目录暂时不更新,跟随合集标题往下走)

    源码

    BlazorOIDC.WinForms

    建立 BlazorOIDC.WinForms 工程

    自行安装 Vijay Anand E G 模板,快速建立 Blazor WinForms 工程, 命名为 BlazorOIDC.WinForms

    引用以下库

        
            
            
            
            
            
        
    

    _Imports.razor 加入引用

    @using Microsoft.AspNetCore.Components.Authorization
    

    Main.razor 加入授权

    完整代码

    
        
            
                
            
            
                
                    

    Sorry, there's nothing at this address.

    添加Oidc授权配置

    新建文件 ExternalAuthStateProvider.cs

    完整代码

    using IdentityModel.OidcClient;
    using Microsoft.AspNetCore.Components.Authorization;
    using System.Security.Claims;
    
    namespace BlazorOIDC.WinForms;
    
    public class ExternalAuthStateProvider : AuthenticationStateProvider
    {
        private readonly Task authenticationState;
    
        public ExternalAuthStateProvider(AuthenticatedUser user) =>
            authenticationState = Task.FromResult(new AuthenticationState(user.Principal));
    
        private ClaimsPrincipal currentUser = new ClaimsPrincipal(new ClaimsIdentity());
    
        public override Task GetAuthenticationStateAsync() =>
            Task.FromResult(new AuthenticationState(currentUser));
    
        public Task LogInAsync()
        {
            var loginTask = LogInAsyncCore();
            NotifyAuthenticationStateChanged(loginTask);
    
            return loginTask;
    
            async Task LogInAsyncCore()
            {
                var user = await LoginWithExternalProviderAsync();
                currentUser = user;
    
                return new AuthenticationState(currentUser);
            }
        }
    
        private async Task LoginWithExternalProviderAsync()
        {
            /*
                提供 Open ID/MSAL 代码以对用户进行身份验证。查看您的身份
                提供商的文档以获取详细信息。
    
                根据新的声明身份返回新的声明主体。
            */
    
            string authority = "https://localhost:5001/";
            //string authority = "https://ids2.app1.es/"; //真实环境
            string api = $"{authority}WeatherForecast";
            string clientId = "Blazor5002";
    
            OidcClient? _oidcClient;
            HttpClient _apiClient = new HttpClient { BaseAddress = new Uri(api) };
    
            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 }
    
            };
    
            _oidcClient = new OidcClient(options);
            var result = await _oidcClient.LoginAsync(new LoginRequest());
            ShowResult(result);
    
            var authenticatedUser = result.User;
    
            return authenticatedUser;
        }
    
        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"}");
            }
        }
    
        public Task Logout()
        {
            currentUser = new ClaimsPrincipal(new ClaimsIdentity());
            NotifyAuthenticationStateChanged(
                Task.FromResult(new AuthenticationState(currentUser)));
            return Task.CompletedTask;
        }
    }
    
    public class AuthenticatedUser
    {
        public ClaimsPrincipal Principal { get; set; } = new();
    }
    
    

    添加Oidc浏览器授权方法

    新建文件 SystemBrowser.cs

    完整代码

    using IdentityModel.OidcClient.Browser;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.AspNetCore.Http;
    using System.Diagnostics;
    using System.Net;
    using System.Net.Sockets;
    using System.Runtime.InteropServices;
    using System.Text;
    #nullable disable
    
    namespace BlazorOIDC.WinForms;
    
    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 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 _source = new TaskCompletionSource();
    
        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("

    您现在可以返回应用程序.

    "); 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("

    无效的请求.

    "); await ctx.Response.Body.FlushAsync(); } } public Task WaitForCallbackAsync(int timeoutInSeconds = DefaultTimeout) { Task.Run(async () => { await Task.Delay(timeoutInSeconds * 1000); _source.TrySetCanceled(); }); return _source.Task; } }

    Shared 文件夹新建登录/注销页面组件

    LoginComponent.razor

    完整代码

    @inject AuthenticationStateProvider AuthenticationStateProvider
    @page "/Login"
    @using System.Security.Claims
    
    
    
    

    @Msg

    你好, @context.User.Identity?.Name


    以下是用户的声明

    @foreach (var claim in context.User.Claims) {

    @claim.Type: @claim.Value

    }

    以下是基于角色或基于策略的授权,未登录不显示

    只有管理员或超级用户才能看到.

    @code { [Inject] private AuthenticatedUser? authenticatedUser { get; set; } /// /// 级联参数获取身份验证状态数据 /// [CascadingParameter] private Task? authenticationStateTask { get; set; } private string? Msg { get; set; } private ClaimsPrincipal? User { get; set; } public async Task Login() { var authenticationState = await ((ExternalAuthStateProvider)AuthenticationStateProvider).LogInAsync(); User = authenticationState?.User; if (User != null) { if (User.Identity != null && User.Identity.IsAuthenticated) { Msg += "已登录." + Environment.NewLine; } } } }

    LogoutComponent.razor

    完整代码

    @inject AuthenticationStateProvider AuthenticationStateProvider
    @page "/Logout"
    
    
    
    @code
    {
        public async Task Logout()
        {
            await ((ExternalAuthStateProvider)AuthenticationStateProvider).Logout();
        }
    }
    

    NavMenu.razor 加入菜单

    		
    Login
    Logout

    Form1.cs 修改首页

    
            var blazor = new BlazorWebView()
            {
                Dock = DockStyle.Fill,
                HostPage = "wwwroot/index.html",
                Services = Startup.Services!,
                StartPath = "/Login"
            };
            blazor.RootComponents.Add
    ("#app"); Controls.Add(blazor);

    Startup.cs 注册服务

    完整代码

    using BlazorOIDC.WinForms.Data;
    using Microsoft.AspNetCore.Components.Authorization;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.DependencyInjection.Extensions;
    using Microsoft.Extensions.Hosting;
    
    namespace BlazorOIDC.WinForms;
    public static class Startup
    {
        public static IServiceProvider? Services { get; private set; }
    
        public static void Init()
        {
            var host = Host.CreateDefaultBuilder()
                           .ConfigureServices(WireupServices)
                           .Build();
            Services = host.Services;
        }
    
        private static void WireupServices(IServiceCollection services)
        {
            services.AddWindowsFormsBlazorWebView();
            services.AddSingleton();
    
            services.AddAuthorizationCore();
            services.TryAddScoped();
            services.AddSingleton();
     
      
    
    #if DEBUG
            services.AddBlazorWebViewDeveloperTools();
    #endif
        }
    }
    

    运行

  • 相关阅读:
    mac安装node失败
    leetcode 6103 — 从树中删除边的最小分数
    window升级版本
    DLL详解
    E. Red-Black Pepper
    1130 - Host ‘17216.18083‘ is not allowed to connect to this MySQL server
    公众号推送早安问候以及天气预报(JAVA)
    【数据结构(邓俊辉)学习笔记】向量02——动态空间管理
    轻松合并视频,一键为视频添加封面,打造个性化批量剪辑!
    Word处理控件Aspose.Words功能演示:在 Python 中比较两个 Word 文档
  • 原文地址:https://www.cnblogs.com/densen2014/p/18014332
  • 最新文章
  • 攻防演习之三天拿下官网站群
    数据安全治理学习——前期安全规划和安全管理体系建设
    企业安全 | 企业内一次钓鱼演练准备过程
    内网渗透测试 | Kerberos协议及其部分攻击手法
    0day的产生 | 不懂代码的"代码审计"
    安装scrcpy-client模块av模块异常,环境问题解决方案
    leetcode hot100【LeetCode 279. 完全平方数】java实现
    OpenWrt下安装Mosquitto
    AnatoMask论文汇总
    【AI日记】24.11.01 LangChain、openai api和github copilot
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
正则表达式工具 cron表达式工具 密码生成工具

京公网安备 11010502049817号