Skip to main content

moregeek program

spring安全体系结构-多极客编程

Spring安全体系结构_应用程序

本指南是 Spring 安全性的入门书,提供了对框架的设计和基本构建块的见解。我们只介绍应用程序安全性的基础知识。但是,通过这样做,我们可以消除使用 Spring Security 的开发人员遇到的一些困惑。为此,我们来看看通过使用过滤器以及更一般地使用方法注释在 Web 应用程序中应用安全性的方式。当您需要大致了解安全应用程序的工作原理、如何自定义应用程序,或者需要了解如何考虑应用程序安全性时,请使用本指南。

本指南并非旨在作为解决最基本问题的手册或秘诀(还有其他来源),但它可能对初学者和专家都很有用。Spring Boot 也经常被引用,因为它为安全应用程序提供了一些默认行为,并且了解它如何适应整体架构会很有用。

注意

所有原则同样适用于不使用 Spring 引导的应用程序。

身份验证和访问控制

应用程序安全性归结为两个或多或少独立的问题:身份验证(你是谁?)和授权(你可以做什么?)。有时人们会说“访问控制”而不是“授权”,这可能会令人困惑,但这样想可能会有所帮助,因为“授权”在其他地方是超载的。Spring 安全性具有旨在将身份验证与授权分开的体系结构,并具有两者的策略和扩展点。

认证

身份验证的主要策略接口是 ,它只有一个方法:​​AuthenticationManager​

public interface AuthenticationManager {

Authentication authenticate(Authentication authentication)
throws AuthenticationException;
}

A 可以在其方法中执行以下 3 件事之一:​​AuthenticationManager​​​​authenticate()​

  • 如果可以验证输入是否表示有效的主体,则返回 (通常带有)。Authenticationauthenticated=true
  • 如果它认为输入表示无效主体,则抛出 an。AuthenticationException
  • 如果无法决定,则返回。null

​AuthenticationException​​是运行时异常。它通常由应用程序以通用方式处理,具体取决于应用程序的样式或用途。换句话说,用户代码通常不需要捕获和处理它。例如,Web UI 可能会呈现一个指出身份验证失败的页面,后端 HTTP 服务可能会发送 401 响应,根据上下文,有或没有标头。​​WWW-Authenticate​

最常用的实现是 ,它委托给实例链。A 有点像 ,但它有一个额外的方法,允许调用方查询它是否支持给定类型:​​AuthenticationManager​​​​ProviderManager​​​​AuthenticationProvider​​​​AuthenticationProvider​​​​AuthenticationManager​​​​Authentication​

public interface AuthenticationProvider {

Authentication authenticate(Authentication authentication)
throws AuthenticationException;

boolean supports(Class<?> authentication);
}

方法中的参数是真的(它只被问到它是否支持传递给方法的东西)。A 可以通过委派给 .如果 无法识别特定实例类型,则会跳过该实例类型。​​Class<?>​​​​supports()​​​​Class<? extends Authentication>​​​​authenticate()​​​​ProviderManager​​​​AuthenticationProviders​​​​ProviderManager​​​​Authentication​

A 有一个可选的父级,如果所有提供程序都返回 ,它可以咨询该父级。如果父级不可用,则 将生成 .​​ProviderManager​​​​null​​​​null​​​​Authentication​​​​AuthenticationException​

