前言

简单介绍一下权限源码阅读一下。

正文

一直有人对授权这个事情上争论不休,有的人认为在输入账户密码给后台这个时候进行了授权,因为认为发送了一个身份令牌,令牌里面可能有些用户角色信息,认为这就是授权,有的人认为这只是获取令牌的过程。

现实生活中有一个是授权证书,那么有人认为token 是授权证书,但这只是颁发证书。账户密码获取获取身份令牌也不是认证,认证是证明你的身份令牌有效的过程。

那么netcore 中是如何解释授权的:

授权是指确定用户可执行的操作的过程。故而实际上,获取身份令牌只是获取令牌,授权是指在访问过程中,确认是否可以访问的过程。身份令牌中有角色,有些是根据角色还确定是否可以访问的,这就是授权了。

不过随着业务的复杂,网关可以根据角色授权接口,也可以根据自己的策略了,授权的过程五花八门的。

在网关中一般有认证和授权两部分,先认证再授权,先确定合法身份在来确定一下授权。

先来看下认证吧,有些人一认证就想到了jwt,或者想到了具体的认证方式,其实认证就是你的系统认为它符合了合法身份,和具体的东西没有关系,是一个抽象的概念。

app.UseAuthentication();

通过上面这个看下认证过程。

然后看下具体的中间件。

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;
}

从这里看呢,IAuthenticationSchemeProvider 提供了认证解决方案,可以看下这个接口。

/// <summary>
/// Responsible for managing what authenticationSchemes are supported.
/// </summary>
public interface IAuthenticationSchemeProvider
{
/// <summary>
/// Returns all currently registered <see cref="AuthenticationScheme"/>s.
/// </summary>
/// <returns>All currently registered <see cref="AuthenticationScheme"/>s.</returns>
Task<IEnumerable<AuthenticationScheme>> GetAllSchemesAsync(); /// <summary>
/// Returns the <see cref="AuthenticationScheme"/> matching the name, or null.
/// </summary>
/// <param name="name">The name of the authenticationScheme.</param>
/// <returns>The scheme or null if not found.</returns>
Task<AuthenticationScheme?> GetSchemeAsync(string name); /// <summary>
/// Returns the scheme that will be used by default for <see cref="IAuthenticationService.AuthenticateAsync(HttpContext, string)"/>.
/// This is typically specified via <see cref="AuthenticationOptions.DefaultAuthenticateScheme"/>.
/// Otherwise, this will fallback to <see cref="AuthenticationOptions.DefaultScheme"/>.
/// </summary>
/// <returns>The scheme that will be used by default for <see cref="IAuthenticationService.AuthenticateAsync(HttpContext, string)"/>.</returns>
Task<AuthenticationScheme?> GetDefaultAuthenticateSchemeAsync(); /// <summary>
/// Returns the scheme that will be used by default for <see cref="IAuthenticationService.ChallengeAsync(HttpContext, string, AuthenticationProperties)"/>.
/// This is typically specified via <see cref="AuthenticationOptions.DefaultChallengeScheme"/>.
/// Otherwise, this will fallback to <see cref="AuthenticationOptions.DefaultScheme"/>.
/// </summary>
/// <returns>The scheme that will be used by default for <see cref="IAuthenticationService.ChallengeAsync(HttpContext, string, AuthenticationProperties)"/>.</returns>
Task<AuthenticationScheme?> GetDefaultChallengeSchemeAsync(); /// <summary>
/// Returns the scheme that will be used by default for <see cref="IAuthenticationService.ForbidAsync(HttpContext, string, AuthenticationProperties)"/>.
/// This is typically specified via <see cref="AuthenticationOptions.DefaultForbidScheme"/>.
/// Otherwise, this will fallback to <see cref="GetDefaultChallengeSchemeAsync"/> .
/// </summary>
/// <returns>The scheme that will be used by default for <see cref="IAuthenticationService.ForbidAsync(HttpContext, string, AuthenticationProperties)"/>.</returns>
Task<AuthenticationScheme?> GetDefaultForbidSchemeAsync(); /// <summary>
/// Returns the scheme that will be used by default for <see cref="IAuthenticationService.SignInAsync(HttpContext, string, System.Security.Claims.ClaimsPrincipal, AuthenticationProperties)"/>.
/// This is typically specified via <see cref="AuthenticationOptions.DefaultSignInScheme"/>.
/// Otherwise, this will fallback to <see cref="AuthenticationOptions.DefaultScheme"/>.
/// </summary>
/// <returns>The scheme that will be used by default for <see cref="IAuthenticationService.SignInAsync(HttpContext, string, System.Security.Claims.ClaimsPrincipal, AuthenticationProperties)"/>.</returns>
Task<AuthenticationScheme?> GetDefaultSignInSchemeAsync(); /// <summary>
/// Returns the scheme that will be used by default for <see cref="IAuthenticationService.SignOutAsync(HttpContext, string, AuthenticationProperties)"/>.
/// This is typically specified via <see cref="AuthenticationOptions.DefaultSignOutScheme"/>.
/// Otherwise, this will fallback to <see cref="GetDefaultSignInSchemeAsync"/> .
/// </summary>
/// <returns>The scheme that will be used by default for <see cref="IAuthenticationService.SignOutAsync(HttpContext, string, AuthenticationProperties)"/>.</returns>
Task<AuthenticationScheme?> GetDefaultSignOutSchemeAsync(); /// <summary>
/// Registers a scheme for use by <see cref="IAuthenticationService"/>.
/// </summary>
/// <param name="scheme">The scheme.</param>
void AddScheme(AuthenticationScheme scheme); /// <summary>
/// Registers a scheme for use by <see cref="IAuthenticationService"/>.
/// </summary>
/// <param name="scheme">The scheme.</param>
/// <returns>true if the scheme was added successfully.</returns>
bool TryAddScheme(AuthenticationScheme scheme)
{
try
{
AddScheme(scheme);
return true;
}
catch {
return false;
}
} /// <summary>
/// Removes a scheme, preventing it from being used by <see cref="IAuthenticationService"/>.
/// </summary>
/// <param name="name">The name of the authenticationScheme being removed.</param>
void RemoveScheme(string name); /// <summary>
/// Returns the schemes in priority order for request handling.
/// </summary>
/// <returns>The schemes in priority order for request handling</returns>
Task<IEnumerable<AuthenticationScheme>> GetRequestHandlerSchemesAsync();
}

