热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

BlazorWebAssembly项目访问IdentityServer4

BlazorWebAssembly项目访问IdentityServer4 IdentityServer系列目录BlazorServer访问IdentityServer4单点登录-S

Blazor WebAssembly项目访问Identity Server 4

 

Identity Server系列目录



  1. Blazor Server访问Identity Server 4单点登录 - SunnyTrudeau - 博客园 (cnblogs.com)

  2. Blazor Server访问Identity Server 4单点登录2-集成Asp.Net角色 - SunnyTrudeau - 博客园 (cnblogs.com)

  3. Blazor Server访问Identity Server 4-手机验证码登录 - SunnyTrudeau - 博客园 (cnblogs.com)

  4. Blazor MAUI客户端访问Identity Server登录 - SunnyTrudeau - 博客园 (cnblogs.com)

  5. Identity Server 4项目集成Blazor组件 - SunnyTrudeau - 博客园 (cnblogs.com)

  6. Identity Server 4退出登录自动跳转返回 - SunnyTrudeau - 博客园 (cnblogs.com)

  7. Identity Server通过ProfileService返回用户角色 - SunnyTrudeau - 博客园 (cnblogs.com)

  8. Identity Server 4返回自定义用户Claim - SunnyTrudeau - 博客园 (cnblogs.com)

  9. Blazor Server获取Token访问外部Web Api - SunnyTrudeau - 博客园 (cnblogs.com)

  10. Blazor Server通过RefreshToken更新AccessToken - SunnyTrudeau - 博客园 (cnblogs.com)

 

Blazor WebAssembly项目提供了丰富的认证和授权支持,参考微软官网两篇文章,编写一个Blazor WebAssembly项目访问之前已经建好的Identity Server 4服务器。

https://docs.microsoft.com/zh-cn/aspnet/core/blazor/security/webassembly/standalone-with-authentication-library?view=aspnetcore-6.0&tabs=visual-studio

https://docs.microsoft.com/zh-cn/aspnet/core/blazor/security/webassembly/hosted-with-identity-server?view=aspnetcore-6.0&tabs=visual-studio

 


创建Blazor WebAssembly项目

新建Blazor WebAssembly项目WebAsmOidc,身份验证类型=个人账户,无托管主机。框架自动引用认证相关的NuGet类库,自动生成认证相关的文件,改一下就能用。

 

appsettings.Development.json改为访问已有的Identity Server 4服务器

 

"Local": {
"Authority": "https://localhost:5001/",
"ClientId": "WebAssemblyOidc",
"DefaultScopes": [
"scope1"
],
"PostLogoutRedirectUri": "/",
"ResponseType": "code"
}

 

launchSettings.json改一下项目的端口

      "applicationUrl": "https://localhost:5801;http://localhost:5800",

 

AspNetId4Web项目增加Blazor WebAssembly项目的客户端配置,因为WebAssembly代码在浏览器里边可以看到,没有必要用秘钥了

 

// Blazor WebAssembly客户端
new Client
{
ClientId
= "WebAssemblyOidc",
ClientName
= "WebAssemblyOidc",
RequireClientSecret
= false,
AllowedGrantTypes
= GrantTypes.Code,
AllowedScopes
={ "openid", "profile", "scope1", },
//网页客户端运行时的URL
AllowedCorsOrigins = {
"https://localhost:5801",
},
//登录成功之后将要跳转的网页客户端的URL
RedirectUris = {
"https://localhost:5801/authentication/login-callback",
},
//退出登录之后将要跳转的网页客户端的URL
PostLogoutRedirectUris = {
"https://localhost:5801",
},
},

 

同时运行AspNetId4Web项目、WebAsmOidc项目,在WebAsmOidc项目登录,可以跳转到Identity Server 4登录页面,并成功返回。

 


重新映射用户角色

参考微软官网的例子,把角色数组拆分为单个角色。

///


/// 自定义用户工厂
/// 在 Client 应用中,创建自定义用户工厂。 Identity 服务器在一个 role 声明中发送多个角色作为 JSON 数组。 单个角色在该声明中作为单个字符串值进行发送。
/// 工厂为每个用户的角色创建单个 role 声明。
/// https://docs.microsoft.com/zh-cn/aspnet/core/blazor/security/webassembly/hosted-with-identity-server?view=aspnetcore-6.0&tabs=visual-studio#name-and-role-claim-with-api-authorization
///

