java 接口 安全加密_Java中的安全加密

java 接口 安全加密

上一次我写关于密码学的文章时 ,我概述了Apache Shiro加密API,并展示了如何使用其两个对称密码。 我还写道:“您不需要在应用程序中加密和解密敏感数据的更多功能。”

我了解了更多有关密码的知识,发现您需要了解更多。 我写的内容在一定程度上是正确的,但是除非您小心谨慎,否则敏感数据可能并非对所有攻击者都是安全的。

开箱即用Shiro提供了Blowfish-CBC和AES-CBC加密方法,我建议使用它们。 两者都旨在防止被动窃听攻击者并且擅长于此。 不幸的是,真正的攻击者更为复杂,可能会破坏基于他们的系统。

注意“可能”。 攻击者只有在被攻击系统至少与他配合时才能成功。 如果要使用这些密码,则必须知道如何安全地编写系统。 当然,另一种选择是使用更强的密码并完全避免该问题。

第一章中很少解释所需的理论术语和概念 。 第二章介绍了如何以更安全的方式加密数据。 然后,我们描述了Blowfish-CBC和AES-CBC的工作方式,并展示了对它们的两种可能的攻击 。 最后,该帖子的末尾包含对其他资源的引用

理论

我们从一些理论概念开始。 如果您不想阅读它,请直接转到包含解决方案的章节。

首先要描述的重要事情是被动攻击者和主动攻击者之间的区别。 然后,我们解释什么是分组密码以及什么是经过身份验证的加密。 最后两个子章节列出了选定的易受攻击的密码和选定的安全密码。

主动与被动攻击者

仅窃听攻击者通常是被动的。 他能够读取加密的通信,但无法对其进行修改或将新的密文发送给通信应用程序。 他无法影响交流,他只能听。 他的被动性只有一个例外:攻击者能够将未加密的信息提供给通信方之一,并获得具有该确切信息的密文。

现实世界中的攻击者通常更活跃。 他们编写自己的消息,并将其发送到通信应用程序。 然后,应用程序假定这些消息已加密,因此尝试对其解密。 攻击者观察其React(返回的错误代码,需要的响应时间等),并进一步了解密码。

如果幸运的话,他可能会使用获得的知识来破解密码或植入虚假信息。

分组密码

分组密码只能加密短消息。 例如,AES只能加密16个字节长的消息,而Blowfish只能加密8个字节长的消息。

较长的消息分为多个块。 每个块与先前加密的块组合,然后传递到块密码。 块组合称为操作模式,并且有多种安全方式来实现。

本文讨论的两种主动攻击是对CBC操作模式的攻击。 分组密码本身是安全的。

认证加密

经过身份验证的加密将任何修改后的密文视为无效。 无法获取加密的数据,对其进行修改并以有效的密文结尾。 此属性也称为密文完整性。

密码首先检查完整性,然后以相同方式拒绝所有已修改的消息。 由于攻击者无法通过完整性检查,因此他从向应用程序发送新消息中得不到任何好处。

身份验证和密文完整性通常(但不总是)由操作模式提供。

弱势密码

任何不提供密文完整性或未通过身份验证的加密算法都可能容易受到某些主动攻击。 使用哪个加密库都没有关系。 加密算法是在标准中定义的,并且与所使用的库无关,其含义相同。

换句话说,如果可以在不知道密钥的情况下创建有效的密文,则即使不知道,也可能存在一些主动攻击。

这篇文章介绍了两种攻击CBC的操作模式。 了解了这些攻击以及被动攻击者和主动攻击者之间的区别之后,您应该能够对CFB,CTR,OFB或其他未经身份验证的密码模式提出类似的攻击。

安全密码

经过身份验证的加密可以防止主动攻击者的攻击。 还提供身份验证的最常见操作模式是:

  • GCM
  • EAX
  • CCM

用这些模式之一(例如AES-EAX)替换CBC,您将拥有一个针对活动攻击者的安全密码。

当然,针对主动攻击者的安全并不意味着该密码没有其他实际限制。 例如,大多数密码只有在用户加密了太多数据后才更改密钥时才是安全的。 如果您对数据加密很认真,则应该研究并了解这些限制。