虽然没有看到具体的provider,但是呢,可以通过接口注释看个大概哈。

比如说AuthenticationScheme 是认证方案的意思,从英文表面理解哈。然后里面有方法增删改查,意味着我们可以有多种认证方式。

这其实是刚需,因为比如以前颁发的身份令牌和现在接口颁发的身份令牌不一样了,那么为了无缝衔接,可以认可两种认证方式。

那么看下AuthenticationScheme 认证方案里面有些啥吧。

/// <summary>
/// AuthenticationSchemes assign a name to a specific <see cref="IAuthenticationHandler"/>
/// handlerType.
/// </summary>
public class AuthenticationScheme
{
/// <summary>
/// Initializes a new instance of <see cref="AuthenticationScheme"/>.
/// </summary>
/// <param name="name">The name for the authentication scheme.</param>
/// <param name="displayName">The display name for the authentication scheme.</param>
/// <param name="handlerType">The <see cref="IAuthenticationHandler"/> type that handles this scheme.</param>
public AuthenticationScheme(string name, string? displayName, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type handlerType)
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
if (handlerType == null)
{
throw new ArgumentNullException(nameof(handlerType));
}
if (!typeof(IAuthenticationHandler).IsAssignableFrom(handlerType))
{
throw new ArgumentException("handlerType must implement IAuthenticationHandler.");
} Name = name;
HandlerType = handlerType;
DisplayName = displayName;
} /// <summary>
/// The name of the authentication scheme.
/// </summary>
public string Name { get; } /// <summary>
/// The display name for the scheme. Null is valid and used for non user facing schemes.
/// </summary>
public string? DisplayName { get; } /// <summary>
/// The <see cref="IAuthenticationHandler"/> type that handles this scheme.
/// </summary>
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
public Type HandlerType { get; }
}