有时,应用程序具有受保护资源的逻辑组(例如,与路径模式匹配的所有 Web 资源,例如 ),并且每个组都可以有自己的专用 .通常,它们中的每一个都是一个 ,并且它们共享一个父项。然后,父资源是一种“全局”资源,充当所有提供程序的后备。​​/api/**​​​​AuthenticationManager​​​​ProviderManager​

Spring安全体系结构_身份验证_02

图1.使用​​AuthenticationManager​​​​ProviderManager​

定制身份验证管理器

Spring 安全性提供了一些配置帮助程序,以快速获取应用程序中设置的常见身份验证管理器功能。最常用的帮助程序是 ,它非常适合设置内存中、JDBC 或 LDAP 用户详细信息或添加自定义 。以下示例显示配置全局(父级)的应用程序:​​AuthenticationManagerBuilder​​​​UserDetailsService​​​​AuthenticationManager​

@Configuration
public class ApplicationSecurity extends WebSecurityConfigurerAdapter {

... // web stuff here

@Autowired
public void initialize(AuthenticationManagerBuilder builder, DataSource dataSource) {
builder.jdbcAuthentication().dataSource(dataSource).withUser("dave")
.password("secret").roles("USER");
}

}

此示例与 Web 应用程序相关,但 的用法适用范围更广(请参阅​​AuthenticationManagerBuilder​​网络安全有关如何实现 Web 应用程序安全性的更多详细信息)。请注意,is into a 中的一个方法 — 这就是它构建全局(父级)的原因。相反,请考虑以下示例:​​AuthenticationManagerBuilder​​​​@Autowired​​​​@Bean​​​​AuthenticationManager​

@Configuration
public class ApplicationSecurity extends WebSecurityConfigurerAdapter {

@Autowired
DataSource dataSource;

... // web stuff here

@Override
public void configure(AuthenticationManagerBuilder builder) {
builder.jdbcAuthentication().dataSource(dataSource).withUser("dave")
.password("secret").roles("USER");
}

}

如果我们在配置器中使用了 of 方法,则 将仅用于构建一个 “本地” ,这将是全局的子项。在 Spring Boot 应用程序中,您可以将全局 bean 放入另一个 bean,但除非你自己显式公开它,否则您无法对本地 bean 执行此操作。​​@Override​​​​AuthenticationManagerBuilder​​​​AuthenticationManager​​​​@Autowired​

Spring 引导提供了一个默认的全局(只有一个用户),除非你通过提供自己的 bean 来抢占它。默认值本身就足够安全,您不必担心它,除非您主动需要自定义全局。如果执行任何构建 的配置,通常可以对要保护的资源进行本地配置,而不必担心全局默认值。​​AuthenticationManager​​​​AuthenticationManager​​​​AuthenticationManager​​​​AuthenticationManager​

授权或访问控制

身份验证成功后,我们可以继续进行授权,这里的核心策略是 .框架提供了三个实现,并且所有三个都委托给实例链,有点像委托给。​​AccessDecisionManager​​​​AccessDecisionVoter​​​​ProviderManager​​​​AuthenticationProviders​

一个考虑一个(代表一个委托人)和一个安全,它被装饰为:​​AccessDecisionVoter​​​​Authentication​​​​Object​​​​ConfigAttributes​

boolean supports(ConfigAttribute attribute);

boolean supports(Class<?> clazz);

int vote(Authentication authentication, S object,
Collection<ConfigAttribute> attributes);

在 和 的签名中是完全通用的。它表示用户可能想要访问的任何内容(Web 资源或 Java 类中的方法两种最常见的情况)。它们也是相当通用的,代表安全的安全装饰,其中包含一些元数据,这些元数据决定了访问它所需的权限级别。 是一个接口。它只有一个方法(非常通用并返回一个),因此这些字符串以某种方式编码资源所有者的意图,表达有关允许谁访问它的规则。典型的是用户角色的名称(如或),它们通常具有特殊格式(如前缀)或表示需要计算的表达式。​​Object​​​​AccessDecisionManager​​​​AccessDecisionVoter​​​​ConfigAttributes​​​​Object​​​​ConfigAttribute​​​​String​​​​ConfigAttribute​​​​ROLE_ADMIN​​​​ROLE_AUDIT​​​​ROLE_​

大多数人使用默认值,即(如果任何选民返回肯定,则授予访问权限)。任何自定义都倾向于在选民中发生,要么通过添加新的,要么修改现有自定义的工作方式。​​AccessDecisionManager​​​​AffirmativeBased​

使用 Spring 表达式语言 (SpEL) 表达式是很常见的 — 例如,.这可以由可以处理表达式并为它们创建上下文的 支持。要扩展可处理的表达式范围,需要自定义实现 ,有时还需要 。​​ConfigAttributes​​​​isFullyAuthenticated() && hasRole('user')​​​​AccessDecisionVoter​​​​SecurityExpressionRoot​​​​SecurityExpressionHandler​

网络安全

Web层中的Spring Security(对于UI和HTTP后端)是基于Servlet的,因此首先了解其角色是有帮助的。下图显示了单个 HTTP 请求的处理程序的典型分层。​​Filters​​​​Filters​

Spring安全体系结构_身份验证_03

客户端向应用程序发送请求,容器根据请求 URI 的路径决定哪些过滤器和哪个 servlet 应用于它。一个 Servlet 最多可以处理单个请求,但过滤器形成一个链,因此它们是有序的。事实上,如果过滤器想要处理请求本身,它可以否决链的其余部分。过滤器还可以修改下游过滤器和 servlet 中使用的请求或响应。过滤器链的顺序非常重要,Spring Boot 通过两种机制对其进行管理:类型可以有一个 or 实现,它们可以是 a 的一部分,它本身有一个顺序作为其 API 的一部分。一些现成的过滤器定义了自己的常量,以帮助表明它们喜欢的相对顺序(例如,来自Spring Session有一个of,它告诉我们它喜欢在链的早期,但它不排除其他过滤器在它之前)。​​@Beans​​​​Filter​​​​@Order​​​​Ordered​​​​FilterRegistrationBean​​​​SessionRepositoryFilter​​​​DEFAULT_ORDER​​​​Integer.MIN_VALUE + 50​

Spring Security 作为链中的一个单一安装,其具体类型是 ,原因我们很快就会介绍。在 Spring 引导应用程序中,安全过滤器是 中的 ,默认情况下会安装它,以便将其应用于每个请求。它安装在由 定义的位置,而 又由 锚定(Spring 引导应用程序期望过滤器包装请求时具有的最大顺序,修改其行为)。不过,还有更多的事情要做:从容器的角度来看,Spring Security 是一个单独的过滤器,但是,在它里面,还有额外的过滤器,每个过滤器都扮演着特殊的角色。下图显示了此关系:​​Filter​​​​FilterChainProxy​​​​@Bean​​​​ApplicationContext​​​​SecurityProperties.DEFAULT_FILTER_ORDER​​​​FilterRegistrationBean.REQUEST_WRAPPER_FILTER_MAX_ORDER​

Spring安全体系结构_ide_04

图2.Spring 安全性是单个物理的,但将处理委托给内部过滤器链​​Filter​

事实上,在安全过滤器中甚至还有一层间接性:它通常作为 ,不一定是 Spring 。代理委托给 ,它始终为 ,通常具有固定的名称 。它包含内部排列为过滤器链(或多个链)的所有安全逻辑。所有过滤器都有相同的API(它们都实现了Servlet规范中的接口),并且它们都有机会否决链的其余部分。​​DelegatingFilterProxy​​​​@Bean​​​​FilterChainProxy​​​​@Bean​​​​springSecurityFilterChain​​​​FilterChainProxy​​​​Filter​

可以有多个过滤器链,所有过滤器链都由 Spring Security 在同一顶层进行管理,并且容器都不知道所有这些过滤器链。Spring 安全性过滤器包含一个过滤器链列表,并将请求调度到与其匹配的第一个链。下图显示了基于匹配请求路径(之前匹配)发生的调度。这很常见,但不是匹配请求的唯一方法。此调度过程最重要的特征是只有一个链处理请求。​​FilterChainProxy​​​​/foo/**​​​​/**​

Spring安全体系结构_身份验证_05

图3.Spring 安全性将请求分派给匹配的第一个链。​​FilterChainProxy​

没有自定义安全配置的 vanilla Spring Boot 应用程序有几个(称为 n)过滤器链,其中通常 n=6。第一个 (n-1) 链只是为了忽略静态资源模式,如 和 ,以及错误视图:。(路径可以由用户从配置 Bean 控制。最后一个链与 catch-all 路径 () 匹配,并且更加活跃,包含用于身份验证、授权、异常处理、会话处理、标头写入等的逻辑。默认情况下,此链中总共有 11 个过滤器,但通常用户无需关心使用哪些过滤器以及何时使用。​​/css/**​​​​/images/**​​​​/error​​​​security.ignored​​​​SecurityProperties​​​​/**​

注意

Spring 安全性内部的所有过滤器对于容器都是未知的这一事实很重要,尤其是在 Spring 引导应用程序中,默认情况下,所有类型都自动注册到容器中。因此,如果要将自定义筛选器添加到安全链,则需要不将其设置为或将其包装在显式禁用容器注册的筛选器中。​​@Beans​​​​Filter​​​​@Bean​​​​FilterRegistrationBean​

创建和自定义筛选器链

Spring 引导应用程序(带有请求匹配器的应用程序)中的默认回退过滤器链具有预定义的顺序 .您可以通过设置 来完全关闭它,也可以将其用作回退并以较低的顺序定义其他规则。要执行后者,请添加 a of 类型(或)并使用 修饰类,如下所示:​​/**​​​​SecurityProperties.BASIC_AUTH_ORDER​​​​security.basic.enabled=false​​​​@Bean​​​​WebSecurityConfigurerAdapter​​​​WebSecurityConfigurer​​​​@Order​

@Configuration
@Order(SecurityProperties.BASIC_AUTH_ORDER - 10)
public class ApplicationConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/match1/**")
...;
}
}

此 Bean 会导致 Spring Security 添加新的过滤器链,并在回退之前对其进行排序。

与另一组资源相比,许多应用程序对一组资源具有完全不同的访问规则。例如,托管 UI 和后备 API 的应用程序可能支持基于 Cookie 的身份验证(重定向到 UI 部分的登录页面)和基于令牌的身份验证(对 API 部件的未经身份验证的请求发出 401 响应)。每组资源都有自己的唯一顺序和自己的请求匹配器。如果匹配规则重叠,则最早的有序筛选器链优先。​​WebSecurityConfigurerAdapter​

请求匹配调度和授权

安全筛选器链(或等效的 )具有请求匹配器,用于决定是否将其应用于 HTTP 请求。一旦决定应用特定的筛选器链,就不会应用其他筛选器链。但是,在筛选器链中,可以通过在配置器中设置其他匹配器来更精细地控制授权,如下所示:​​WebSecurityConfigurerAdapter​​​​HttpSecurity​

@Configuration
@Order(SecurityProperties.BASIC_AUTH_ORDER - 10)
public class ApplicationConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/match1/**")
.authorizeRequests()
.antMatchers("/match1/user").hasRole("USER")
.antMatchers("/match1/spam").hasRole("SPAM")
.anyRequest().isAuthenticated();
}
}

配置 Spring 安全性时最容易犯的错误之一是忘记了这些匹配器适用于不同的进程。一个是整个筛选器链的请求匹配器,另一个是仅选择要应用的访问规则。

将应用程序安全规则与执行器规则相结合

如果将 Spring 引导执行器用于管理端点,您可能希望它们是安全的,默认情况下,它们是安全的。事实上,一旦将执行器添加到安全应用程序,就会获得一个仅适用于执行器端点的额外筛选器链。它使用仅匹配执行器端点的请求匹配器定义,其顺序为 ,比默认回退筛选器少 5,因此在回退之前会咨询它。​​ManagementServerProperties.BASIC_AUTH_ORDER​​​​SecurityProperties​

如果您希望应用程序安全规则应用于执行器端点,则可以添加一个筛选器链,该筛选器链的排序早于执行器,并且具有包含所有执行器终结点的请求匹配器。如果您更喜欢执行器端点的默认安全设置,最简单的方法是在执行器之后添加您自己的过滤器,但早于回退(例如,),如下所示:​​ManagementServerProperties.BASIC_AUTH_ORDER + 1​

@Configuration
@Order(ManagementServerProperties.BASIC_AUTH_ORDER + 1)
public class ApplicationConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/foo/**")
...;
}
}

注意

Web 层中的 Spring 安全性目前与 Servlet API 相关联,因此它仅在 Servlet 容器中运行应用程序时才真正适用,无论是嵌入式还是其他容器。然而,它不依赖于Spring MVC或Spring Web堆栈的其余部分,因此它可以用于任何servlet应用程序 - 例如,使用JAX-RS的应用程序。

方法安全性

除了支持保护Web应用程序外,Spring 安全性还支持将访问规则应用于Java方法执行。对于 Spring 安全性,这只是一种不同类型的“受保护资源”。对于用户,这意味着访问规则是使用相同的字符串格式(例如,角色或表达式)声明的,但在代码中的不同位置。第一步是启用方法安全性 — 例如,在我们的应用程序的顶级配置中:​​ConfigAttribute​

@SpringBootApplication
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SampleSecureApplication {
}

然后我们可以直接装饰方法资源:

@Service
public class MyService {

@Secured("ROLE_USER")
public String secure() {
return "Hello Security";
}

}

此示例是具有安全方法的服务。如果 Spring 创建了这种类型的,它将被代理,调用者必须在实际执行该方法之前通过安全拦截器。如果访问被拒绝,调用方将获得 而不是实际方法结果。​​@Bean​​​​AccessDeniedException​

还可以在方法上使用其他批注来强制实施安全约束,特别是 和 ,它们允许您编写分别包含对方法参数和返回值的引用的表达式。​​@PreAuthorize​​​​@PostAuthorize​

提示

将 Web 安全性和方法安全性结合起来的情况并不少见。筛选器链提供用户体验功能,例如身份验证和重定向到登录页等,方法安全性在更精细的级别提供保护。

使用线程

Spring 安全性基本上是线程绑定的,因为它需要使当前经过身份验证的主体可供各种下游使用者使用。基本构建块是 ,它可能包含一个(当用户登录时,它是一个显式的)。您始终可以在 中访问和操作 中的静态便利方法,而这些方法又可以操作 .以下示例显示了这样的安排:​​SecurityContext​​​​Authentication​​​​Authentication​​​​authenticated​​​​SecurityContext​​​​SecurityContextHolder​​​​ThreadLocal​

SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
assert(authentication.isAuthenticated);

用户应用程序代码这样做并不常见,但是例如,如果您需要编写自定义身份验证过滤器,则此功能很有用(尽管即使这样,Spring 安全性中仍有可以使用基类,以便避免需要使用 )。​​SecurityContextHolder​

如果需要访问 Web 端点中当前经过身份验证的用户,可以在 中使用方法参数,如下所示:​​@RequestMapping​

@RequestMapping("/foo")
public String foo(@AuthenticationPrincipal User user) {
... // do stuff with user
}

此注释将电流从 中拉出并在其上调用方法以生成方法参数。in 的类型取决于用于验证身份验证的 ,因此这可能是获取对用户数据的类型安全引用的有用小技巧。​​Authentication​​​​SecurityContext​​​​getPrincipal()​​​​Principal​​​​Authentication​​​​AuthenticationManager​

如果正在使用 Spring 安全性,则 from 是 类型,因此您也可以直接使用它:​​Principal​​​​HttpServletRequest​​​​Authentication​

@RequestMapping("/foo")
public String foo(Principal principal) {
Authentication authentication = (Authentication) principal;
User = (User) authentication.getPrincipal();
... // do stuff with user
}

如果您需要编写在不使用 Spring 安全性时有效的代码,这有时很有用(您需要在加载类时更加防御)。​​Authentication​

异步处理安全方法

由于 是线程绑定的,如果要执行任何调用安全方法的后台处理(例如,使用 ),则需要确保上下文已传播。这归结为包装在后台执行的任务(、等)。Spring 安全性提供了一些帮助程序来简化此操作,例如 和 的包装器。若要传播 to 方法,需要提供并确保 是 正确的类型:​​SecurityContext​​​​@Async​​​​SecurityContext​​​​Runnable​​​​Callable​​​​Runnable​​​​Callable​​​​SecurityContext​​​​@Async​​​​AsyncConfigurer​​​​Executor​

@Configuration
public class ApplicationConfiguration extends AsyncConfigurerSupport {

@Override
public Executor getAsyncExecutor() {
return new DelegatingSecurityContextExecutorService(Executors.newFixedThreadPool(5));
}

}

©著作权归作者所有:来自51CTO博客作者Spring_Learnin的原创作品,请联系作者获取转载授权,否则将追究法律责任

spring boot docker 应用程序-多极客编程

许多人使用容器来包装他们的 Spring Boot 应用程序,构建容器并不是一件简单的事情。这是针对 Spring Boot 应用程序开发人员的指南,容器并不总是开发人员的良好抽象。它们迫使您了解和思考低层次的问题。但是,有时可能会要求您创建或使用容器,因此了解构建基块是值得的。在本指南中,我们旨在向您展示在面临需要创建自己的容器的前景时可以做出的一些选择。我们假设您知道如何创建和构建基本的 Sp

kubernetes 上的spring-多极客编程

在构建在云中运行的 Java 应用程序时,弹簧和弹簧靴显然是最受欢迎的.同样越来越明显的是,Docker和Kubernetes等技术在春季社区中发挥重要作用.将 Spring Boot 应用程序打包在 Docker 容器中将该应用程序部署到 Kubernetes 已经有一段时间了,而且花费很少的努力。由于“让 jar 而不是战争”的座右铭,容器化 Spring Boot 应用程序所需要的只是一个带

使用 spring 构建 rest 服务-多极客编程

REST 已迅速成为在 Web 上构建 Web 服务的事实标准,因为它们易于构建和使用。关于 REST 如何适应微服务领域,还有更大的讨论,但是在本教程中,让我们只看一下构建 RESTful 服务。为什么休息?REST 包含 Web 的准则,包括其架构、优势和其他一切。这并不奇怪,因为它的作者罗伊·菲尔丁(Roy Fielding)参与了大约十几个规范,这些规范控制着网络的运作方式。有什么好处?W

#yyds干货盘点# leetcode程序员面试金典:插入-多极客编程

题目:给定两个整型数字 N​ 与 M​,以及表示比特位置的 i​ 与 j(i <= j,且从 0 位开始计算)。编写一种方法,使 M​ 对应的二进制数字插入 N​ 对应的二进制数字的第 i ~ j​ 位区域,不足之处用 0 补齐。具体插入过程如图所示。题目保证从 i​ 位到 j​ 位足以容纳 M​, 例如: M = 10011​,则 i~j 区域至少可容纳 5 位。 示例1:输入:N = 1

#yyds干货盘点# leetcode程序员面试金典:求和路径-多极客编程

题目:给定一棵二叉树,其中每个节点都含有一个整数数值(该值或正或负)。设计一个算法,打印节点数值总和等于某个给定值的所有路径的数量。注意,路径不一定非得从二叉树的根节点或叶节点开始或结束,但是其方向必须向下(只能从父节点指向子节点方向)。示例:给定如下二叉树,以及目标和 ​sum = 22,5 / \ 4 8 / / \

详解java线程问题诊断工具thread dump-多极客编程

摘要:Thread Dump是非常有用的诊断Java应用问题的工具。本文分享自华为云社区《​​调试排错 - Java 线程分析之线程Dump分析​​》,作者:龙哥手记。Thread Dump是非常有用的诊断Java应用问题的工具。每一个Java虚拟机都有及时生成所有线程在某一点状态的thread-dump的能力,虽然各个 Java虚拟机打印的thread dump略有不同,但是 大多都提供了当前活

spring boot docker 应用程序-多极客编程

许多人使用容器来包装他们的 Spring Boot 应用程序,构建容器并不是一件简单的事情。这是针对 Spring Boot 应用程序开发人员的指南,容器并不总是开发人员的良好抽象。它们迫使您了解和思考低层次的问题。但是,有时可能会要求您创建或使用容器,因此了解构建基块是值得的。在本指南中,我们旨在向您展示在面临需要创建自己的容器的前景时可以做出的一些选择。我们假设您知道如何创建和构建基本的 Sp

kubernetes 上的spring-多极客编程

在构建在云中运行的 Java 应用程序时,弹簧和弹簧靴显然是最受欢迎的.同样越来越明显的是,Docker和Kubernetes等技术在春季社区中发挥重要作用.将 Spring Boot 应用程序打包在 Docker 容器中将该应用程序部署到 Kubernetes 已经有一段时间了,而且花费很少的努力。由于“让 jar 而不是战争”的座右铭,容器化 Spring Boot 应用程序所需要的只是一个带

使用 spring 构建 rest 服务-多极客编程

REST 已迅速成为在 Web 上构建 Web 服务的事实标准,因为它们易于构建和使用。关于 REST 如何适应微服务领域,还有更大的讨论,但是在本教程中,让我们只看一下构建 RESTful 服务。为什么休息?REST 包含 Web 的准则,包括其架构、优势和其他一切。这并不奇怪,因为它的作者罗伊·菲尔丁(Roy Fielding)参与了大约十几个规范,这些规范控制着网络的运作方式。有什么好处?W

【c语言】memcpy() 内存拷贝不重叠-多极客编程

前言本篇博客就来介绍下关于C语言常用的内存函数之memcpy()函数。 🎆memcpy() - 内存拷贝不重叠🎇memcpy()指定头文件是:#include<string.h>memcpy() 函数的声明方式如下 👇 int *memcpy(void *str1, const void *str2, size_t n)参数讲解:↓str1 → 指向用于存储复制内容的目标数组,类型强制

【c语言】calloc()、realloc()-多极客编程

👏calloc()calloc()函数的声明,如下所示↓void* calloc (size_t num, size_t size);为num元素数组分配一块内存,每个元素大小为字节长,并将其所有位初始化为零。有效的结果是分配一个(num*size)字节的零初始化内存块。如果size为0,则返回值取决于特定的库实现(它可能是一个空指针,也可能不是),但返回的指针不应被解引用。 参数num → 要分

c++概念之explicit,static成员,友元,内部类,匿名对象,拷贝对象的编译器优化(7千字长文详解!)-多极客编程

c++详解之explicit,static成员,友元,内部类,匿名对象,拷贝对象的编译器优化 关于对象的隐性类型转换 类型转换 我们知道当我们的将一个内置类型的变量强制赋值个另一个内置类型就会发生类型转换,可以显性的也可以隐形的 int main() { int a = 1; double b = 1.1111; a = b; return 0; } 这样样子就发生了一次隐形的类型转换