使用Shiro进行身份验证的加密

本章介绍如何在Java中使用经过身份验证的加密。 对于那些不遵守该理论的人 ,认证加密可以防止数据篡改。 没有密钥的任何人都无法修改加密的消息,并且不可能对这种密码进行主动攻击。

Apache Shiro没有实现自己的加密算法。 它将所有工作委托给Java密码学扩展(JCE),每个Java运行时中都可用。 Shiro“仅”提供易于使用的API和安全的默认值。

因此,我们必须做两件事:

  • 将经过身份验证的操作模式安装到Java密码扩展中,
  • 将Shiro与新的经过验证的操作模式集成。

安装经过身份验证的操作模式

Java密码术扩展是可扩展的API。 所有类和算法都是由提供程序创建的,新提供程序可以随时安装到系统中。 提供者可以是:

  • 安装到Java运行时中,并且可用于所有Java应用程序,
  • 与应用程序一起分发和初始化。

我们将展示如何将Bouncy Castle添加到项目中。 Bouncy Castle是根据MIT许可证分发的提供商,并且包含EAX和GCM操作模式。

Bouncy Castle分发了多个jar文件,每个文件针对不同的Java版本进行了优化。 由于我们的演示项目使用Java 6,因此我们必须使用bcprov-jdk16库。 将maven依赖项添加到pom.xml中:

<dependency><groupId>org.bouncycastle</groupId><artifactId>bcprov-jdk16</artifactId><version>1.46</version>
</dependency>

库存在后,您必须将其提供程序安装到java安全系统中。 在应用程序启动时运行以下方法:

private BouncyCastleProvider installBouncyCastle() {BouncyCastleProvider provider = new BouncyCastleProvider();Security.addProvider(provider);return provider;
}

现在已安装Bouncy Castle,并且两种验证操作模式都可用。

与Shiro集成

Shiro加密软件包基本上是JCE上易于使用的外观。 它将JCE对象配置为使用安全默认值,并为其添加线程安全性。

扩展DefaultBlockCipherService类以利用这些功能。 大多数配置都由该类完成,但是我们仍然必须指定三件事:

  • 分组密码名称和参数,
  • 操作模式,
  • 填充。

块密码名称在构造函数参数中指定。 我们将使用AES,因为它不需要其他配置。 GCM和CCM都不需要填充,因此我们必须指定填充NONE

新密码服务:

class GCMCipherService extends DefaultBlockCipherService {private static final String ALGORITHM_NAME = "AES";public GCMCipherService() {super(ALGORITHM_NAME);setMode(OperationMode.GCM);setPaddingScheme(PaddingScheme.NONE);}}

这就对了。 您可以将新的身份验证密码用作任何其他Shiro密码服务 。

测试用例

我们创建了一个简单的测试用例,以证明完整性检查有效。 它加密一条消息并更改其密文的第三个字节。 如果密码提供了身份验证,则尝试解密修改后的密文会导致运行时异常:

@Test
public void testGCMAuthentication() {String message = "secret message";GCMCipherService gcmCipher = new GCMCipherService();assertIngetrityCheck(message, gcmCipher);
}private void assertIngetrityCheck(String message, DefaultBlockCipherService cipher) {byte[] key = cipher.generateNewKey().getEncoded();byte[] messageBytes = CodecSupport.toBytes(message);ByteSource encrypt = cipher.encrypt(messageBytes, key);// change the ciphertextencrypt.getBytes()[3] = 0;try {// it should be impossible to decrypt changed ciphertextcipher.decrypt(encrypt.getBytes(), key).getBytes();} catch (Exception ex) {return;}fail("It should not be possible to decrypt changed ciphertext.");
}

关于Java 7的注意事项

根据文档 ,Java 7支持两种经过身份验证的操作模式:CCM和GCM。 从理论上讲,您不需要第三方加密提供程序。

不幸的是,Oracle无法在第一个JDK 7版本中提供这些模式的完整实现。 他们想在更新版本中添加它 ,因此情况将来可能会改变。 Oracle错误数据库包含两个相关的 错误 。

Java 1.7.0_01仍然没有它们。

密码块链接(CBC)