public class CustomUserFactory : AccountClaimsPrincipalFactory
{
public CustomUserFactory(IAccessTokenProviderAccessor accessor)
:
base(accessor)
{
}
public override async ValueTask CreateUserAsync(
RemoteUserAccount account,
RemoteAuthenticationUserOptions options)
{
var user = await base.CreateUserAsync(account, options);
if (user.Identity.IsAuthenticated)
{
var identity = (ClaimsIdentity)user.Identity;
var roleClaims = identity.FindAll(identity.RoleClaimType).ToArray();
if (roleClaims.Any())
{
foreach (var existingClaim in roleClaims)
{
identity.RemoveClaim(existingClaim);
}
var rolesElem = account.AdditionalProperties[identity.RoleClaimType];
if (rolesElem is JsonElement roles)
{
if (roles.ValueKind == JsonValueKind.Array)
{
foreach (var role in roles.EnumerateArray())
{
identity.AddClaim(
new Claim(options.RoleClaim, role.GetString()));
}
}
else
{
identity.AddClaim(
new Claim(options.RoleClaim, roles.GetString()));
}
}
}
}
return user;
}

Program.cs注册工厂,注意角色的名称也要转换

 

builder.Services.AddOidcAuthentication(optiOns=>
{
// Configure your authentication provider options here.
// For more information, see https://aka.ms/blazor-standalone-auth
builder.Configuration.Bind("Local", options.ProviderOptions);
//这里是个ClaimType的转换,Identity Server的ClaimType和Blazor中间件使用的名称有区别,需要统一。
options.UserOptions.NameClaim = "name";
options.UserOptions.RoleClaim
= "role";
})
.AddAccountClaimsPrincipalFactory
();

 

FetchData.razor页面增加认证要求

 

@using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Roles
= "Admin")]

 

再次运行2个项目,测试aliceAdmin权限,可以访问FetchData.razor页面,bob不行。

 


获取Access Token访问资源Web Api

参考微软官网定义,在Program.cs访问资源服务器的HttpClient参数,框架会自动获取Access TokenHttpClientHeader

https://docs.microsoft.com/zh-cn/aspnet/core/blazor/security/webassembly/additional-scenarios?view=aspnetcore-6.0#configure-the-httpclient-handler

AuthorizationMessageHandler 是一个 DelegatingHandler,用于将访问令牌附加到传出 HttpResponseMessage 实例。 令牌是使用由框架注册的 IAccessTokenProvider 服务获取的。

可以使用 ConfigureHandler 方法将 AuthorizationMessageHandler 配置为授权的 URL、作用域和返回 URLConfigureHandler 配置此处理程序,以使用访问令牌授权出站 HTTP 请求。 仅当至少有一个授权 URL 是请求 URI (HttpRequestMessage.RequestUri) 的基 URI 时,才附加访问令牌。 

 

builder.Services.AddHttpClient("MyWebApi",
client
=> client.BaseAddress = new Uri("https://localhost:5601"))
.AddHttpMessageHandler(sp
=> sp.GetRequiredService()
.ConfigureHandler(
authorizedUrls:
new[] { "https://localhost:5601" },
scopes:
new[] { "scope1" }));
builder.Services.AddScoped(sp
=> sp.GetRequiredService()
.CreateClient(
"MyWebApi"));

 

FetchData.razor页面改为访问MyWebApi项目获取数据

 

protected override async Task OnInitializedAsync()
{
//forecasts = await Http.GetFromJsonAsync("sample-data/weather.json");
try
{
forecasts
= await Http.GetFromJsonAsync("WeatherForecast");
}
catch (AccessTokenNotAvailableException exception)
{
exception.Redirect();
}
}

 


资源Web Api配置跨域共享

同时运行AspNetId4Web项目、MyWebAPi项目、WebAsmOidc项目,用管理员alice登录,访问FetchData.razor页面,提示跨域访问错误。

blazor.webassembly.js:1 info: System.Net.Http.HttpClient.MyWebApi.ClientHandler[100]

      Sending HTTP request GET https://localhost:5601/WeatherForecast

fetchdata:1

        

       Access to fetch at 'https://localhost:5601/WeatherForecast' from origin 'https://localhost:5801' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

:5601/WeatherForecast:1

 

参考微软官网给MyWebApi项目增加跨域共享配置

https://docs.microsoft.com/zh-cn/aspnet/core/blazor/call-web-api?view=aspnetcore-6.0&pivots=webassembly#call-web-api-example

 

app.UseCors(policy =>
policy.WithOrigins(
"https://localhost:5801")
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials());

 

Fiddler抓包看一下,WebAsmOidc项目访问了2MyWebAPi项目。

第一次是OPTIONS方法,获取MyWebAPi项目支持的功能。

 

OPTIONS https://localhost:5601/WeatherForecast HTTP/1.1
Host: localhost:5601
Connection: keep
-alive
Accept:
*/*
Access-Control-Request-Method: GET
Access-Control-Request-Headers: authorization
Origin:
https://localhost:5801
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36 Edg/99.0.1150.39
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
Sec-Fetch-Dest: empty
Referer:
https://localhost:5801/
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
HTTP/1.1 204 No Content
Date: Wed, 16 Mar 2022 12:13:16 GMT
Server: Kestrel
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: authorization
Access-Control-Allow-Methods: GET
Access-Control-Allow-Origin:
https://localhost:5801

 

第二次才是查询数据。

GET https://localhost:5601/WeatherForecast HTTP/1.1
Host: localhost:5601
Connection: keep
-alive
sec
-ch-ua: " Not A;Brand";v="99", "Chromium";v="99", "Microsoft Edge";v="99"
authorization: Bearer eyJ……ihg
sec
-ch-ua-mobile: ?0
User
-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36 Edg/99.0.1150.39
sec
-ch-ua-platform: "Windows"
Accept:
*/*
Origin:
https://localhost:5801
Sec-Fetch-Site: same-site
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer:
https://localhost:5801/
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Wed, 16 Mar 2022 12:13:17 GMT
Server: Kestrel
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin:
https://localhost:5801
Transfer-Encoding: chunked
1ee
[{"date":"2022-03-17T20:13:18.0963201+08:00","temperatureC":13,"temperatureF":55,"summary":"Cool"},{"date":"2022-03-18T20:13:18.0966368+08:00","temperatureC":24,"temperatureF":75,"summary":"Balmy"},{"date":"2022-03-19T20:13:18.0966403+08:00","temperatureC":-17,"temperatureF":2,"summary":"Mild"},{"date":"2022-03-20T20:13:18.0966405+08:00","temperatureC":15,"temperatureF":58,"summary":"Chilly"},{"date":"2022-03-21T20:13:18.0966406+08:00","temperatureC":10,"temperatureF":49,"summary":"Mild"}]
0

