翼度科技»论坛 编程开发 .net 查看内容

Blazor OIDC 单点登录授权实例5 - 独立SSR App (net8 webapp ) 端授权

3

主题

3

帖子

9

积分

新手上路

Rank: 1

积分
9
目录:
(目录暂时不更新,跟随合集标题往下走)
源码

BlazorSSRAppOIDC
十分钟搞定单点登录

单点登录(SSO)简化了用户体验,使用户能够在访问多个应用时只需一次登录。这提高了用户满意度,减少了密码遗忘的风险,同时增强了安全性。但是,实现单点登录并不容易,需要应用程序实现和认证服务器的交互逻辑,增加了应用程序的开发工作量。例子中的安全策略中提供了 OpenID Connect (OIDC) 的能力,无需对应用做过多的修改,在十分钟内即可立刻实现单点登录。
当采用单点登录之后,用户只需要登录一次,就可以访问多个应用系统。SSO 通常由一个独立的身份管理系统来完成,该系统为每个用户分配一个全局唯一的标识,用户在登录时,只需要提供一次身份认证,就可以访问所有的应用系统。我们在使用一些网站时,经常会看到“使用微信登录”、“使用 Google 账户登录”等按钮,这些网站就是通过 SSO 来实现的。
采用单点登录有以下几个好处:
用户只需要登录一次,就可以访问多个应用系统,不需要为每个应用系统都单独登录。
应用系统不需要自己实现用户认证,只需将认证工作交给单点登录系统,可以大大减少应用系统的开发工作量。
使用 OIDC 单点登录, 可以简化客户端编写流程, 专注于功能实现而不用重复撰写登录部分功能代码, 也不用直接接触身份验证数据库, 剥离繁琐的重复劳动部分.
建立 net8 webapp ssr 工程


引用以下库
  1.     <ItemGroup>
  2.         <PackageReference Include="BootstrapBlazor" Version="8.*" />
  3.         <PackageReference Include="Densen.Extensions.BootstrapBlazor" Version="8.*" />
  4.         <PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="8.*" />
  5.         <PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="8.*" />
  6.         <PackageReference Include="Microsoft.Extensions.Http" Version="8.*" />
  7.     </ItemGroup>
复制代码
_Imports.razor 加入引用
  1. @using BootstrapBlazor.Components
  2. @using Microsoft.AspNetCore.Authorization
  3. @using Microsoft.AspNetCore.Components.Authorization
复制代码
App.razor 加入必须的UI库引用代码

完整文件
  1.     <ItemGroup>
  2.         <PackageReference Include="BootstrapBlazor" Version="8.*" />
  3.         <PackageReference Include="Densen.Extensions.BootstrapBlazor" Version="8.*" />
  4.         <PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="8.*" />
  5.         <PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="8.*" />
  6.         <PackageReference Include="Microsoft.Extensions.Http" Version="8.*" />
  7.     </ItemGroup>   
复制代码
Routes.razor 加入授权

完整代码
  1. <Router AppAssembly="typeof(Program).Assembly">
  2.     <Found Context="routeData">
  3.         <AuthorizeRouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)">
  4.             <NotAuthorized>
  5.                 <p role="alert">您无权访问该资源.</p>
  6.             </NotAuthorized>
  7.             <Authorizing>
  8.                 <p>正在验证您的身份...</p>
  9.             </Authorizing>
  10.         </AuthorizeRouteView>
  11.     </Found>
  12. </Router>
复制代码
添加Oidc授权配置


新建 OidcProfile.cs 文件
  1. using Microsoft.AspNetCore.Authentication;
  2. using Microsoft.AspNetCore.Authentication.OpenIdConnect;
  3. using Microsoft.IdentityModel.Protocols.OpenIdConnect;
  4. using System.Security.Claims;
  5. namespace OidcClientShared;
  6. public class OidcProfile
  7. {
  8.     public static void OidcDIY(OpenIdConnectOptions options)
  9.     {
  10.         var authority = "https://ids2.app1.es/"; //由于时间的关系,已经部署有一个实际站点, 大家也可以参考往期文章使用本机服务器测试
  11.         //authority = "https://localhost:5001/";
  12.         var clientId = "Blazor5002";
  13.         var callbackEndPoint = "http://localhost:5002";
  14.         options.Authority = authority;
  15.         options.ClientId = clientId;
  16.         options.ResponseType = OpenIdConnectResponseType.Code;
  17.         options.ResponseMode = OpenIdConnectResponseMode.Query;
  18.         options.SignedOutRedirectUri = callbackEndPoint;
  19.         options.CallbackPath = "/authentication/login-callback";
  20.         options.SignedOutCallbackPath = "/authentication/logout-callback";
  21.         options.Scope.Add("BlazorWasmIdentity.ServerAPI openid profile");
  22.         options.GetClaimsFromUserInfoEndpoint = true;
  23.         options.SaveTokens = true;
  24.         options.MapInboundClaims = false;
  25.         options.ClaimActions.MapAll();
  26.         options.ClaimActions.MapJsonKey(ClaimTypes.Name, "name");
  27.         options.ClaimActions.MapJsonKey(ClaimValueTypes.Email, "email", ClaimValueTypes.Email);
  28.         options.ClaimActions.MapJsonKey(ClaimTypes.Role, "role");
  29.         options.Events = new OpenIdConnectEvents
  30.         {
  31.             OnAccessDenied = context =>
  32.             {
  33.                 context.HandleResponse();
  34.                 context.Response.Redirect("/");
  35.                 return Task.CompletedTask;
  36.             },
  37.             OnTokenValidated = context =>
  38.             {
  39.                 var token = context.TokenEndpointResponse?.AccessToken;
  40.                 if (!string.IsNullOrEmpty(token))
  41.                 {
  42.                     if (context.Principal?.Identity != null)
  43.                     {
  44.                         var identity = context.Principal!.Identity as ClaimsIdentity;
  45.                         identity!.AddClaim(new Claim("AccessToken", token));
  46.                     }
  47.                 }
  48.                 return Task.CompletedTask;
  49.             }
  50.         };
  51.     }
  52. }