在描述承诺的攻击之前,我们需要做的最后一件事是密码块链接(CBC)操作模式。 这种操作模式足以抵御被动攻击者,相当快速且易于实施。

不幸的是,它也容易受到主动攻击。

基本

CBC用于使用分组密码对长消息进行加密。 分组密码只能加密短数据块,因此它首先将消息拆分为短数据块。

第一个和最后一个块是特殊情况。 我们将在以下子章节中说明如何处理它们。 现在,假设消息开头已经被加密并且其第i个块m i与密文c i相对应。

分两个步骤对下一个消息块进行加密:

  • 用上一个块的密文对该块进行异或(例如, m i ?c i-1 ),
  • 用分组密码(例如Blowfish(key, m i ?c i-1 ) )对结果进行加密。

示例:假设该秘密消息有三个块,我们正在尝试使用Blowfish-CBC对其进行加密。 第一块已经被加密,其密文为1, 2, 3, 4, 5, 6, 7, 8 。 第二个块是字节数组1, 0, 1, 0, 1, 0, 1, 0

步骤1:将第一个块密文与第二个块异或:

1, 2, 3, 4, 5, 6, 7, 8 ? 1, 0, 1, 0, 1, 0, 1, 0 = 0, 2, 2, 4, 4, 6, 6, 8

步骤2:使用河豚算法对结果进行加密:

Blowfish(secret_key, {0, 2, 2, 4, 4, 6, 6, 8})

第一块–初始化向量

第一个块没有要合并的上一个块。 因此,我们将生成一个称为初始化向量的随机数据块。 初始化向量用作数据的第一块。 它与第一个消息块进行异或运算,并使用块密码对结果进行加密。

初始化向量作为密文的第一个块未经加密地发送。 如果没有密文,收件人将无法解密密文,也没有理由对其保密。

示例:假设该秘密消息有三个块,我们正在尝试使用Blowfish-CBC对其进行加密。 第一个块是一个字节数组1, 1, 1, 1, 1, 1, 1, 1

步骤1:产生随机初始化向量:

1, 8, 2, 7, 3, 6, 4, 5

步骤2:将第一个块与初始化向量进行异或:

1, 8, 2, 7, 3, 6, 4, 5 ? 1, 1, 1, 1, 1, 1, 1, 1 = 0, 9, 3, 6, 2, 7, 5, 4

步骤3:使用河豚算法对结果进行加密:

Blowfish(secret_key, {0, 9, 3, 6, 2, 7, 5, 4})

步骤4:结合初始化向量和密文。 如果上一步中的Blowfish函数结果为1, 2, 3, 4, 5, 6, 7, 8 ,则密文为:

1, 8, 2, 7, 3, 6, 4, 5, 1, 2, 3, 4, 5, 6, 7, 8


最后一块–填充

分组密码能够对固定长度的消息进行加密,而最后一个分组通常要短一些。 由于密码无法对其进行加密,因此我们需要一种在消息末尾添加其他字节的方法。

Shiro默认使用PKCS#5填充。 每个字节等于填充的长度:

  • 如果最后一块太短,请计算丢失的字节数,并用该数字填充丢失的字节。
  • 如果最后一个块的大小正确,则将其视为丢失整个块的消息。 向其添加一个新的填充块。 每个字节都等于块大小。

示例1:假设该秘密消息有三个块,我们正在尝试使用Blowfish-CBC对其进行加密。 最后的块是字节数组1, 1, 1, 1 。 填充块:

1, 1, 1, 1, 4, 4, 4, 4

示例2:假设该秘密消息有三个块,我们正在尝试使用Blowfish-CBC对其进行加密。 最后一块是字节数组8, 7, 6, 5, 4, 3, 2, 1 。 最后一块和填充:

8, 7, 6, 5, 4, 3, 2, 1, 8, 8, 8, 8, 8, 8, 8, 8

进攻

最后,我们准备展示对基于CBC的密码的两种不同攻击,并证明问题是真实的。 我们的样本项目包含针对AES-CBC和Blowfish-CBC密码的两种攻击的测试案例。

第一部分说明如何将加密文本的开头更改为我们选择的任何消息。 第二小节说明了如何解密密文。

数据篡改