这里面有name 和displayname,一个是名称,一个是显示名称,相信很多人都见到过这样的类,里面有name 还有 displayname。

不要那么计较,显示名称是为了好大家好而已。比如说我们的sex 表示性别,那么displayname 可以写显示名称。

比如说你的一个计划类,里面可以有name 和 displayname。name 是JC159,displayname 是瞎扯计划,JC159 多难理解啊,瞎扯计划多好理解,瞎扯啊。

然后里面有一个是HandlerType,叫做处理类型,处理认证计划的类型。上面有注释,这个类型继承IAuthenticationHandler这个接口,那么也就是这个方案将由实现IAuthenticationHandler的类来处理,具体看实际的处理方案,比如jwt。

然后看下中间件的invoke。

/// <summary>
/// Invokes the middleware performing authentication.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/>.</param>
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 request
var 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);
}

一段一段看吧。

context.Features.Set<IAuthenticationFeature>(new AuthenticationFeature
{
OriginalPath = context.Request.Path,
OriginalPathBase = context.Request.PathBase
});

Features 是经过一个集合,比如我们经过中间件,我们可以向里面写入一些东西,然后供下一个中间件使用都行,有点像是游戏里面背包的功能。

var 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;
}
}

这里面就是获取就是获取相应的认真方案处理器,然后进行执行HandleRequestAsync。

值得主意的是,这里面并不是执行IAuthenticationHandler的方法,而是IAuthenticationRequestHandler的方法。

所以这并不意味这我们的写入的每个方案都必须通过,而是如果我们的写入的每个认证方案继承IAuthenticationRequestHandler,那么必须通过其中的HandleRequestAsync方法。

然后看下IAuthenticationRequestHandler 这个哈。

/// <summary>
/// Used to determine if a handler wants to participate in request processing.
/// </summary>
public interface IAuthenticationRequestHandler : IAuthenticationHandler
{
/// <summary>
/// Gets a value that determines if the request should stop being processed.
/// <para>
/// This feature is supported by the Authentication middleware
/// which does not invoke any subsequent <see cref="IAuthenticationHandler"/> or middleware configured in the request pipeline
/// if the handler returns <see langword="true" />.
/// </para>
/// </summary>
/// <returns><see langword="true" /> if request processing should stop.</returns>
Task<bool> HandleRequestAsync();
}

继续往下看:

var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync();
if (defaultAuthenticate != null)
{
var result = await context.AuthenticateAsync(defaultAuthenticate.Name);
if (result?.Principal != null)
{
context.User = result.Principal;
}
}

继续往下看哈,然后里面也有这个哈,如果有默认的认证方案,那么context.User 会通过默认认证方案的处理器进行获取。也就是说如果我们设置了默认方案,那么就会通过默认方案来进行认证。

await _next(context);

这个表示继续往下执行了。

那么来看下具体服务的认证吧,比如说jwt的。

services.AddAuthentication("Bearer")
// 添加JwtBearer服务
.AddJwtBearer(o =>
{
o.TokenValidationParameters = tokenValidationParameters;
o.Events = new JwtBearerEvents
{
OnAuthenticationFailed = context =>
{
// 如果过期,则把<是否过期>添加到,返回头信息中
if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
{
context.Response.Headers.Add("Token-Expired", "true");
}
return Task.CompletedTask;
}
};
});

首先来看一下:

services.AddAuthentication("Bearer")

这里面就是设置默认的认证方案:

public static AuthenticationBuilder AddAuthentication(this IServiceCollection services, string defaultScheme)
=> services.AddAuthentication(o => o.DefaultScheme = defaultScheme); public static AuthenticationBuilder AddAuthentication(this IServiceCollection services, Action<AuthenticationOptions> configureOptions) {
if (services == null)
{
throw new ArgumentNullException(nameof(services));
} if (configureOptions == null)
{
throw new ArgumentNullException(nameof(configureOptions));
} var builder = services.AddAuthentication();
services.Configure(configureOptions);
return builder;
}

看一下:var builder = services.AddAuthentication();

这个哈,这个才是具体增加具体服务的。

