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 认证相关的扩展,它提供了如下扩展方法: public static class AuthenticationHttpContextExtensions {public static Task&l…...
基于OpenCV的图像颜色与形状识别的原理2
基于OpenCV的图像颜色与形状识别通常涉及以下几个步骤: 图像读取:使用OpenCV的cv2.imread()函数读取图像。预处理:可能包括图像的灰度转换、二值化、滤波等,以减少噪声和无关信息。颜色识别:颜色空间转换:…...
无法获取前置摄像头的预览图像?【Bug已解决-鸿蒙开发】
文章目录 项目场景:问题描述原因分析:解决方案:此Bug解决方案总结HarmonyOS和OpenHarmony区别和联系项目场景: 最近也是遇到了这个问题,看到网上也有人在询问这个问题,本文总结了自己和其他人的解决经验,解决了无法获取前置摄像头的预览图像的问题。 问题:前置摄像头…...
微信小程序的bindtap和catchtap的区别
一. 事件 1.事件是视图层到逻辑层的通讯方式。 2. 事件可以将用户的行为反馈到逻辑层进行处理。 3. 事件可以绑定在组件上,当达到触发事件,就会执行逻辑层中对应的事件处理函数。 二. 如何使用事件 1. 简单来说就是将事件绑定到组件上面,bi…...
python哈希算法实现
以下是用Python实现SHA-256算法的示例代码: 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: ■ Qt接收注射泵GBK编码后显示乱码■■ ■ 编码 ■ ASCII (American Standard Code for Information Interchange,美国信息交换标准代码)…...
使用Python实现Linux惠尔顿上网认证客户端
在本文中,我们将展示如何使用Python编写一个简单的脚本来实现Linux下的惠尔顿上网认证。以下是我们需要的参数和值: wholeton_host: 惠尔顿服务器地址,例如 192.168.10.10wholeton_user: 用户名,例如 AABBCCwholeton_pass: 密码&…...
【漏洞复现】某检测系统(admintool)接口任意文件上传漏洞
文章目录 前言声明一、漏洞详情二、影响版本三、漏洞复现四、修复建议 前言 湖南建研检测系统 admintool接口任意文件上传漏洞,攻击者可通过该漏洞获取服务器敏感信息。 声明 请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息或者…...
检测如下MHA运行条件【踩坑记录】
【masterha_check_ssh --conf/etc/mha/app1.cnf: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 来判断数据类型,只能区分基本类型,即 “number”,”string”,”undefined”,”boolean”,”object” 五种。 对于数组、对象来说,其关系错综复杂&…...
AutoSAR(基础入门篇)2.1Autosar架构中的AppL
目录 一、Autosar中APPL概述 1、AppL的内容 2、汽车顶灯示例 3、SWC的通信...
怎么使用jupter notebook并配置环境变量
有的时候需要使用Jupyter Notebook运行代码,Jupyter Notebook的主要特点: ① 编程时具有语法高亮、缩进、tab补全的功能。 ② 可直接通过浏览器运行代码,同时在代码块下方展示运行结果。 ③ 以富媒体格式展示计算结果。富媒体格式包括&…...
深信服技术认证“SCSA-S”划重点:文件上传与解析漏洞
为帮助大家更加系统化地学习网络安全知识,以及更高效地通过深信服安全服务认证工程师考核,深信服特别推出“SCSA-S认证备考秘笈”共十期内容,“考试重点”内容框架,帮助大家快速get重点知识~ 划重点来啦 *点击图片放大展示 深信服…...
Sql 动态行转列
SELECT ID, Name, [Month],auth FROM dbo.Test3 数据列表: 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页面有上传图片,应用商店审核必须加授权提示问题的解决方案
场景: 1、项目内加载了h5页面的七鱼客服,(相当于webview 加载help.html) 2、应用上架要求必须有授权提示弹窗 解决思路 1、客服的h5 页面在跳转时候,给父层webview 发消息,注入一段拦截代码,监听到有上传操作时候&…...
Stable Diffusion模型原理
1 Stable Diffusion概述 1.1 图像生成的发展 在Stable Diffusion诞生之前,计算机视觉和机器学习方面最重要的突破是 GAN(Generative Adversarial Networks 生成对抗网络)。GAN让超越训练数据已有内容成为可能,从而打开了一个全新…...
【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系列之基础数组
一、索引数组 概念 索引数组 是指键名为整数的数组。默认情况下,索引数组的键名是从0开始,并依次递增。它主要适用于利用位置(0、1、2……)来标识数组元素的情况。另外,索引数组的键名也可以自己指定 示例代码 <…...
【C语言】指针详解(四)
目录 1.assert断言 2.指针的使用和传址调用 2.1strlen的模拟使用 2.2传值调用和传址调用 1.assert断言 assert.h头文件定义了宏 assert(),用于在运行时确保程序符合指定条件,如果不符合,就报错终止运行。这个宏常常被称为“断言”。 例如…...
【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...
《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...
GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...
【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...
浪潮交换机配置track检测实现高速公路收费网络主备切换NQA
浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求,本次涉及的主要是收费汇聚交换机的配置,浪潮网络设备在高速项目很少,通…...
SQL慢可能是触发了ring buffer
简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...
Vue 3 + WebSocket 实战:公司通知实时推送功能详解
📢 Vue 3 WebSocket 实战:公司通知实时推送功能详解 📌 收藏 点赞 关注,项目中要用到推送功能时就不怕找不到了! 实时通知是企业系统中常见的功能,比如:管理员发布通知后,所有用户…...