仅当攻击者已经知道加密消息的内容时 ,才可能进行数据篡改攻击。 这样的攻击者可以将第一个消息块更改为他希望的任何内容。

特别是可以:

  • 更改AES-CBC加密消息的前16个字节,
  • 更改Blowfish-CBC加密邮件的前8个字节。

潜在危险

这种攻击是否危险,在很大程度上取决于情况。

如果您使用密码通过网络发送密码,则数据篡改并不是那么危险。 最坏的情况是,合法用户的登录将被拒绝。 同样,如果您的加密数据存储在某些只读存储中,则您不必担心数据被篡改。

但是,如果要通过网络发送银行订单,则数据篡改是真正的威胁。 如果有人将消息Pay Mark 100$更改为Pay Tom 9999$ ,则Tom会得到9999 $,而他不应该得到。

攻击

加密的消息分为三个部分:

  • 随机初始向量
  • 第一组密文,
  • 消息的其余部分。

接收者使用块密码解密第一组密文。 这给了他message block?initial vector 。 为了获得消息,他必须将此值与初始向量进行异或:

message block ? initial vector ? initial vector = message block

初始向量与消息一起传输,主动攻击者可以更改它。 如果攻击者用another iv替换了原始初始向量,则接收者将解密另一条消息:

message block ? initial vector ? another iv = another message

重新排列前面的等式,您将得到:

another iv = message block ? initial vector ? another message

如果攻击者同时知道加密消息的初始向量和内容,则可以将消息修改为所需的任何内容。 他要做的就是将已知消息内容和所需消息都与原始初始向量异或。

解密修改后的密文的任何人都将获得修改后的消息,而不是原始消息。

测试用例

我们创建了一个简单的测试案例,演示了对使用AES-CBC加密的消息的这种攻击。

想象一下,攻击者捕获了加密的电子邮件,并以某种方式知道其中的内容:

//original message
private static final String EMAIL = "Hi,\n" +"send Martin all requested money please.\n\n" +"With Regards, \n" +"Accounting\n";

攻击者只能更改消息的前16个字节,因此他决定将资金重定向到Andrea:

//changed message
private static final String MODIFICATION = "Hi,\n" +"give Andrea all requested money please.\n\n" +"With Regards, \n" +"Accounting\n";

以下测试用例对消息进行加密并修改密文。 修改后的密文被解密并与预期消息进行比较:

@Test
public void testModifiedMessage_AES() {//create cipher and the secret key StringCipherService cipher = new StringCipherService(new AesCipherService());byte[] key = cipher.generateNewKey();//encrypt the messagebyte[] ciphertext = cipher.encrypt(EMAIL, key);//attack: modify the encrypted messagefor (int i = 0; i < 16; i++) {ciphertext[i] = (byte)(ciphertext[i] ^ MODIFICATION.getBytes()[i] ^ EMAIL.getBytes()[i]);}//decrypt and verifyString result = cipher.decrypt(ciphertext, key);assertEquals(MODIFICATION, result);
}

当然,可以对河豚CBC进行类似的攻击。 这次我们只能更改前8个字节:

@Test
public void testModifiedMessage_Blowfish() {String email = "Pay 100 dollars to them, but nothing more. Accounting\n";StringCipherService cipher = new StringCipherService(new BlowfishCipherService());byte[] key = cipher.generateNewKey();byte[] ciphertext = cipher.encrypt(email, key);String modified = "Pay 900 dollars to them, but nothing more. Accounting\n";for (int i = 0; i < 8; i++) {ciphertext[i] = (byte)(ciphertext[i] ^ modified.getBytes()[i] ^ email.getBytes()[i]);}String result = cipher.decrypt(ciphertext, key);assertEquals(modified, result);
}

解密密码

第二次攻击使攻击者可以解密该秘密消息。 仅当解密机密消息的应用程序与攻击者合作时,才有可能进行攻击。

填充Oracle

攻击者会创建许多伪密文,并将其发送给收件人。 当他尝试解密这些消息时,将发生以下情况之一:

  • 密文解密为毫无意义的垃圾,
  • 修改后的消息将根本不是有效的密文。