public static AuthenticationBuilder AddAuthentication(this IServiceCollection services)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
} services.AddAuthenticationCore();
services.AddDataProtection();
services.AddWebEncoders();
services.TryAddSingleton<ISystemClock, SystemClock>();
return new AuthenticationBuilder(services);
}

然后看下services.AddAuthenticationCore();,为什么看下这个呢?难道我提前看了这个东西吗?

不是,因为我们知道分层的时候有个Core的层,是具体实现的,那么这种带core 一般就是具体实现方式了。

public static IServiceCollection AddAuthenticationCore(this IServiceCollection services)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
} services.TryAddScoped<IAuthenticationService, AuthenticationService>();
services.TryAddSingleton<IClaimsTransformation, NoopClaimsTransformation>(); // Can be replaced with scoped ones that use DbContext
services.TryAddScoped<IAuthenticationHandlerProvider, AuthenticationHandlerProvider>();
services.TryAddSingleton<IAuthenticationSchemeProvider, AuthenticationSchemeProvider>();
return services;
}

前面我们看了这个IAuthenticationHandlerProvider 和IAuthenticationSchemeProvider ,那么这里可以看到他们的具体实现是AuthenticationHandlerProvider和AuthenticationSchemeProvider。

前面提及到会通过handletype来获取具体的处理器,那么来看下具体怎么实现的吧。

/// <summary>
/// Returns the handler instance that will be used.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="authenticationScheme">The name of the authentication scheme being handled.</param>
/// <returns>The handler instance.</returns>
public async Task<IAuthenticationHandler> GetHandlerAsync(HttpContext context, string authenticationScheme)
{
if (_handlerMap.ContainsKey(authenticationScheme))
{
return _handlerMap[authenticationScheme];
} 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;
}

看这个GetHandlerAsync,是通过依赖注入的方式来获取的,根据方案里面的GetHandlerAsync。

那么从这里就能猜到jwt的具体实现了,那么直接来看吧。

services.AddAuthentication("Bearer")
// 添加JwtBearer服务
.AddJwtBearer(o =>
{
o.TokenValidationParameters = tokenValidationParameters;
o.Events = new JwtBearerEvents
{
OnAuthenticationFailed = context =>
{
// 如果过期,则把<是否过期>添加到,返回头信息中
if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
{
context.Response.Headers.Add("Token-Expired", "true");
}
return Task.CompletedTask;
}
};
});

其实不建议这么写的,应该是:

直接标明这里使用的策略,之所以这个能够生效,是因为默认的是

红框框部分是Bearer,但是不友好,对框架不熟,容易形成误导。

继续往下看:

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);
}

AddScheme 就是具体的注入了:

/// <summary>
/// Adds a <see cref="AuthenticationScheme"/> which can be used by <see cref="IAuthenticationService"/>.
/// </summary>
/// <typeparam name="TOptions">The <see cref="AuthenticationSchemeOptions"/> type to configure the handler."/>.</typeparam>
/// <typeparam name="THandler">The <see cref="AuthenticationHandler{TOptions}"/> used to handle this scheme.</typeparam>
/// <param name="authenticationScheme">The name of this scheme.</param>
/// <param name="displayName">The display name of this scheme.</param>
/// <param name="configureOptions">Used to configure the scheme options.</param>
/// <returns>The builder.</returns>
public virtual AuthenticationBuilder AddScheme<TOptions, THandler>(string authenticationScheme, string displayName, Action<TOptions> configureOptions)
where TOptions : AuthenticationSchemeOptions, new()
where THandler : AuthenticationHandler<TOptions>
=> AddSchemeHelper<TOptions, THandler>(authenticationScheme, displayName, configureOptions);

然后加入到认证方案中去,JwtBearerOptions 就是这个方案的配置,JwtBearerHandler就是具体的处理,看下AddSchemeHelper。

private AuthenticationBuilder AddSchemeHelper<TOptions, THandler>(string authenticationScheme, string displayName, Action<TOptions> configureOptions)
where TOptions : class, new()
where THandler : class, IAuthenticationHandler
{
Services.Configure<AuthenticationOptions>(o =>
{
o.AddScheme(authenticationScheme, scheme => {
scheme.HandlerType = typeof(THandler);
scheme.DisplayName = displayName;
});
});
if (configureOptions != null)
{
Services.Configure(authenticationScheme, configureOptions);
}
Services.AddTransient<THandler>();
return this;
}

