IdentityServer4源码解析_1_项目结构
目录
- IdentityServer4源码解析_1_项目结构
- IdentityServer4源码解析_2_元数据接口
- IdentityServer4源码解析_3_认证接口
- IdentityServer4源码解析_4_令牌发放接口
- IdentityServer4源码解析_5_查询用户信息接口
- [IdentityServer4源码解析_6_结束会话接口]
- [IdentityServer4源码解析_7_查询令牌信息接口]
- [IdentityServer4源码解析_8_撤销令牌接口]
简介
Security源码解析系列介绍了微软提供的各种认证架构,其中OAuth2.0,OpenIdConnect属于远程认证架构,所谓远程认证,是指token的颁发是由其他站点完成的。
IdentityServer4是基于OpenIdConnect协议的认证中心框架,可以帮助我们快速搭建微服务认证中心。
初学者可能看到生涩的概念比较头疼,可以将OAuth, OpenIdConnect协议简单理解成需求文档,idsv4基于需求提供了一系列的api实现。
对于idsv还不太了解的可以看下面的资料,本系列主要学习梳理idsv4的源码,结合协议加深理解。
晓晨姐姐系列文章
官方文档
项目结构
项目地址如下
克隆到本地,项目结构如图
核心项目是IdentityServer4,其余的都是与微软框架集成、以及处理持久化的项目。
项目结构如图。Endpoints文件夹就是接口文件,我们先看下依赖注入、中间件的代码,然后看下每个接口。
依赖注入
public static IIdentityServerBuilder AddIdentityServer(this IServiceCollection services)
{
var builder = services.AddIdentityServerBuilder();
builder
.AddRequiredPlatformServices()
.AddCookieAuthentication()
.AddCoreServices()
.AddDefaultEndpoints()
.AddPluggableServices()
.AddValidators()
.AddResponseGenerators()
.AddDefaultSecretParsers()
.AddDefaultSecretValidators();
// provide default in-memory implementation, not suitable for most production scenarios
builder.AddInMemoryPersistedGrants();
return builder;
}
- AddRequiredPlatformServices - 注入平台服务
- IHttpContextAccessor:HttpContext访问器
- IdentityServerOptions:配置类
public static IIdentityServerBuilder AddRequiredPlatformServices(this IIdentityServerBuilder builder)
{
builder.Services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
builder.Services.AddOptions();
builder.Services.AddSingleton(
resolver => resolver.GetRequiredService<IOptions<IdentityServerOptions>>().Value);
builder.Services.AddHttpClient();
return builder;
}
- AddCookieAuthentication - 注入cookie服务
- 注入名称为idsrv的cookie认证架构
- 注入IAuthenticationService的实现IdentityServerAuthenticationService
- 注入IAuthenticationHandlerProvider的实现FederatedSignoutAuthenticationHandlerProvider
public static IIdentityServerBuilder AddCookieAuthentication(this IIdentityServerBuilder builder)
{
builder.Services.AddAuthentication(IdentityServerConstants.DefaultCookieAuthenticationScheme)
.AddCookie(IdentityServerConstants.DefaultCookieAuthenticationScheme)
.AddCookie(IdentityServerConstants.ExternalCookieAuthenticationScheme);
builder.Services.AddSingleton<IConfigureOptions<CookieAuthenticationOptions>, ConfigureInternalCookieOptions>();
builder.Services.AddSingleton<IPostConfigureOptions<CookieAuthenticationOptions>, PostConfigureInternalCookieOptions>();
builder.Services.AddTransientDecorator<IAuthenticationService, IdentityServerAuthenticationService>();
builder.Services.AddTransientDecorator<IAuthenticationHandlerProvider, FederatedSignoutAuthenticationHandlerProvider>();
return builder;
}
- AddCoreServices - 注入核心服务
/// <summary>
/// Adds the core services.
/// </summary>
/// <param name="builder">The builder.</param>
/// <returns></returns>
public static IIdentityServerBuilder AddCoreServices(this IIdentityServerBuilder builder)
{
builder.Services.AddTransient<SecretParser>();
builder.Services.AddTransient<SecretValidator>();
builder.Services.AddTransient<ScopeValidator>();
builder.Services.AddTransient<ExtensionGrantValidator>();
builder.Services.AddTransient<BearerTokenUsageValidator>();
builder.Services.AddTransient<JwtRequestValidator>();
// todo: remove in 3.0
#pragma warning disable CS0618 // Type or member is obsolete
builder.Services.AddTransient<BackChannelHttpClient>();
#pragma warning restore CS0618 // Type or member is obsolete
builder.Services.AddTransient<ReturnUrlParser>();
builder.Services.AddTransient<IdentityServerTools>();
builder.Services.AddTransient<IReturnUrlParser, OidcReturnUrlParser>();
builder.Services.AddScoped<IUserSession, DefaultUserSession>();
builder.Services.AddTransient(typeof(MessageCookie<>));
builder.Services.AddCors();
builder.Services.AddTransientDecorator<ICorsPolicyProvider, CorsPolicyProvider>();
return builder;
}
- AddDefaultEndpoints - 注入接口
- AuthorizeCallbackEndpoint:认证回调接口
- AuthorizeEndpoint:认证接口
- CheckSessionEndpoint:检查会话接口
- DeviceAuthorizationEndpoint:设备认证接口
- DiscoveryEndpoint:元数据键接口
- DiscoveryEndpoint:元数据接口
- EndSessionCallbackEndpoint:结束会话回调接口
- EndSessionEndpoint:结束会话接口
- IntrospectionEndpoint:查询令牌信息接口
- TokenRevocationEndpoint:撤销令牌接口
- TokenEndpoint:发放令牌接口
- UserInfoEndpoint:查询用户信息接口
注入所有默认接口,包括接口名称和地址。请求进来之后,路由类EndPointRouter通过路由来寻找匹配的处理器。
public static IIdentityServerBuilder AddDefaultEndpoints(this IIdentityServerBuilder builder)
{
builder.Services.AddTransient<IEndpointRouter, EndpointRouter>();
builder.AddEndpoint<AuthorizeCallbackEndpoint>(EndpointNames.Authorize, ProtocolRoutePaths.AuthorizeCallback.EnsureLeadingSlash());
builder.AddEndpoint<AuthorizeEndpoint>(EndpointNames.Authorize, ProtocolRoutePaths.Authorize.EnsureLeadingSlash());
builder.AddEndpoint<CheckSessionEndpoint>(EndpointNames.CheckSession, ProtocolRoutePaths.CheckSession.EnsureLeadingSlash());
builder.AddEndpoint<DeviceAuthorizationEndpoint>(EndpointNames.DeviceAuthorization, ProtocolRoutePaths.DeviceAuthorization.EnsureLeadingSlash());
builder.AddEndpoint<DiscoveryKeyEndpoint>(EndpointNames.Discovery, ProtocolRoutePaths.DiscoveryWebKeys.EnsureLeadingSlash());
builder.AddEndpoint<DiscoveryEndpoint>(EndpointNames.Discovery, ProtocolRoutePaths.DiscoveryConfiguration.EnsureLeadingSlash());
builder.AddEndpoint<EndSessionCallbackEndpoint>(EndpointNames.EndSession, ProtocolRoutePaths.EndSessionCallback.EnsureLeadingSlash());
builder.AddEndpoint<EndSessionEndpoint>(EndpointNames.EndSession, ProtocolRoutePaths.EndSession.EnsureLeadingSlash());
builder.AddEndpoint<IntrospectionEndpoint>(EndpointNames.Introspection, ProtocolRoutePaths.Introspection.EnsureLeadingSlash());
builder.AddEndpoint<TokenRevocationEndpoint>(EndpointNames.Revocation, ProtocolRoutePaths.Revocation.EnsureLeadingSlash());
builder.AddEndpoint<TokenEndpoint>(EndpointNames.Token, ProtocolRoutePaths.Token.EnsureLeadingSlash());
builder.AddEndpoint<UserInfoEndpoint>(EndpointNames.UserInfo, ProtocolRoutePaths.UserInfo.EnsureLeadingSlash());
return builder;
}
- AddPluggableServices - 注入可插拔服务
public static IIdentityServerBuilder AddPluggableServices(this IIdentityServerBuilder builder)
{
builder.Services.TryAddTransient<IPersistedGrantService, DefaultPersistedGrantService>();
builder.Services.TryAddTransient<IKeyMaterialService, DefaultKeyMaterialService>();
builder.Services.TryAddTransient<ITokenService, DefaultTokenService>();
builder.Services.TryAddTransient<ITokenCreationService, DefaultTokenCreationService>();
builder.Services.TryAddTransient<IClaimsService, DefaultClaimsService>();
builder.Services.TryAddTransient<IRefreshTokenService, DefaultRefreshTokenService>();
builder.Services.TryAddTransient<IDeviceFlowCodeService, DefaultDeviceFlowCodeService>();
builder.Services.TryAddTransient<IConsentService, DefaultConsentService>();
builder.Services.TryAddTransient<ICorsPolicyService, DefaultCorsPolicyService>();
builder.Services.TryAddTransient<IProfileService, DefaultProfileService>();
builder.Services.TryAddTransient<IConsentMessageStore, ConsentMessageStore>();
builder.Services.TryAddTransient<IMessageStore<LogoutMessage>, ProtectedDataMessageStore<LogoutMessage>>();
builder.Services.TryAddTransient<IMessageStore<EndSession>, ProtectedDataMessageStore<EndSession>>();
builder.Services.TryAddTransient<IMessageStore<ErrorMessage>, ProtectedDataMessageStore<ErrorMessage>>();
builder.Services.TryAddTransient<IIdentityServerInteractionService, DefaultIdentityServerInteractionService>();
builder.Services.TryAddTransient<IDeviceFlowInteractionService, DefaultDeviceFlowInteractionService>();
builder.Services.TryAddTransient<IAuthorizationCodeStore, DefaultAuthorizationCodeStore>();
builder.Services.TryAddTransient<IRefreshTokenStore, DefaultRefreshTokenStore>();
builder.Services.TryAddTransient<IReferenceTokenStore, DefaultReferenceTokenStore>();
builder.Services.TryAddTransient<IUserConsentStore, DefaultUserConsentStore>();
builder.Services.TryAddTransient<IHandleGenerationService, DefaultHandleGenerationService>();
builder.Services.TryAddTransient<IPersistentGrantSerializer, PersistentGrantSerializer>();
builder.Services.TryAddTransient<IEventService, DefaultEventService>();
builder.Services.TryAddTransient<IEventSink, DefaultEventSink>();
builder.Services.TryAddTransient<IUserCodeService, DefaultUserCodeService>();
builder.Services.TryAddTransient<IUserCodeGenerator, NumericUserCodeGenerator>();
builder.Services.TryAddTransient<IBackChannelLogoutService, DefaultBackChannelLogoutService>();
builder.AddJwtRequestUriHttpClient();
builder.AddBackChannelLogoutHttpClient();
//builder.Services.AddHttpClient<BackChannelLogoutHttpClient>();
//builder.Services.AddHttpClient<JwtRequestUriHttpClient>();
builder.Services.AddTransient<IClientSecretValidator, ClientSecretValidator>();
builder.Services.AddTransient<IApiSecretValidator, ApiSecretValidator>();
builder.Services.TryAddTransient<IDeviceFlowThrottlingService, DistributedDeviceFlowThrottlingService>();
builder.Services.AddDistributedMemoryCache();
return builder;
}
- AddValidators - 注入校验类
public static IIdentityServerBuilder AddValidators(this IIdentityServerBuilder builder)
{
// core
builder.Services.TryAddTransient<IEndSessionRequestValidator, EndSessionRequestValidator>();
builder.Services.TryAddTransient<ITokenRevocationRequestValidator, TokenRevocationRequestValidator>();
builder.Services.TryAddTransient<IAuthorizeRequestValidator, AuthorizeRequestValidator>();
builder.Services.TryAddTransient<ITokenRequestValidator, TokenRequestValidator>();
builder.Services.TryAddTransient<IRedirectUriValidator, StrictRedirectUriValidator>();
builder.Services.TryAddTransient<ITokenValidator, TokenValidator>();
builder.Services.TryAddTransient<IIntrospectionRequestValidator, IntrospectionRequestValidator>();
builder.Services.TryAddTransient<IResourceOwnerPasswordValidator, NotSupportedResourceOwnerPasswordValidator>();
builder.Services.TryAddTransient<ICustomTokenRequestValidator, DefaultCustomTokenRequestValidator>();
builder.Services.TryAddTransient<IUserInfoRequestValidator, UserInfoRequestValidator>();
builder.Services.TryAddTransient<IClientConfigurationValidator, DefaultClientConfigurationValidator>();
builder.Services.TryAddTransient<IDeviceAuthorizationRequestValidator, DeviceAuthorizationRequestValidator>();
builder.Services.TryAddTransient<IDeviceCodeValidator, DeviceCodeValidator>();
// optional
builder.Services.TryAddTransient<ICustomTokenValidator, DefaultCustomTokenValidator>();
builder.Services.TryAddTransient<ICustomAuthorizeRequestValidator, DefaultCustomAuthorizeRequestValidator>();
return builder;
}
- AddResponseGenerators - 注入响应生成类
public static IIdentityServerBuilder AddResponseGenerators(this IIdentityServerBuilder builder)
{
builder.Services.TryAddTransient<ITokenResponseGenerator, TokenResponseGenerator>();
builder.Services.TryAddTransient<IUserInfoResponseGenerator, UserInfoResponseGenerator>();
builder.Services.TryAddTransient<IIntrospectionResponseGenerator, IntrospectionResponseGenerator>();
builder.Services.TryAddTransient<IAuthorizeInteractionResponseGenerator, AuthorizeInteractionResponseGenerator>();
builder.Services.TryAddTransient<IAuthorizeResponseGenerator, AuthorizeResponseGenerator>();
builder.Services.TryAddTransient<IDiscoveryResponseGenerator, DiscoveryResponseGenerator>();
builder.Services.TryAddTransient<ITokenRevocationResponseGenerator, TokenRevocationResponseGenerator>();
builder.Services.TryAddTransient<IDeviceAuthorizationResponseGenerator, DeviceAuthorizationResponseGenerator>();
return builder;
}
- AddDefaultSecretParsers & AddDefaultSecretValidators
/// <summary>
/// Adds the default secret parsers.
/// </summary>
/// <param name="builder">The builder.</param>
/// <returns></returns>
public static IIdentityServerBuilder AddDefaultSecretParsers(this IIdentityServerBuilder builder)
{
builder.Services.AddTransient<ISecretParser, BasicAuthenticationSecretParser>();
builder.Services.AddTransient<ISecretParser, PostBodySecretParser>();
return builder;
}
/// <summary>
/// Adds the default secret validators.
/// </summary>
/// <param name="builder">The builder.</param>
/// <returns></returns>
public static IIdentityServerBuilder AddDefaultSecretValidators(this IIdentityServerBuilder builder)
{
builder.Services.AddTransient<ISecretValidator, HashedSharedSecretValidator>();
return builder;
}
IdentityServerOptions - 配置类
/// <summary>
/// The IdentityServerOptions class is the top level container for all configuration settings of IdentityServer.
/// </summary>
public class IdentityServerOptions
{
/// <summary>
/// Gets or sets the unique name of this server instance, e.g. https://myissuer.com.
/// If not set, the issuer name is inferred from the request
/// </summary>
/// <value>
/// Unique name of this server instance, e.g. https://myissuer.com
/// </value>
public string IssuerUri { get; set; }
/// <summary>
/// Gets or sets the origin of this server instance, e.g. https://myorigin.com.
/// If not set, the origin name is inferred from the request
/// Note: Do not set a URL or include a path.
/// </summary>
/// <value>
/// Origin of this server instance, e.g. https://myorigin.com
/// </value>
public string PublicOrigin { get; set; }
/// <summary>
/// Gets or sets the value for the JWT typ header for access tokens.
/// </summary>
/// <value>
/// The JWT typ value.
/// </value>
public string AccessTokenJwtType { get; set; } = "at+jwt";
/// <summary>
/// Emits an aud claim with the format issuer/resources. That's needed for some older access token validation plumbing. Defaults to false.
/// </summary>
public bool EmitLegacyResourceAudienceClaim { get; set; } = false;
/// <summary>
/// Gets or sets the endpoint configuration.
/// </summary>
/// <value>
/// The endpoints configuration.
/// </value>
public EndpointsOptions Endpoints { get; set; } = new EndpointsOptions();
/// <summary>
/// Gets or sets the discovery endpoint configuration.
/// </summary>
/// <value>
/// The discovery endpoint configuration.
/// </value>
public DiscoveryOptions Discovery { get; set; } = new DiscoveryOptions();
/// <summary>
/// Gets or sets the authentication options.
/// </summary>
/// <value>
/// The authentication options.
/// </value>
public AuthenticationOptions Authentication { get; set; } = new AuthenticationOptions();
/// <summary>
/// Gets or sets the events options.
/// </summary>
/// <value>
/// The events options.
/// </value>
public EventsOptions Events { get; set; } = new EventsOptions();
/// <summary>
/// Gets or sets the max input length restrictions.
/// </summary>
/// <value>
/// The length restrictions.
/// </value>
public InputLengthRestrictions InputLengthRestrictions { get; set; } = new InputLengthRestrictions();
/// <summary>
/// Gets or sets the options for the user interaction.
/// </summary>
/// <value>
/// The user interaction options.
/// </value>
public UserInteractionOptions UserInteraction { get; set; } = new UserInteractionOptions();
/// <summary>
/// Gets or sets the caching options.
/// </summary>
/// <value>
/// The caching options.
/// </value>
public CachingOptions Caching { get; set; } = new CachingOptions();
/// <summary>
/// Gets or sets the cors options.
/// </summary>
/// <value>
/// The cors options.
/// </value>
public CorsOptions Cors { get; set; } = new CorsOptions();
/// <summary>
/// Gets or sets the Content Security Policy options.
/// </summary>
public CspOptions Csp { get; set; } = new CspOptions();
/// <summary>
/// Gets or sets the validation options.
/// </summary>
public ValidationOptions Validation { get; set; } = new ValidationOptions();
/// <summary>
/// Gets or sets the device flow options.
/// </summary>
public DeviceFlowOptions DeviceFlow { get; set; } = new DeviceFlowOptions();
/// <summary>
/// Gets or sets the mutual TLS options.
/// </summary>
public MutualTlsOptions MutualTls { get; set; } = new MutualTlsOptions();
}
UserIdentityServer - 中间件逻辑
- 执行校验
- BaseUrlMiddleware中间件:设置BaseUrl
- 配置CORS跨域:CorsPolicyProvider根据client信息生成动态策略
- IdentityServerMiddlewareOptions默认调用了UseAuthentication,所以如果使用IdentityServer不用重复注册Authentication中间件
- 使用MutualTlsTokenEndpointMiddleware中间件:要求客户端、服务端都使用https,默认不开启
- 使用IdentityServerMiddleware中间件:IEndpointRouter根据请求寻找匹配的IEndpointHandler,如果找到的话则由EndPointHandler处理请求。
public static IApplicationBuilder UseIdentityServer(this IApplicationBuilder app, IdentityServerMiddlewareOptions options = null)
{
app.Validate();
app.UseMiddleware<BaseUrlMiddleware>();
app.ConfigureCors();
// it seems ok if we have UseAuthentication more than once in the pipeline --
// this will just re-run the various callback handlers and the default authN
// handler, which just re-assigns the user on the context. claims transformation
// will run twice, since that's not cached (whereas the authN handler result is)
// related: https://github.com/aspnet/Security/issues/1399
if (options == null) options = new IdentityServerMiddlewareOptions();
options.AuthenticationMiddleware(app);
app.UseMiddleware<MutualTlsTokenEndpointMiddleware>();
app.UseMiddleware<IdentityServerMiddleware>();
return app;
}
核心中间件IdentityServerMiddleware的代码,逻辑比较清晰
- IEndpointRouter路由类旬斋匹配接口
- 匹配接口处理请求返回结果IEndpointResult
- IEndpointResult执行结果,写入上下文,返回报文
public async Task Invoke(HttpContext context, IEndpointRouter router, IUserSession session, IEventService events)
{
// this will check the authentication session and from it emit the check session
// cookie needed from JS-based signout clients.
await session.EnsureSessionIdCookieAsync();
try
{
var endpoint = router.Find(context);
if (endpoint != null)
{
_logger.LogInformation("Invoking IdentityServer endpoint: {endpointType} for {url}", endpoint.GetType().FullName, context.Request.Path.ToString());
var result = await endpoint.ProcessAsync(context);
if (result != null)
{
_logger.LogTrace("Invoking result: {type}", result.GetType().FullName);
await result.ExecuteAsync(context);
}
return;
}
}
catch (Exception ex)
{
await events.RaiseAsync(new UnhandledExceptionEvent(ex));
_logger.LogCritical(ex, "Unhandled exception: {exception}", ex.Message);
throw;
}
await _next(context);
}
看一下路由类的处理逻辑
之前AddDefaultEndpoints注入了所有默认接口,路由类可以通过依赖注入拿到所有接口信息,将请求地址与接口地址对比得到匹配的接口,然后从容器拿到对应的接口处理器。
public EndpointRouter(IEnumerable<Endpoint> endpoints, IdentityServerOptions options, ILogger<EndpointRouter> logger)
{
_endpoints = endpoints;
_options = options;
_logger = logger;
}
public IEndpointHandler Find(HttpContext context)
{
if (context == null) throw new ArgumentNullException(nameof(context));
foreach(var endpoint in _endpoints)
{
var path = endpoint.Path;
if (context.Request.Path.Equals(path, StringComparison.OrdinalIgnoreCase))
{
var endpointName = endpoint.Name;
_logger.LogDebug("Request path {path} matched to endpoint type {endpoint}", context.Request.Path, endpointName);
return GetEndpointHandler(endpoint, context);
}
}
_logger.LogTrace("No endpoint entry found for request path: {path}", context.Request.Path);
return null;
}
private IEndpointHandler GetEndpointHandler(Endpoint endpoint, HttpContext context)
{
if (_options.Endpoints.IsEndpointEnabled(endpoint))
{
var handler = context.RequestServices.GetService(endpoint.Handler) as IEndpointHandler;
if (handler != null)
{
_logger.LogDebug("Endpoint enabled: {endpoint}, successfully created handler: {endpointHandler}", endpoint.Name, endpoint.Handler.FullName);
return handler;
}
else
{
_logger.LogDebug("Endpoint enabled: {endpoint}, failed to create handler: {endpointHandler}", endpoint.Name, endpoint.Handler.FullName);
}
}
else
{
_logger.LogWarning("Endpoint disabled: {endpoint}", endpoint.Name);
}
return null;
}
总结
主干流程大致如图
idsv的代码量还是比较大的,有很多的类,但是代码还是要写的挺规范清晰,梳理下来脉络还是很明了的。
IdentityServer4源码解析_1_项目结构的更多相关文章
- identityserver4源码解析_2_元数据接口
目录 identityserver4源码解析_1_项目结构 identityserver4源码解析_2_元数据接口 identityserver4源码解析_3_认证接口 identityserver4 ...
- identityserver4源码解析_3_认证接口
目录 identityserver4源码解析_1_项目结构 identityserver4源码解析_2_元数据接口 identityserver4源码解析_3_认证接口 identityserver4 ...
- IdentityServer4源码解析_4_令牌发放接口
目录 identityserver4源码解析_1_项目结构 identityserver4源码解析_2_元数据接口 identityserver4源码解析_3_认证接口 identityserver4 ...
- IdentityServer4源码解析_5_查询用户信息接口
协议简析 UserInfo接口是OAuth2.0中规定的需要认证访问的接口,可以返回认证用户的声明信息.请求UserInfo接口需要使用通行令牌.响应报文通常是json数据格式,包含了一组claim键 ...
- AspNetCore3.1_Secutiry源码解析_1_目录
文章目录 AspNetCore3.1_Secutiry源码解析_1_目录 AspNetCore3.1_Secutiry源码解析_2_Authentication_核心项目 AspNetCore3.1_ ...
- Springboot源码分析之项目结构
Springboot源码分析之项目结构 摘要: 无论是从IDEA还是其他的SDS开发工具亦或是https://start.spring.io/ 进行解压,我们都会得到同样的一个pom.xml文件 4. ...
- elementUi源码解析(1)--项目结构篇
因为在忙其他事情好久没有更新iview的源码,也是因为后面的一些组件有点复杂在考虑用什么方式把复杂的功能逻辑简单的展示出来,还没想到方法,突然想到element的组件基本也差不多,内部功能的逻辑也差不 ...
- axios 源码解析(中) 代码结构
axios现在最新的版本的是v0.19.0,本节我们来分析一下它的实现源码,首先通过 gitHub地址获取到它的源代码,地址:https://github.com/axios/axios/tree/v ...
- 黄聪:wordpress源码解析-数据库表结构(转)
如果是一个普通的用户,不需要了解wordpress数据库的结构.但是,如果你正在写一个插件,你应该会对wordpress如何处理它的数据和关系感兴趣.如果你已经尝试使用已经存在的wordpress a ...
随机推荐
- pandas入门(一):pandas的安装和创建
pandas 对于数据分析的人员来说都是必须熟悉的第三方库,pandas 在科学计算上有很大的优势,特别是对于数据分析人员来说,相当的重要.python中有了Numpy ,但是Numpy 还是比较数学 ...
- 自研测试框架ktest介绍(适用于UI和API)
iTesting,爱测试,爱分享 在自动化测试的过程中,测试框架是我们绕不过去的一个工具,无论你是不需要写代码直接改动数据生成脚本,还是你需要检查测试结果甚至持续集成,测试框架都在发挥它的作用. 不同 ...
- 【桌面篇】Archlinux安装kde桌面
ArchLinux安装配置手册[桌面篇] 现在你的U盘可以拔掉了,重启后会发现和刚刚没什么区别,还是命令行的界面,别着急现在就带你安装桌面环境. 连接网络 首先检查一下网络是否连接成功 ping ww ...
- 使用webpack从0搭建多入口网站脚手架,可复用导航栏/底部通栏/侧边栏,根据页面文件自动更改配置,支持ES6/Less
之前只知道webpack很强大,但是一直没有深入学习过,这次从头看了一下教程,然后从0开始搭建了一个多入口网站的开发脚手架,期间遇到过很多问题,所以有心整理一下,希望能给大家一点帮助. 多HTML网站 ...
- C++读入输出优化
读入输出优化虽然对于小数据没有半点作用,但是对于大数据来说,可以优化几十ms. 有时就是那么几十ms,可以被卡掉大数据的点 读入优化 int read() { int x=0,sig=1; char ...
- 《深入理解 Java 虚拟机》读书笔记:虚拟机类加载机制
正文 虚拟机把描述类的数据从 Class 文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的 Java 类型,这就是虚拟机的类加载机制. 一.类加载的时机 1.类的生命 ...
- Python入门的三大问题和三大谎言
Python广告,铺天盖地,小白们雾里看花,Python无限美好.作为会20几种语言(BASIC Foxbase/pro VB VC C C++ c# js typescript HTML Ardui ...
- axios下载文件乱码问题 无法解压 文件损坏
/* 下载附件 */ downloadFile(fileName) { // window.open(url); var that = this; var url = "PO2116&quo ...
- 遍历Map的四种方式(Java)
public static void main(String[] args) { Map<String, String> map = new HashMap<String, Stri ...
- 基于osg的python三维程序开发(一)
背景: osg是一款开源的三维引擎,在过去多年的发展中积累了大量的用户,该引擎基于场景树的管理,使用方法简单.但是对长期使用python作为开发工具的朋友来说, 有一定门槛. 下面的小程序,演示了如何 ...