如果应用程序在两种情况下的行为均相同,则一切正常。 如果行为不同,则攻击者可以解密密文。 基于CBC的密文只有一种错误的方法-如果填充错误。

例如,如果密文包含加密的密码,则易受攻击的服务器可能在解密密码错误的情况下以“拒绝登录”响应,而在无效密文的情况下以运行时异常响应。 在这种情况下,攻击者可以恢复密码。

大概的概念

每条虚假消息都有两个部分:一个虚假的初始向量和一个消息块。 两者都发送到服务器。 如果它回答“正确填充”,那么我们知道:

message ? original iv ? fake iv = valid padding

上述方程式中唯一未知的变量是消息。 original iv是先前的密文块, fake iv是由我们创建的,有效的填充是12, 23, 3, 3...8, 8, ..., 8等之一。

因此,我们可以将块内容计算为:

message = valid padding ? original iv ? fake iv

算法

从恢复最后一个块字节开始。 每个伪初始向量都以0开头,以不同的最后一个字节结尾。 这样,我们几乎可以确定服务器仅在以1结尾的消息上回答“向右填充”。使用上一章公式计算最后一个块字节。

获取消息的倒数第二个字节非常相似。 唯一的区别是我们必须制作一个密文,该密文解密为第二个最短的填充2, 2 。 消息的最后一个字节是已知的,因此将2强制为最后一个值很容易。 初始向量的开头并不重要,请将其设置为0。

然后,我们尝试初始向量的倒数第二个字节的所有可能值。 服务器回答“正确填充”后,我们可以从与前面相同的公式中获取倒数第二个消息字节: original iv ? fake iv ? 2 original iv ? fake iv ? 2 original iv ? fake iv ? 2

我们计算倒数第三个消息字节出来的假消息与填充3, 3, 3 ; 用填充4、4、4、4填充消息中的第四4, 4, 4, 4 ; 以此类推,直到整个块被解密为止。

测试用例

易受攻击的服务器使用PaddingOraculum类进行模拟。 此类的每个实例都会生成一个随机密钥,并将其保密。 它仅公开两个公共方法:

  • byte[] encrypt(String message) –用密钥加密一个字符串,
  • boolean verifyPadding(byte[] ciphertext) –返回填充是否正确。

填充oraculum攻击是用decryptLastBlock方法实现的。 该方法解密加密消息的最后一块:

private String decryptLastBlock(PaddingOraculum oraculum, byte[] ciphertext) {// extract relevant part of the ciphertextbyte[] ivAndBlock = getLastTwoBlocks(ciphertext, oraculum.getBlockSize());// modified initial vectorbyte[] ivMod = new byte[oraculum.getBlockSize()];Arrays.fill(ivMod, (byte) 0);// Start with last byte of the last block and // continue to the first byte.for (int i = oraculum.getBlockSize()-1; i >= 0; i--) {// add padding to the initial vector    int expectedPadding = oraculum.getBlockSize() - i;xorPad(ivMod, expectedPadding);// loop through possible values of ivModification[i]for (ivMod[i] =  -128; ivMod[i] <  127; ivMod[i]++) {// create fake message and verify its paddingbyte[] modifiedCiphertext = replaceBeginning(ivAndBlock, ivMod);if (oraculum.verifyPadding(modifiedCiphertext)) {// we can stop looping// the ivModification[i] =//    = solution ^ expectedPadding ^ ivAndBlock[i]break;}}// remove the padding from the initial vectorxorPad(ivMod, expectedPadding);}// modified initial vector now contains the solution xor // original initial vectorString result = "";for (int i = 0; i < ivMod.length; i++) {ivMod[i] = (byte) (ivMod[i] ^ ivAndBlock[i]);result += (char) ivMod[i];}return result;
}

我们的样本项目包含两个测试用例 。 一个人用AES-CBC加密消息,然后使用填充字眼到密文的最后一块。 另一个对河豚-CBC也做同样的事情。

解密Blowfish-CBC测试案例:

@Test
public void testPaddingOracle_Blowfish() {String message = "secret message!";PaddingOraculum oraculum = new PaddingOraculum(new BlowfishCipherService());//Oraculum encrypts the message with a secret key.byte[] ciphertext = oraculum.encrypt(message);//use oraculum to decrypt the messageString result = decryptLastBlock(oraculum, ciphertext);//the original message had padding 1assertEquals("essage!"+(char)1, result);
}