分步骤看下:

Services.Configure<AuthenticationOptions>(o =>
{
o.AddScheme(authenticationScheme, scheme => {
scheme.HandlerType = typeof(THandler);
scheme.DisplayName = displayName;
});
});

这一步就是添加具体的认证方案。

if (configureOptions != null)
{
Services.Configure(authenticationScheme, configureOptions);
}
Services.AddTransient<THandler>();

这一步就是注入配置文件,并且将处理器注入到ioc中,这里就是JwtBearerHandler了。

JwtBearerHandler 就不看了,就是一些具体的实现,根据配置文件,然后处理,就属于jwt的知识了。

补充

这里扩容一下配置的知识,主要解释一下JwtBearerHandler 是如何根据不同的authenticationScheme 获取不同的配置的。

Services.Configure(authenticationScheme, configureOptions);
public static IServiceCollection Configure<TOptions>(this IServiceCollection services, string name, Action<TOptions> configureOptions)
where TOptions : class
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
} if (configureOptions == null)
{
throw new ArgumentNullException(nameof(configureOptions));
} services.AddOptions();
services.AddSingleton<IConfigureOptions<TOptions>>(new ConfigureNamedOptions<TOptions>(name, configureOptions));
return services;
}

看到吧,实际上获IConfigureOptions,会获取一组ConfigureNamedOptions,然后通过name筛选出来。

看下JwtBearerHandler:

public class JwtBearerHandler : AuthenticationHandler<JwtBearerOptions>
public JwtBearerHandler(IOptionsMonitor<JwtBearerOptions> options, ILoggerFactory logger, UrlEncoder encoder, IDataProtectionProvider dataProtection, ISystemClock clock)
: base(options, logger, encoder, clock)
{ }

将options 传给了AuthenticationHandler。

那么看下AuthenticationHandler 中如何处理的吧。

初始化的时候:

/// <summary>
/// Initialize the handler, resolve the options and validate them.
/// </summary>
/// <param name="scheme"></param>
/// <param name="context"></param>
/// <returns></returns>
public async Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)
{
if (scheme == null)
{
throw new ArgumentNullException(nameof(scheme));
}
if (context == null)
{
throw new ArgumentNullException(nameof(context));
} Scheme = scheme;
Context = context; Options = OptionsMonitor.Get(Scheme.Name) ?? new TOptions();
Options.Validate(Scheme.Name); await InitializeEventsAsync();
await InitializeHandlerAsync();
}

进行一波筛选而来的哈。

下一节看下授权的源码吧。

