当前位置: 首页 > news >正文

ASP.NET Core认证原理和实现

ASP.NET Core认证原理和实现

AuthenticationHttpContextExtensions

AuthenticationHttpContextExtensions 类是对 HttpContext 认证相关的扩展,它提供了如下扩展方法:

public static class AuthenticationHttpContextExtensions
{public static Task<AuthenticateResult> AuthenticateAsync(this HttpContext context, string scheme) =>context.RequestServices.GetRequiredService<IAuthenticationService>().AuthenticateAsync(context, scheme);public static Task ChallengeAsync(this HttpContext context, string scheme, AuthenticationProperties properties) { }public static Task ForbidAsync(this HttpContext context, string scheme, AuthenticationProperties properties) { }public static Task SignInAsync(this HttpContext context, string scheme, ClaimsPrincipal principal, AuthenticationProperties properties) {}public static Task SignOutAsync(this HttpContext context, string scheme, AuthenticationProperties properties) { }public static Task<string> GetTokenAsync(this HttpContext context, string scheme, string tokenName) { }
}

主要包括如上6个扩展方法,其它的只是一些参数重载:

SignInAsync 用户登录成功后颁发一个证书(加密的用户凭证),用来标识用户的身份。

SignOutAsync 退出登录,如清除Coookie等。

AuthenticateAsync 验证在 SignInAsync 中颁发的证书,并返回一个 AuthenticateResult 对象,表示用户的身份。

ChallengeAsync 返回一个需要认证的标识来提示用户登录,通常会返回一个 401 状态码。

ForbidAsync 禁上访问,表示用户权限不足,通常会返回一个 403 状态码。

GetTokenAsync 用来获取 AuthenticationProperties 中保存的额外信息。

它们的实现都非常简单,与展示的第一个方法类似,从DI系统中获取到 IAuthenticationService 接口实例,然后调用其同名方法。

因此,如果我们希望使用认证服务,那么首先要注册 IAuthenticationService 的实例,ASP.NET Core 中也提供了对应注册扩展方法:

public static class AuthenticationCoreServiceCollectionExtensions
{public static IServiceCollection AddAuthenticationCore(this IServiceCollection services){services.TryAddScoped<IAuthenticationService, AuthenticationService>();services.TryAddSingleton<IClaimsTransformation, NoopClaimsTransformation>(); // Can be replaced with scoped ones that use DbContextservices.TryAddScoped<IAuthenticationHandlerProvider, AuthenticationHandlerProvider>();services.TryAddSingleton<IAuthenticationSchemeProvider, AuthenticationSchemeProvider>();return services;}public static IServiceCollection AddAuthenticationCore(this IServiceCollection services, Action<AuthenticationOptions> configureOptions) {services.AddAuthenticationCore();services.Configure(configureOptions);return services;}
}

如上,AddAuthenticationCore 中注册了认证系统的三大核心对象:IAuthenticationSchemeProvider,IAuthenticationHandlerProvider 和 IAuthenticationService,以及一个对Claim进行转换的 IClaimsTransformation(不常用), 三大对象。

IAuthenticationSchemeProvider

首先来解释一下 Scheme 是用来做什么的。因为在 ASP.NET Core 中可以支持各种各样的认证方式(如,cookie, bearer, oauth, openid 等等),而 Scheme 用来标识使用的是哪种认证方式,不同的认证方式其处理方式是完全不一样的,所以Scheme是非常重要的。

IAuthenticationSchemeProvider 用来提供对Scheme的注册和查询

IAuthenticationHandlerProvider

在 ASP.NET Core 的认证系统中,AuthenticationHandler 负责对用户凭证的验证

通常在应用程序中,安全分为前后两个步骤:验证和授权。验证负责检查当前请求者的身份,而授权则根据上一步得到的身份决定当前请求者是否能够访问期望的资源。

IAuthenticationHandlerProvider

在 ASP.NET Core 的认证系统中,AuthenticationHandler 负责对用户凭证的验证

既然安全从验证开始,我们也就从验证开始介绍安全。

验证的核心概念

我们先从比较简单的场景开始考虑,例如在 Web API 开发中,需要验证请求方是否提供了安全令牌,安全令牌是否有效。如果无效,那么 API 端应该拒绝提供服务。在命名空间 Microsoft.AspNetCore.Authentication 下,定义关于验证的核心接口。对应的程序集是 Microsoft.AspNetCore.Authentication.Abstractions.dll。

验证接口 IAuthenticationHandler

在 ASP.NET 下,验证中包含 3 个基本操作:

Authenticate 验证

验证操作负责基于当前请求的上下文,使用来自请求中的信息,例如请求头、Cookie 等等来构造用户标识。构建的结果是一个 AuthenticateResult 对象,它指示了验证是否成功,如果成功的话,用户标识将可以在验证票据中找到。

常见的验证包括:

  • 基于 Cookie 的验证,从请求的 Cookie 中验证用户

  • 基于 JWT Bearer 的验证,从请求头中提取 JWT 令牌进行验证

Challenge 质询

在授权管理阶段,如果用户没有得到验证,但所期望访问的资源要求必须得到验证的时候,授权服务会发出质询。例如,当匿名用户访问受限资源的时候,或者当用户点击登录链接的时候。授权服务会通过质询来相应用户。

例如

  • 基于 Cookie 的验证会将用户重定向到登录页面

  • 基于 JWT 的验证会返回一个带有 www-authenticate: bearer 响应头的 401 响应来提醒客户端需要提供访问凭据

质询操作应该让用户知道应该使用何种验证机制来访问请求的资源。

Forbid 拒绝

在授权管理阶段,如果用户已经通过了验证,但是对于其访问的资源并没有得到许可,此时会使用拒绝操作。

例如:

  • Cookie 验证模式下,已经登录但是没有访问权限的用户,被重定向到一个提示无权访问的页面

  • JWT 验证模式下,返回 403

  • 在自定义验证模式下,将没有权限的用户重定向到申请资源的页面

拒绝访问处理应该让用户知道:

  • 它已经通过了验证

  • 但是没有权限访问请求的资源

在这个场景下,可以看到,验证需要提供的基本功能就包括了验证和验证失败后的拒绝服务两个操作。在 ASP.NET Core 中,验证被称为 Authenticate,拒绝被称为 Forbid。 在供消费者访问的网站上,如果我们希望在验证失败后,不是像 API 一样直接返回一个错误页面,而是将用户导航到登录页面,那么,就还需要增加一个操作,这个操作的本质是希望用户再次提供安全凭据,在 ASP.NET Core 中,这个操作被称为 Challenge。这 3 个操作结合在一起,就是验证最基本的要求,以接口形式表示,就是 IAuthenticationHandler 接口,如下所示:

public interface IAuthenticationHandler
{Task InitializeAsync(AuthenticationScheme scheme, HttpContext context);Task<AuthenticateResult> AuthenticateAsync();Task ChallengeAsync(AuthenticationProperties? properties);Task ForbidAsync(AuthenticationProperties? properties);
}

验证的结果是一个 AuthenticateResult 对象。值得注意的是,它还提供了一个静态方法 NoResult() 用来返回没有得到结果,静态方法 Fail() 生成一个表示验证异常的结果,而 Success() 成功则需要提供验证票据。

通过验证之后,会返回一个包含了请求者票据的验证结果。

namespace Microsoft.AspNetCore.Authentication
{public class AuthenticateResult{// ......public static AuthenticateResult NoResult(){return new AuthenticateResult() { None = true };}public static AuthenticateResult Fail(Exception failure){return new AuthenticateResult() { Failure = failure };}public static AuthenticateResult Success(AuthenticationTicket ticket){if (ticket == null){throw new ArgumentNullException(nameof(ticket));}return new AuthenticateResult() { Ticket = ticket, Properties = ticket.Properties };}public static AuthenticateResult Success(AuthenticationTicket ticket){if (ticket == null){throw new ArgumentNullException(nameof(ticket));}return new AuthenticateResult() { Ticket = ticket, Properties = ticket.Properties };}// ......}
}

在 GitHub 中查看 AuthenticateResult 源码

那么验证的信息来自哪里呢?除了前面介绍的 3 个操作之外,还要求一个初始化的操作 Initialize,通过这个方法来提供当前请求的上下文信息。

在 GitHub 中查看 IAuthenticationHandler 定义

支持登录和登出操作的验证接口

有的时候,我们还希望提供登出操作,增加登出操作的接口被称为 IAuthenticationSignOutHandler。

public interface IAuthenticationSignOutHandler : IAuthenticationHandler
{Task SignOutAsync(AuthenticationProperties? properties);
}

在 GitHub 中查看 IAuthenticationSignOutHandler 源码

在登出的基础上,如果还希望提供登录操作,那么就是 IAuthenticationSignInHandler 接口。

public interface IAuthenticationSignInHandler : IAuthenticationSignOutHandler
{Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties? properties);
}

在 GitHub 中查看 IAuthenticationSignInHandler 源码

实现验证支持的抽象基类 AuthenticationHandler

直接实现接口还是比较麻烦的,在命名空间 Microsoft.AspNetCore.Authentication 下,微软提供了抽象基类 AuthenticationHandler 以方便验证控制器的开发,其它控制器可以从该控制器派生,以取得其提供的服务。

namespace Microsoft.AspNetCore.Authentication
{public abstract class AuthenticationHandler<TOptions> : IAuthenticationHandler where TOptions : AuthenticationSchemeOptions, new(){protected AuthenticationHandler(IOptionsMonitor<TOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock){Logger = logger.CreateLogger(this.GetType().FullName);UrlEncoder = encoder;Clock = clock;OptionsMonitor = options;}}// ......
}

通过类的定义可以看到,它使用了泛型。每个控制器应该有一个对应该控制器的配置选项,通过泛型来指定验证处理器所使用的配置类型,在构造函数中,可以看到它被用于获取对应的配置选项对象。

在 GitHub 中查看 AuthenticationHandler 源码

通过 InitializeAsync(),验证处理器可以获得当前请求的上下文对象 HttpContext。

public async Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)