资源资源

其他相关资源:

  • 免费在线斯坦福加密课程 。
  • 从2002年开始用padding oracle攻击的原始文件。
  • 填充oracle攻击的一个很好的替代解释 。
  • 解释了对CBC的野兽袭击 。 它基于填充oracle攻击。
  • 关于常见密码错误的堆栈交换线程 。

结束

没有密码可以绝对安全地防御所有可能的攻击。 相反,它们仅提供针对定义明确的攻击类别的保护。 只有对系统的潜在威胁与密码强度匹配时,密码才是安全的。

可以通过两种方法来防御主动攻击:

  • 通过设计使主动攻击不可能。
  • 使用经过身份验证的加密。

使用经过身份验证的加密可以说更容易,并且应该是首选。 排除主动攻击者容易出错,而且风险更大。

这篇文章中使用的所有代码示例都可以在Github上找到 。

参考: This is Stuff博客上的JCG合作伙伴 Maria Jurcovicova提供的Java安全加密 。


翻译自: https://www.javacodegeeks.com/2012/05/secure-encryption-in-java.html

java 接口 安全加密

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

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

相关文章

设置Tomcat管理员用户名和密码

http://dove19900520.iteye.com/blog/1774980 今天tomcat出点问题&#xff0c;然后我就想进入tomcat manager看看&#xff0c;结果怎么输入密码都不行&#xff0c;后来网上查了查才找到配置管理员用户名和密码的正确方式&#xff0c;在此记录一下&#xff1a; 要想配置管理员用…

qgraphicsitem 复制副本_QGraphicsItem:调用paint函数时

I am creating an animation with QGraphicsView, QGraphicsScene and QGraphicsItem. Can someone explain me when the paint function is called? Although I does not change variables of the item, the paint function is called every ≈100 ms. Can I stop that, so i…

怎么把两个div一左一右放

怎么把两个div一左一右放1.代码 <% page contentType"text/html;charsetUTF-8" language"java" %> <html> <head><title>Title</title> </head> <body> <div style"width:150px;height:50px;margin:0;…

为Java + STANDARD值引入Cucumber

作为软件开发人员&#xff0c;我们都有最喜欢的工具来使我们成功。 许多人在上手时就很适合这份工作&#xff0c;但很快就不见了。 其他人则需要太多的设置和培训才能“将脚趾浸入水中”&#xff0c;只是为了找出自己是否是正确的工具即可。 Cucumber JVM是一个测试框架&#…

一个逼格很低的appium自动化测试框架

Github地址: https://github.com/wuranxu 使用说明 1. 安装配置Mongo数据库 下载地址 mongo是用来存放元素定位的&#xff0c;截图如下: 通过case_id区分每个case的元素定位 里面提供了value, method和text字段&#xff0c;分别作用是定位的值&#xff0c;定位的方法和要输入的…

手机长曝光怎么设置_挑战黑暗:怎样用手机拍出漂亮的长曝光照片?

无数个血淋淋的教训告诉我们&#xff1a;用手机&#xff0c;玩夜拍真的不靠谱。在光线不足的情况下&#xff0c;手机那比指甲盖还小的传感器会产生可怕的噪点&#xff0c;不管你眼睛看到的夜景有多美&#xff0c;用手机拍出来&#xff0c;效果都会变得一团糟。难道夜晚真的是手…

Port already in use: 1099;

Port already in use: 1099;1.使用命令:netstat -aon|findstr 1099 找出占用1099端口的进程; 2.关闭占用该端口的进程:taskkill -f -pid 9336;

一款好用且免费的语句分析工具Plan Explorer

在调优过程中的查询语句优化阶段&#xff0c;分析语句的执行计划是必经之路&#xff0c;一款好的执行计划分析工具确实可以帮助我们事半功倍 小贴士&#xff1a;Plan Explorer是将Plan Explorer 专业版与免费版整合在一起发布的全新、完全免费版。微软的数据库专家和开发人员也…

前端如何发ModelAndView的请求