重新整理 .net core 实践篇——— 权限源码阅读四十五]的更多相关文章

  1. 重新整理 .net core 实践篇——— 权限中间件源码阅读[四十六]

    前言 前面介绍了认证中间件,下面看一下授权中间件. 正文 app.UseAuthorization(); 授权中间件是这个,前面我们提及到认证中间件并不会让整个中间件停止. 认证中间件就两个作用,我们 ...

  2. ABP源码分析四十五:ABP ZERO中的EntityFramework模块

    AbpZeroDbContext:配置ABP.Zero中定义的entity的Dbset EntityFrameworkModelBuilderExtensions:给PrimitiveProperty ...

  3. J.U.C并发框架源码阅读(十五)CopyOnWriteArrayList

    基于版本jdk1.7.0_80 java.util.concurrent.CopyOnWriteArrayList 代码如下 /* * Copyright (c) 2003, 2011, Oracle ...

  4. 重新整理 .net core 实践篇————熔断与限流[三十五]

    前言 简单整理一下熔断与限流,跟上一节息息相关. 正文 polly 的策略类型分为两类: 被动策略(异常处理.结果处理) 主动策略(超时处理.断路器.舱壁隔离.缓存) 熔断和限流通过下面主动策略来实现 ...

  5. 34 网络相关函数(二)——live555源码阅读(四)网络

    34 网络相关函数(二)——live555源码阅读(四)网络 34 网络相关函数(二)——live555源码阅读(四)网络 2)socketErr 套接口错误 3)groupsockPriv函数 4) ...

  6. 33 网络相关函数(一)——live555源码阅读(四)网络

    33 网络相关函数(一)——live555源码阅读(四)网络 33 网络相关函数(一)——live555源码阅读(四)网络 简介 1)IsMulticastAddress多播(组播)地址判断函数 多播 ...

  7. 31 GroupSock(AddressString)——live555源码阅读(四)网络

    31 GroupSock(AddressString)——live555源码阅读(四)网络 31 GroupSock(AddressString)——live555源码阅读(四)网络 简介 Addre ...

  8. 29 GroupSock(NetAddressList)——live555源码阅读(四)网络

    29 GroupSock(NetAddressList)——live555源码阅读(四)网络 29 GroupSock(NetAddressList)——live555源码阅读(四)网络 简介 Net ...

  9. 28 GroupSock(NetAddress)——live555源码阅读(四)网络

    28 GroupSock(NetAddress)——live555源码阅读(四)网络 28 GroupSock(NetAddress)——live555源码阅读(四)网络 简介 1) NetAddre ...

随机推荐

  1. 极简SpringBoot指南-Chapter01-如何用Spring框架声明Bean

    仓库地址 w4ngzhen/springboot-simple-guide: This is a project that guides SpringBoot users to get started ...

  2. Windows下node-gyp查找VS安装路径简单解析

    node-gyp的作用我已经不想赘述了,这里给一个我之前文章的链接:cnblogs看这里,知乎看这里.本文主要从源码入手,介绍node-gyp查找VisualStudio的过程 为了方便我们研究nod ...

  3. ☕【Java技术指南】「编译器专题」深入分析探究“静态编译器”(JAVA\IDEA\ECJ编译器)是否可以实现代码优化?

    技术分析 大家都知道Eclipse已经实现了自己的编译器,命名为 Eclipse编译器for Java (ECJ). ECJ 是 Eclipse Compiler for Java 的缩写,是 Jav ...

  4. C#开发BIMFACE系列53 WinForm程序中使用CefSharp加载模型图纸1 简单应用

    BIMFACE二次开发系列目录     [已更新最新开发文章,点击查看详细] 在我的博客<C#开发BIMFACE系列52 CS客户端集成BIMFACE应用的技术方案>中介绍了多种集成BIM ...

  5. Sequence Model-week1编程题3-用LSTM网络生成爵士乐

    Improvise a Jazz Solo with an LSTM Network 实现使用LSTM生成音乐的模型,你可以在结束时听你自己的音乐,接下来你将会学习到: 使用LSTM生成音乐 使用深度 ...

  6. [软工顶级理解组] Alpha阶段事后分析

    目录 设想和目标 计划 资源 变更管理 设计/实现 测试/发布 团队的角色,管理,合作 总结 质量提高 会议截图 设想和目标 我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰 ...

  7. Noip模拟76 2021.10.14

    T1 洛希极限 上来一道大数据结构或者单调队列优化$dp$ 真就没分析出来正解复杂度 正解复杂度$O(q+nm)$,但是据说我的复杂度是假的 考虑一个点转移最优情况是从它上面的一个反$L$形转移过来 ...

  8. Noip模拟33垫底反思 2021.8.8

    T1 Hunter 考场上没写$%p$挂了25分.也是很牛皮,以后打完过了样例一定要检查 因为样例太小了......很容易忘记%%%% 正解随便手模就出来了. 1 #include<bits/s ...

  9. 震惊,hzoi的分差竟然折磨大,活到爆!

    众所周知,hzoi的分差非常"大",那么究竟有多大呢?最近,一位外国小哥开发出了hzoi的分差竟然折磨大,活到爆!的方法,这究竟是怎么一回事呢?快和小编一起来看看吧- 竟然1分就可 ...

  10. [经验] 电源抑制比(PSRR)的测量原理及解决方法

    PSRR(Power supply rejection ratio)又称电源抑制比,是衡量电路对于输入电源中纹波抑制大小的重要参数,表示为输出纹波和输入纹波的对数比,单位为分贝(dB)[1],其计算公 ...