最终,作为抽象类的 ,希望派生类来完成这个验证任务,抽象方法 HandleAuthenticateAsync() 提供了扩展点。

/// <summary>
/// Allows derived types to handle authentication.
/// </summary>
/// <returns>The <see cref="AuthenticateResult"/>.</returns>
protected abstract Task<AuthenticateResult> HandleAuthenticateAsync();

验证的结果是一个 AuthenticateResult。

而拒绝服务则简单的多,直接在这个抽象基类中提供了默认实现。直接返回 HTTP 403。

protected virtual Task HandleForbiddenAsync(AuthenticationProperties properties)
{Response.StatusCode = 403;return Task.CompletedTask;
}

剩下的一个也一样,提供了默认实现。直接返回 HTTP 401 响应。

protected virtual Task HandleChallengeAsync(AuthenticationProperties properties)
{Response.StatusCode = 401;return Task.CompletedTask;
}
Jwt 验证处理器是如何实现的?

对于 JWT 来说,并不涉及到登入和登出,所以它需要从实现 IAuthenticationHandler 接口的抽象基类 AuthenticationHandler 派生出来即可。从 AuthenticationHandler 派生出来的 JwtBearerHandler 实现基于自己的配置选项 JwtBearerOptions。所以该类定义就变得如下所示,而构造函数显然配合了抽象基类的要求。

namespace Microsoft.AspNetCore.Authentication.JwtBearer
{public class JwtBearerHandler : AuthenticationHandler<JwtBearerOptions>{public JwtBearerHandler(IOptionsMonitor<JwtBearerOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock): base(options, logger, encoder, clock){ }// ......}
}

在 GitHub 中查看 JwtBearerHandler 源码

真正的验证则在 HandleAuthenticateAsync() 中实现。下面的代码是不是就很熟悉了,从请求头中获取附带的 JWT 访问令牌,然后验证该令牌的有效性,核心代码如下所示。

string authorization = Request.Headers[HeaderNames.Authorization];// If no authorization header found, nothing to process further
if (string.IsNullOrEmpty(authorization))
{return AuthenticateResult.NoResult();
}if (authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
{token = authorization.Substring("Bearer ".Length).Trim();
}// If no token found, no further work possible
if (string.IsNullOrEmpty(token))
{return AuthenticateResult.NoResult();
}// ......
principal = validator.ValidateToken(token, validationParameters, out validatedToken);

在 GitHub 中查看 JwtBearerHandler 源码

注册 Jwt 验证处理器

在 ASP.NET Core 中,你可以使用各种验证处理器,并不仅仅只能使用一个,验证控制器需要一个名称,它被看作该验证模式 Schema 的名称。Jwt 验证模式的默认名称就是 "Bearer",通过字符串常量 JwtBearerDefaults.AuthenticationScheme 定义。

namespace Microsoft.AspNetCore.Authentication.JwtBearer
{/// <summary>/// Default values used by bearer authentication./// </summary>public static class JwtBearerDefaults{/// <summary>/// Default value for AuthenticationScheme property in the JwtBearerAuthenticationOptions/// </summary>public const string AuthenticationScheme = "Bearer";}
}

在 GitHub 中查看 JwtBearerDefaults 源码

最终通过 AuthenticationBuilder 的扩展方法 AddJwtBearer() 将 Jwt 验证控制器注册到依赖注入的容器中。

public static AuthenticationBuilder AddJwtBearer(this AuthenticationBuilder builder)=> builder.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, _ => { });public static AuthenticationBuilder AddJwtBearer(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action<JwtBearerOptions> configureOptions)
{builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<JwtBearerOptions>, JwtBearerPostConfigureOptions>());return builder.AddScheme<JwtBearerOptions, JwtBearerHandler>(authenticationScheme, displayName, configureOptions);
}