前端如何发ModelAndView的请求1.在Web.xml指定用作工具的跳转页面

带有Gluon Ignite和Dagger的JavaFX中的依赖注入

依赖注入抽象框架Gluon Ignite在几个流行的依赖注入框架&#xff08;例如Spring&#xff0c;Dagger和Guice&#xff09;上创建了一个通用抽象。 目前&#xff0c;Gluon 页面仅包含一个示例&#xff0c;该示例使用Gluon Ignite和Google Guice作为依赖注入框架&#xff0c;我想尝…

字符串逆置

重视思想和理解原理 #include <iostream> #include <cstdlib> #include <cstring> using namespace std;char* reverseStr(char * str, char * dest, int len) {//指向最后一个字符char * s &str[len-1];char *d dest;while(len > 0){*dest *s--;le…

友声电子秤设置软件_友声电子秤说明书/操作指南?(一)

友声电子秤说明书一、技术指标:1&#xff0e;内装免维护充电蓄电池&#xff0c;充电可同时开机使用。2&#xff0e;开机自动置零。3&#xff0e;零点自动跟踪。4&#xff0e;累计次数1~100次。5&#xff0e;电源&#xff1a;交流220V(10%、-10%)&#xff0c;50Hz&#xff0c;内…

checkbox对齐排列

checkbox对齐排列<span style"width: 120px;display: inline-block"><lable><input type"checkbox"/></lable> </span>常用的 display 可能的值&#xff1a;

c linux 获取cpuid_Linux下C编程 -- 得到系统的CPU信息(cpuid)

在 linux下可以通过查看 cat /proc/cpuinfo查看CPU的相关信息&#xff0c;但是在linux下C编程需要使用汇编语言来实现&#xff0c;因为C语言中没有实现查看CPU信息的函数&#xff0c;一般实现如下&#xff1a;(C中运行汇编用 __asm__等)#include#includestatic inline voidget_…

centos开发环境安装的备忘

#Centos visudo运行普通用户$(whomai)执行sudo操作 http://www.cnblogs.com/xianyunhe/archive/2011/08/08/2124342.html 在/etc/gdm/custom.conf文件中添加以下内容 [daemon] AutomaticLoginusername AutomaticLoginEnable…

动态增删表单

动态增删表单1.实现效果 2.两种方式&#xff0c;推荐第二种&#xff08;不管是第一种还是第二种&#xff0c;想要序列化都必须&#xff0c;id不同&#xff09; &#xff08;1&#xff09;表单clone的方式&#xff08;简单演示&#xff0c;道理相通&#xff09; <div id&quo…

策略模式(组件协作模式)

策略模式&#xff08;组件协作模式&#xff09; 策略模式实例代码 注解 目的 正常情况下&#xff0c;一个类/对象中会包含其所有可能会使用的内外方法&#xff0c;但是一般情况下&#xff0c;这些常使用的类都是由不同的父类继承、组合得来的&#xff0c;来实现代码的复用&…

openwrt 遍译php_openwrt安装编译

官网安装编译推荐&#xff1a;1. host32位主机~$uname -aLinux yuxi-T530 3.13.0-66-generic #108-Ubuntu SMP Wed Oct 7 15:21:40 UTC 2015 i686 i686 i686 GNU/Linux~$lsb_release -aNo LSB modules are available.Distributor ID: UbuntuDescription: Ubuntu14.04.3LTSRelea…

java11+osgi_错误学习:Java + OSGi

java11osgi最近&#xff0c;我致力于在OSGi环境中使Apache Hive工作。 虽然没有被证明是小菜一碟&#xff08;软件对吗&#xff1f;。。为什么我不感到惊讶&#xff1f; &#xff09;&#xff0c;它引导我解决了各种Java和OSGi错误。 在这里&#xff0c;我列出了其中一些让我有…

《Effective Java》读书笔记 - 5.泛型

Chapter 5 Generics Item 23: Don’t use raw types in new code 虽然你可以把一个List<String>传给一个List类型&#xff08;raw type&#xff09;的参数&#xff0c;但你不应该这么做&#xff08;因为允许这样只是为了兼容遗留代码&#xff09;&#xff0c;举个例子&am…