复制代码
Program.cs 加入授权相关

其中要加入Razor的cshtml支持, 因为登录要依靠管道跳转. 上下有两行都注释在文件内了.
完整代码
  1. using BlazorSSRAppOIDC.Components;
  2. using OidcClientShared;
  3. var builder = WebApplication.CreateBuilder(args);
  4. //在具有 Blazor Web 应用程序模板的 .NET 8 中,需要将其更改为, 由于该Pages文件夹已移至该Components文件夹中,因此您需要指定新位置的根目录,或将该Pages文件夹移回项目的根级别
  5. builder.Services.AddRazorPages().WithRazorPagesRoot("/Components/Pages");
  6. // Add services to the container.
  7. builder.Services.AddRazorComponents()
  8.     .AddInteractiveServerComponents();
  9. builder.Services.AddCascadingAuthenticationState();
  10. builder.Services.AddHttpClient();
  11. builder.Services.AddDensenExtensions();
  12. builder.Services.ConfigureJsonLocalizationOptions(op =>
  13. {
  14.     // 忽略文化信息丢失日志
  15.     op.IgnoreLocalizerMissing = true;
  16. });
  17. builder.Services
  18.     .AddAuthentication(options =>
  19.     {
  20.         options.DefaultScheme = "Cookies";
  21.         options.DefaultChallengeScheme = "oidc";
  22.     })
  23.     .AddCookie("Cookies")
  24.     .AddOpenIdConnect("oidc", OidcProfile.OidcDIY);
  25. var app = builder.Build();
  26. // Configure the HTTP request pipeline.
  27. if (!app.Environment.IsDevelopment())
  28. {
  29.     app.UseExceptionHandler("/Error", createScopeForErrors: true);
  30.     // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
  31.     app.UseHsts();
  32. }
  33. app.UseHttpsRedirection();
  34. app.UseStaticFiles();
  35. app.UseRouting();
  36. app.UseAuthentication();
  37. app.UseAuthorization();
  38. app.UseAntiforgery();
  39. //但出于某种原因,这还不够。在 Blazor Web 应用程序模板中,您明确需要调用
  40. app.MapRazorPages();
  41. app.MapRazorComponents<App>()
  42.     .AddInteractiveServerRenderMode();
  43. app.Run();
复制代码
Pages 文件夹新建登录Razor页实现登录和注销跳转


展开 Login.cshtml 文件组合三角箭头, 编辑 Login.cshtml.cs

Login.cshtml.cs
  1. using Microsoft.AspNetCore.Authentication;
  2. using Microsoft.AspNetCore.Mvc.RazorPages;
  3. namespace PersonalToolKit.Server.Components.Pages;
  4. public class LoginModel : PageModel
  5. {
  6.     public async Task OnGet(string redirectUri)
  7.     {
  8.         await HttpContext.ChallengeAsync("oidc", new AuthenticationProperties { RedirectUri = redirectUri });
  9.     }
  10. }
复制代码
Logout.cshtml.cs
  1. using Microsoft.AspNetCore.Authentication;
  2. using Microsoft.AspNetCore.Mvc.RazorPages;
  3. namespace PersonalToolKit.Server.Components.Pages;
  4. public class LogoutModel : PageModel
  5. {
  6.     public async Task OnGet(string redirectUri)
  7.     {
  8.         await HttpContext.SignOutAsync("Cookies");
  9.         await HttpContext.SignOutAsync("oidc", new AuthenticationProperties { RedirectUri = redirectUri });
  10.     }
  11. }
复制代码
Routes.razor 加入授权

完整代码
  1. [/code][size=4]Home.razor[/size]
  2. 完整代码
  3. [code]@page "/"
  4. @using System.Security.Claims
  5. @inject NavigationManager Navigation
  6. <PageTitle>Home</PageTitle>
  7. <AuthorizeView>
  8.     <Authorized>
  9.         你好, @context.User.Identity?.Name (@context.User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Role)?.Value)
  10.       
  11.         <Button Text="注销" OnClick="BeginLogOut" />
  12.         <br /><br /><br />
  13.         <h5>以下是用户的声明</h5><br />
  14.         @foreach (var claim in context.User.Claims)
  15.         {
  16.             <p>@claim.Type: @claim.Value</p>
  17.         }
  18.     </Authorized>
  19.     <NotAuthorized>
  20.         <Button Text="登录" OnClick="BeginLogIn" />
  21.         <p>默认账号 test@test.com 密码 0</p>
  22.     </NotAuthorized>
  23. </AuthorizeView>
  24. @code {
  25.     private string LoginUrl = "login?redirectUri=";
  26.     private void BeginLogIn()
  27.     {
  28.         var returnUrl = Uri.EscapeDataString(Navigation.Uri);
  29.         Navigation.NavigateTo(LoginUrl + returnUrl, forceLoad: true);
  30.     }
  31.     private string LogoutUrl = "logout?redirectUri=";
  32.     private void BeginLogOut()
  33.     {
  34.         var returnUrl = Uri.EscapeDataString(Navigation.Uri);
  35.         Navigation.NavigateTo(LogoutUrl + returnUrl, forceLoad: true);
  36.     }
  37. }
复制代码
运行



来源:https://www.cnblogs.com/densen2014/p/17969501
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具