在 GitHub 中查看 JwtBearerExtensions 扩展方法源码

验证架构 Schema

一种验证处理器,加上对应的验证配置选项,我们再为它起一个名字,组合起来就成为一种验证架构 Schema。在 ASP.NET Core 中,可以注册多种验证架构。例如,授权策略可以使用架构的名称来指定所使用的验证架构来使用特定的验证方式。在配置验证的时候,通常设置默认的验证架构。当没有指定验证架构的时候,就会使用默认架构进行处理。

还可以

  • 对于 authenticate, challenge, 以及 forbid 操作使用不同的验证架构

  • 使用策略来组合多种验证架构

注册的验证模式,最终变成 AuthenticationScheme,注册到依赖注入服务中。

public class AuthenticationScheme
{public string Name { get; }public string? DisplayName { get; }[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]public Type HandlerType { get; }
}

在 GitHub 中查看 AuthenticationScheme 源码

使用验证处理器

IAuthenticationSchemeProvider

各种验证架构被保存到一个 IAuthenticationSchemeProvider 中。

public interface IAuthenticationSchemeProvider
{Task<IEnumerable<AuthenticationScheme>> GetAllSchemesAsync();Task<AuthenticationScheme?> GetSchemeAsync(string name);void AddScheme(AuthenticationScheme scheme);void RemoveScheme(string name);
}

在 GitHub 中查看 IAuthenticationSchemeProvider 源码

IAuthenticationHandlerProvider

最终的使用是通过 IAuthenticationHandlerProvider 来实现的,通过一个验证模式的字符串名称,可以取得所对应的验证控制器。

public interface IAuthenticationHandlerProvider
{Task<IAuthenticationHandler?> GetHandlerAsync(HttpContext context, string authenticationScheme);
}

在 GitHub 中查看 IAuthenticationHandlerProvider 源码

它的默认实现是 AuthenticationHandlerProvider,源码并不复杂。

public class AuthenticationHandlerProvider : IAuthenticationHandlerProvider
{public IAuthenticationSchemeProvider Schemes { get; }private readonly Dictionary<string, IAuthenticationHandler> _handlerMap = new Dictionary<string, IAuthenticationHandler>(StringComparer.Ordinal);public AuthenticationHandlerProvider(IAuthenticationSchemeProvider schemes){Schemes = schemes;}public async Task<IAuthenticationHandler?> GetHandlerAsync(HttpContext context, string authenticationScheme){if (_handlerMap.TryGetValue(authenticationScheme, out var value)){return value;}var scheme = await Schemes.GetSchemeAsync(authenticationScheme);if (scheme == null){return null;}var handler = (context.RequestServices.GetService(scheme.HandlerType) ??ActivatorUtilities.CreateInstance(context.RequestServices, scheme.HandlerType))as IAuthenticationHandler;if (handler != null){await handler.InitializeAsync(scheme, context);_handlerMap[authenticationScheme] = handler;}return handler;}
}

在 GitHub 中查看 AuthenticationHandlerProvider 源码

Authentication 中间件 AuthenticationMiddleware