问题

Blazor WebAssembly项目访问跨域的资源Web Api配置比较麻烦,这是由浏览器安全机制规定的,简单的Blazor WebAssembly项目最好还是配合托管主机一起使用,网页客户端只访问配套的托管主机服务端,对于第三方资源Web Api也通过托管主机中转,托管主机起到类似网关的作用。托管主机是后台服务器,不受浏览器跨域访问的约束。这样网页客户端的HttpClient配置比较简单,资源Web Api也不用配置跨域共享,当然这个会牺牲性能,有利有弊。

访问托管主机的简单配置:

builder.Services.AddHttpClient("MyWebApi",
client
=> client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
.AddHttpMessageHandler
();
builder.Services.AddScoped(sp
=> sp.GetRequiredService()
.CreateClient(
"MyWebApi"));

DEMO代码地址:https://gitee.com/woodsun/blzid4

 



推荐阅读
  • spring boot使用jetty无法启动 ... [详细]
  • Docker安全策略与管理
    本文探讨了Docker的安全挑战、核心安全特性及其管理策略,旨在帮助读者深入理解Docker安全机制,并提供实用的安全管理建议。 ... [详细]
  • 本文详细介绍了Oracle 11g中的创建表空间的方法,以及如何设置客户端和服务端的基本配置,包括用户管理、环境变量配置等。 ... [详细]
  • 本文介绍了SIP(Session Initiation Protocol,会话发起协议)的基本概念、功能、消息格式及其实现机制。SIP是一种在IP网络上用于建立、管理和终止多媒体通信会话的应用层协议。 ... [详细]
  • 问题场景用Java进行web开发过程当中,当遇到很多很多个字段的实体时,最苦恼的莫过于编辑字段的查看和修改界面,发现2个页面存在很多重复信息,能不能写一遍?有没有轮子用都不如自己造。解决方式笔者根据自 ... [详细]
  • Web动态服务器Python基本实现
    Web动态服务器Python基本实现 ... [详细]
  • 本文详细介绍了如何正确设置Shadowsocks公共代理,包括调整超时设置、检查系统限制、防止滥用及遵守DMCA法规等关键步骤。 ... [详细]
  • 本文探讨了如何通过Service Locator模式来简化和优化在B/S架构中的服务命名访问,特别是对于需要频繁访问的服务,如JNDI和XMLNS。该模式通过缓存机制减少了重复查找的成本,并提供了对多种服务的统一访问接口。 ... [详细]
  • 对于初学者而言,搭建一个高效稳定的 Python 开发环境是入门的关键一步。本文将详细介绍如何利用 Anaconda 和 Jupyter Notebook 来构建一个既易于管理又功能强大的开发环境。 ... [详细]
  • 长期从事ABAP开发工作的专业人士,在面对行业新趋势时,往往需要重新审视自己的发展方向。本文探讨了几位资深专家对ABAP未来走向的看法,以及开发者应如何调整技能以适应新的技术环境。 ... [详细]
  • 本文详细介绍了JQuery Mobile框架中特有的事件和方法,帮助开发者更好地理解和应用这些特性,提升移动Web开发的效率。 ... [详细]
  • CRZ.im:一款极简的网址缩短服务及其安装指南
    本文介绍了一款名为CRZ.im的极简网址缩短服务,该服务采用PHP和SQLite开发,体积小巧,约10KB。本文还提供了详细的安装步骤,包括环境配置、域名解析及Nginx伪静态设置。 ... [详细]
  • 本文深入探讨了Go语言中的接口型函数,通过实例分析其灵活性和强大功能,帮助开发者更好地理解和运用这一特性。 ... [详细]
  • CentOS下ProFTPD的安装与配置指南
    本文详细介绍在CentOS操作系统上安装和配置ProFTPD服务的方法,包括基本配置、安全设置及高级功能的启用。 ... [详细]
  • 本文将从基础概念入手,详细探讨SpringMVC框架中DispatcherServlet如何通过HandlerMapping进行请求分发,以及其背后的源码实现细节。 ... [详细]
author-avatar
gogo迷失的大G
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有