验证中间件的处理就没有那么复杂了。

找到默认的验证模式,使用默认验证模式的名称取得对应的验证处理器,如果验证成功的话,把当前请求用户的主体放到当前请求上下文的 User 上。

里面还有一段特别的代码,用来找出哪些验证处理器实现了 IAuthenticationHandlerProvider,并依次调用它们,看看是否需要提取终止请求处理过程。

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;namespace Microsoft.AspNetCore.Authentication
{public class AuthenticationMiddleware{private readonly RequestDelegate _next;public AuthenticationMiddleware(RequestDelegate next, IAuthenticationSchemeProvider schemes){if (next == null){throw new ArgumentNullException(nameof(next));}if (schemes == null){throw new ArgumentNullException(nameof(schemes));}_next = next;Schemes = schemes;}public IAuthenticationSchemeProvider Schemes { get; set; }public async Task Invoke(HttpContext context){context.Features.Set<IAuthenticationFeature>(new AuthenticationFeature{OriginalPath = context.Request.Path,OriginalPathBase = context.Request.PathBase});// Give any IAuthenticationRequestHandler schemes a chance to handle the requestvar handlers = context.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>();foreach (var scheme in await Schemes.GetRequestHandlerSchemesAsync()){var handler = await handlers.GetHandlerAsync(context, scheme.Name) as IAuthenticationRequestHandler;if (handler != null && await handler.HandleRequestAsync()){return;}}var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync();if (defaultAuthenticate != null){var result = await context.AuthenticateAsync(defaultAuthenticate.Name);if (result?.Principal != null){context.User = result.Principal;}}await _next(context);}}
}

在 GitHub 中查看 AuthenticationMiddle 源码

参考资料
  • https://docs.microsoft.com/en-us/aspnet/core/security/authentication/?view=aspnetcore-5.0

相关文章:

ASP.NET Core认证原理和实现

ASP.NET Core认证原理和实现 AuthenticationHttpContextExtensions AuthenticationHttpContextExtensions 类是对 HttpContext 认证相关的扩展&#xff0c;它提供了如下扩展方法&#xff1a; public static class AuthenticationHttpContextExtensions {public static Task&l…...

基于OpenCV的图像颜色与形状识别的原理2

基于OpenCV的图像颜色与形状识别通常涉及以下几个步骤&#xff1a; 图像读取&#xff1a;使用OpenCV的cv2.imread()函数读取图像。预处理&#xff1a;可能包括图像的灰度转换、二值化、滤波等&#xff0c;以减少噪声和无关信息。颜色识别&#xff1a;颜色空间转换&#xff1a;…...

无法获取前置摄像头的预览图像?【Bug已解决-鸿蒙开发】

文章目录 项目场景:问题描述原因分析:解决方案:此Bug解决方案总结HarmonyOS和OpenHarmony区别和联系项目场景: 最近也是遇到了这个问题,看到网上也有人在询问这个问题,本文总结了自己和其他人的解决经验,解决了无法获取前置摄像头的预览图像的问题。 问题:前置摄像头…...

微信小程序的bindtap和catchtap的区别

一. 事件 1.事件是视图层到逻辑层的通讯方式。 2. 事件可以将用户的行为反馈到逻辑层进行处理。 3. 事件可以绑定在组件上&#xff0c;当达到触发事件&#xff0c;就会执行逻辑层中对应的事件处理函数。 二. 如何使用事件 1. 简单来说就是将事件绑定到组件上面&#xff0c;bi…...

python哈希算法实现

以下是用Python实现SHA-256算法的示例代码&#xff1a; import hashlibdef sha256(message):# 创建SHA-256哈希对象sha256_hash hashlib.sha256()# 更新哈希对象的输入消息sha256_hash.update(message.encode(utf-8))# 计算哈希值并返回十六进制表示return sha256_hash.hexdi…...

SpringBoot实用开发(三)-- Redis提供API接口 -- StringRedisTemplate

引言: 由于redis内部不提供java对象的存储格式,因此当操作的数据以对象的形式存在时,会进行转码,转换成字符串格式后进行操作。为了方便开发者使用基于字符串为数据的操作,springboot整合redis时提供了专用的API接口StringRedisTemplate,你可以理解为这是RedisTe…...

【Qt-编码】

Qt编程指南 ■ 编码■ ASCII■ ANSI■ GB2312■ GBK■ GB18030 编码■ Unicode■ UTF-8&#xff1a; ■ Qt接收注射泵GBK编码后显示乱码■■ ■ 编码 ■ ASCII &#xff08;American Standard Code for Information Interchange&#xff0c;美国信息交换标准代码&#xff09;…...

使用Python实现Linux惠尔顿上网认证客户端

在本文中&#xff0c;我们将展示如何使用Python编写一个简单的脚本来实现Linux下的惠尔顿上网认证。以下是我们需要的参数和值&#xff1a; wholeton_host: 惠尔顿服务器地址&#xff0c;例如 192.168.10.10wholeton_user: 用户名&#xff0c;例如 AABBCCwholeton_pass: 密码&…...

【漏洞复现】某检测系统(admintool)接口任意文件上传漏洞

文章目录 前言声明一、漏洞详情二、影响版本三、漏洞复现四、修复建议 前言 湖南建研检测系统 admintool接口任意文件上传漏洞&#xff0c;攻击者可通过该漏洞获取服务器敏感信息。 声明 请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者…...

检测如下MHA运行条件【踩坑记录】

【masterha_check_ssh --conf/etc/mha/app1.cnf&#xff1a;SSH免密登录】 【错误信息1】 [error][/usr/share/perl5/vendor_perl/MHA/SSHCheck.pm, ln111] SSH connection from root10.0.0.53(10.0.0.53:22) to root10.0.0.51(10.0.0.51:22) failed! 【错误反馈】就是服务器…...

使用js编写一个函数判断所有数据类型的通用方法

一、typeof 在 JavaScript 里使用 typeof 来判断数据类型&#xff0c;只能区分基本类型&#xff0c;即 “number”&#xff0c;”string”&#xff0c;”undefined”&#xff0c;”boolean”&#xff0c;”object” 五种。 对于数组、对象来说&#xff0c;其关系错综复杂&…...

AutoSAR(基础入门篇)2.1Autosar架构中的AppL

目录 一、Autosar中APPL概述 1、AppL的内容 2、汽车顶灯示例 3、SWC的通信...

怎么使用jupter notebook并配置环境变量

有的时候需要使用Jupyter Notebook运行代码&#xff0c;Jupyter Notebook的主要特点&#xff1a; ① 编程时具有语法高亮、缩进、tab补全的功能。 ② 可直接通过浏览器运行代码&#xff0c;同时在代码块下方展示运行结果。 ③ 以富媒体格式展示计算结果。富媒体格式包括&…...

深信服技术认证“SCSA-S”划重点:文件上传与解析漏洞

为帮助大家更加系统化地学习网络安全知识&#xff0c;以及更高效地通过深信服安全服务认证工程师考核&#xff0c;深信服特别推出“SCSA-S认证备考秘笈”共十期内容&#xff0c;“考试重点”内容框架&#xff0c;帮助大家快速get重点知识~ 划重点来啦 *点击图片放大展示 深信服…...

Sql 动态行转列

SELECT ID, Name, [Month],auth FROM dbo.Test3 数据列表&#xff1a; 1.静态行专列 Select auth, MAX( CASE WHEN [Month] 一月 then Name else null end) 一月, MAX( CASE WHEN [Month] 二月 then Name else null end) 二月, MAX( CASE WHEN…...

记录 App webview加载h5页面有上传图片,应用商店审核必须加授权提示问题的解决方案

场景&#xff1a; 1、项目内加载了h5页面的七鱼客服&#xff0c;(相当于webview 加载help.html) 2、应用上架要求必须有授权提示弹窗 解决思路 1、客服的h5 页面在跳转时候&#xff0c;给父层webview 发消息&#xff0c;注入一段拦截代码&#xff0c;监听到有上传操作时候&…...

Stable Diffusion模型原理

1 Stable Diffusion概述 1.1 图像生成的发展 在Stable Diffusion诞生之前&#xff0c;计算机视觉和机器学习方面最重要的突破是 GAN&#xff08;Generative Adversarial Networks 生成对抗网络&#xff09;。GAN让超越训练数据已有内容成为可能&#xff0c;从而打开了一个全新…...

【Android 13】使用Android Studio调试系统应用之Settings移植(二):构建settings app项目目录

文章目录 一、篇头二、系列文章2.1 Android 13 系列文章2.2 Android 9 系列文章2.3 Android 11 系列文章三、准备工作3.1 创建目录3.2 初始化 git 仓库四、提取settings原始代码4.1 提取目标4.2 源码路径4.2.1 settings app4.2.2 SettingsLib4.3 存放位置...

w16php系列之基础数组

一、索引数组 概念 索引数组 是指键名为整数的数组。默认情况下&#xff0c;索引数组的键名是从0开始&#xff0c;并依次递增。它主要适用于利用位置&#xff08;0、1、2……&#xff09;来标识数组元素的情况。另外&#xff0c;索引数组的键名也可以自己指定 示例代码 <…...

【C语言】指针详解(四)

目录 1.assert断言 2.指针的使用和传址调用 2.1strlen的模拟使用 2.2传值调用和传址调用 1.assert断言 assert.h头文件定义了宏 assert()&#xff0c;用于在运行时确保程序符合指定条件&#xff0c;如果不符合&#xff0c;就报错终止运行。这个宏常常被称为“断言”。 例如…...

CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型

CVPR 2025 | MIMO&#xff1a;支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题&#xff1a;MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者&#xff1a;Yanyuan Chen, Dexuan Xu, Yu Hu…...

什么是EULA和DPA

文章目录 EULA&#xff08;End User License Agreement&#xff09;DPA&#xff08;Data Protection Agreement&#xff09;一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA&#xff08;End User License Agreement&#xff09; 定义&#xff1a; EULA即…...

Java面试专项一-准备篇

一、企业简历筛选规则 一般企业的简历筛选流程&#xff1a;首先由HR先筛选一部分简历后&#xff0c;在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如&#xff1a;Boss直聘&#xff08;招聘方平台&#xff09; 直接按照条件进行筛选 例如&#xff1a…...

Mac下Android Studio扫描根目录卡死问题记录

环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中&#xff0c;提示一个依赖外部头文件的cpp源文件需要同步&#xff0c;点…...

Angular微前端架构:Module Federation + ngx-build-plus (Webpack)

以下是一个完整的 Angular 微前端示例&#xff0c;其中使用的是 Module Federation 和 npx-build-plus 实现了主应用&#xff08;Shell&#xff09;与子应用&#xff08;Remote&#xff09;的集成。 &#x1f6e0;️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...

OD 算法题 B卷【正整数到Excel编号之间的转换】

文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的&#xff1a;a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...

使用SSE解决获取状态不一致问题

使用SSE解决获取状态不一致问题 1. 问题描述2. SSE介绍2.1 SSE 的工作原理2.2 SSE 的事件格式规范2.3 SSE与其他技术对比2.4 SSE 的优缺点 3. 实战代码 1. 问题描述 目前做的一个功能是上传多个文件&#xff0c;这个上传文件是整体功能的一部分&#xff0c;文件在上传的过程中…...

2025年- H71-Lc179--39.组合总和(回溯,组合)--Java版

1.题目描述 2.思路 当前的元素可以重复使用。 &#xff08;1&#xff09;确定回溯算法函数的参数和返回值&#xff08;一般是void类型&#xff09; &#xff08;2&#xff09;因为是用递归实现的&#xff0c;所以我们要确定终止条件 &#xff08;3&#xff09;单层搜索逻辑 二…...

基于谷歌ADK的 智能产品推荐系统(2): 模块功能详解

在我的上一篇博客&#xff1a;基于谷歌ADK的 智能产品推荐系统(1): 功能简介-CSDN博客 中我们介绍了个性化购物 Agent 项目&#xff0c;该项目展示了一个强大的框架&#xff0c;旨在模拟和实现在线购物环境中的智能导购。它不仅仅是一个简单的聊天机器人&#xff0c;更是一个集…...

2025-06-01-Hive 技术及应用介绍

Hive 技术及应用介绍 参考资料 Hive 技术原理Hive 架构及应用介绍Hive - 小海哥哥 de - 博客园https://cwiki.apache.org/confluence/display/Hive/Home(官方文档) Apache Hive 是基于 Hadoop 构建的数据仓库工具&#xff0c;它为海量结构化数据提供类 SQL 的查询能力&#xf…...