Java 安全套接字编程以及keytool 使用最佳实践
概述
利用 Java 的 JSSE(Java Secure Socket Extension)技术,我们可以方便的编写安全套接字程序,关于 JSSE 的介绍,可以参阅 Oracle 网站提供的 JSSE 指导。程序编写过程中,我们需要将数字证书应用到代码中。通常在正式的产品开发中,我们可以支付一定的费用,向正规认证机构,例如:Verisign、Geotrust、Thawte 等申请。
如果只是为了实验,我们还可以使用 Java 自带的 keytool 工具来制作证书。keytool 是密钥和证书管理工具,生成的密钥或证书,存放在 jks(Java Key Store) 格式的文件里。从用途上来说,jks 格式的文件常用于:
1) 存储非对称密钥对以及数字证书的证书库;
2) 存储信任证书列表的信任库。
注意:不同版本的 Java 自带的 keytool 命令行参数可能会略有不同。相比于 Java6,在 Java7 中 keytool 工具有如下改动:
-export 选项改名为 -exportcert
-genkey 选项改名为 -genkeypair
-import 选项改名为 – importcert
-keyclone 选项被废弃
-identitydb 选项被废弃
-selfcert 选项被废弃
下面将以 Java7 中的 keytool 为例,对常见的用法进行说明。
--------------------------------------------------------------------------------------------------------------------------------------------------------
使用 keytool 制作证书库以及信任库
生成非对称密钥以及自签发证书
命令:keytool -genkeypair -alias TEST_ROOT -keystore test_root.jks
解释:生成一对密钥以及一个自签发证书,其中私钥和证书以别名 TEST_ROOT 存储在 test_root.jks 文件中。
注意:使用上述命令时,命令行会交互的需要手动填写密码、CN、OU 等信息。也可以直接在命令行指定这些参数,详情见 参考资料中列出的 keytool 使用帮助。
生成证书请求文件
命令:keytool -certreq -file test_server.csr -alias TEST_SERVER -keystore test_server.jks
解释:将别名为 TEST_SERVER 的公钥和一些个人信息从 test_server.jks 文件中导出,作为证书请求文件。
签发证书
命令:keytool -gencert -infile test_server.csr -outfile test_server.cer -alias TEST_ROOT -keystore TEST_ROOT.jks
解释:使用别名为 TEST_ROOT 的私钥为 test_server.csr 签发证书,并保存到 test_server.cer 文件中。
从 jks 文件中导出证书
命令:keytool -exportcert -alias TEST_ROOT -file test_root.cer -keystore test_root.jks
解释:从 test_root.jks 文件中导出别名为 TEST_ROOT 的证书并存放在 test_root.cer 文件中。
导入信任证书到 jks 文件
命令:keytool -importcert -alias TEST_ROOT -file test_root.cer -keystore TEST_SERVER.jks
解释:将证书 test_root.cer 以别名 TEST_ROOT 导入 TEST_SERVER.jks 中。
注意:这里的目标 jks 文件里不含有指定的别名,此时的导入条目才会以 trustedCertEntry 信任证书的形式保存。
导入签发证书到 jks 文件 ( 更新证书 )
命令:keytool -importcert -alias TEST_SERVER -file test_server.cer -keystore TEST_SERVER.jks
解释:将证书 test_server.cer 更新到已存在别名 TEST_SERVER 的 TEST_SERVER.jks 文件中
注意:这里的命令和上述导入信任证书的命令在形式上完全一样,但作用不同。
1. 这里的目标 jks 文件里要含有指定的别名,这样 keytool 工具才会理解命令为更新证书,并以 PrivateKeyEntry 的形式保存。
2. 在更新被签发证书之前,一定要先将相应的 CA 证书,导入进 jks 文件,否则会报错“keytool 错误 : java.lang.Exception: 无法从回复中建立链”。
打印证书内容
命令:keytool – printcert – v – file test_server.cer
解释:将证书 test_server.cer 的内容打印出来
注意:也可以使用 -sslserver ip:port 的参数,直接从网络上打印出某个 ssl server 提供的证书的内容,详情见 参考资料中列出的 keytool 使用帮助。
显示 jks 文件里的内容
命令:keytool – list – v – keystore test_server.jks
解释:显示 test_server.jks 里存储的所有条目
注意:这里会要求提供 jks 文件的密码,如果不输入,也可以显示出所有条目信息,但会提示“存储在密钥库中的信息的完整性尚未得到验证!”
从 jks 文件删除条目
命令:keytool -delete -alias TEST_ROOT -keystore test_server.jks
解释:从 test_server.jks 中删除别名为 TEST_ROOT 的条目
--------------------------------------------------------------------------------------------------------------------------------------------------------
安全套接字程序编写的方法
使用 Java 编写安全套接字程序,可以遵循一定的方法,如图 1 所示,展示了相关的各个类之间的关系。其中 Keystore、KeyManagerFactory、TrustManagerFactory、SSLContext 可以称之为“引擎类”(engine class),对它们指定特定的参数 ( 例如:协议、算法等 ),就可以产生符合我们要求的,用于编程的对象实例。
图 1. 相关类之间的关系
(注:图片引自 《 Java™ Secure Socket Extension (JSSE) Reference Guide 》)
编程的步骤可以简单的小结为以下几步:
1. 使用 Keystore 类将证书库或信任库文件加载进来;
2. 使用 KeyManagerFactory 和加载了证书库的 Keystore 实例,产生 KeyManager 实例数组;
3. 使用 TrustManagerFactory 和加载了信任库的 Keystore 实例,产生 TrustManager 实例数组;
4. 使用 SSLContext 初始化 KeyManager 实例数组和 TrustManager 实例数组,从而设定好通信的环境。
5. 利用 SSLContext 产生的 SSLSocket 或 SSLServerSocket 进行通信。
在编写具体程序之前,我们需要利用前文对 keytool 工具的知识介绍,准备如下 jks 文件:
1. test_root.jks:该文件中存有自签发的证书,用作 CA 来签发证书;
2. test_server_cert.jks:该文件中存有 CA 签名的证书,用于 SSL/TSL 通信的服务端;
3. test_server_trust.jks:该文件中存有信任客户端的证书,用于 SSL/TSL 通信的服务端;
4. test_client_cert.jks:该文件中存有 CA 签名的证书,用于 SSL/TSL 通信的客户端;
5. test_client_trust.jks:该文件中存有信任服务端的证书,用于 SSL/TSL 通信的客户端。
假定每个 jks 文件的密码都设定为“Testpassw0rd”,都存放在“D:”盘下。
--------------------------------------------------------------------------------------------------------------------------------------------------------
通过系统属性指定证书库和信任库
这种编写方式比较简单直观,可以通过给 JVM 传递参数,或者在代码中使用 System.setProperty() 方法,来指定通信需要的 jks 文件。
服务端程序
要运行如清单 1 所示的程序,可以在命令行添加如下虚拟机参数,指定服务端程序要使用的证书库和密码:
-Djavax.net.ssl.keyStore="D:/test_server_cert.jks"
-Djavax.net.ssl.keyStorePassword="Testpassw0rd"
注意到程序中 setNeedClientAuth(false),表示不需要验证客户端身份。如果这里设置为 true,则我们这里还需要指定信任库和密码:
-Djavax.net.ssl.trustStore="D:/test_server_trust.jks"
-Djavax.net.ssl.trustStorePassword="Testpassw0rd"
清单 1. 简单的 SSL 通信服务端程序
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket; import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocket; public class Simple_SSLServerSocket{
// 定义了监听端口号
private final static int LISTEN_PORT=54321; public static void main(String args[]) throws IOException{
SSLServerSocket serverSocket=null;
SSLSocket clientSocket=null;
// 使用默认方式获取套接字工厂实例
SSLServerSocketFactory ssf=(SSLServerSocketFactory)SSLServerSocketFactory.getDefault(); try{
serverSocket=(SSLServerSocket)ssf.createServerSocket(LISTEN_PORT);
// 设置不需要验证客户端身份
serverSocket.setNeedClientAuth(false);
System.out.println("SSLServer is listening on "+LISTEN_PORT+" port");
// 循环监听端口,如果有客户端连入就新开一个线程与之通信
while(true){
// 接受新的客户端连接
clientSocket=(SSLSocket)serverSocket.accept();
ClientConnection clientConnection=new ClientConnection(clientSocket);
// 启动一个新的线程
Thread clientThread=new Thread(clientConnection);
System.out.println("Client "+clientThread.getId()+" is connected");
clientThread.run();
}
}catch(IOException ioExp){
ioExp.printStackTrace();
}catch(Exception e){
e.printStackTrace();
}finally{
serverSocket.close();
}
}
} class ClientConnection implements Runnable{
private Socket clientSocket=null; public ClientConnection(SSLSocket sslsocket){
clientSocket=sslsocket;
} public void run(){
BufferedReader reader=null;
// 将接收到的来自客户端的文字打印出来
try{
reader=new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
while(true){
String line=reader.readLine();
if(line==null){
System.out.println("Communication end.");
break;
}
System.out.println("Receive message: "+line);
}
reader.close();
clientSocket.close();
}catch(IOException ioExp){
ioExp.printStackTrace();
}catch(Exception e){
e.printStackTrace();
}
}
}
客户端程序
对应于清单 1 所示的服务端程序,清单 2 是客户端程序,需要在命令行添加如下虚拟机参数,指定信任库和密码:
-Djavax.net.ssl.trustStore="D:/test_client_trust.jks"
-Djavax.net.ssl.trustStorePassword="Testpassw0rd"
如果服务端程序 setNeedClientAuth(true) 要求验证客户端身份,则我们还需要指定证书库和密码:
-Djavax.net.ssl.keyStore="D:/test_client_cert.jks"
-Djavax.net.ssl.keyStorePassword="Testpassw0rd"
清单 2. 简单的 SSL 通信客户端程序
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Writer;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory; public class Simple_SSLSocket{
// 定义要连接的服务器名和端口号
private static final int DEFAULT_PORT=54321;
private static final String DEFAULT_HOST="localhost";
public static void main(String args[]){
SSLSocket socket=null;
// 使用默认的方式获取工厂实例
SSLSocketFactory sf=(SSLSocketFactory)SSLSocketFactory.getDefault();
try{
// 连接服务端的端口,完成握手过程
socket=(SSLSocket)sf.createSocket(DEFAULT_HOST, DEFAULT_PORT);
socket.startHandshake();
System.out.println("Connected to "+DEFAULT_HOST+":"+DEFAULT_PORT+" !");
// 从控制台输入要发送给服务端的文字
BufferedReader reader=new BufferedReader(new InputStreamReader(System.in));
Writer writer=new OutputStreamWriter(socket.getOutputStream());
// 可以反复向服务端发送消息
boolean done=false;
while (!done) {
System.out.print("Send Message: ");
String line=reader.readLine();
if (line!=null) {
writer.write(line+"\n");
writer.flush();
}else{
done=true;
}
}
socket.close();
}catch (Exception e) {
System.out.println("Connection failed: "+e);
try{
socket.close();
}catch(IOException ioe){}
socket=null;
}
}
}
通过 SSLContext 指定证书库和信任库
前文描述的,通过系统参数指定证书库和信任库的方法,虽然简单易用,但是缺点也是显而易见的,整个程序的环境都得使用同样的 jks 文件。如果程序里有不同的 SSL/TSL 通信,则需要使用不同的 jks 文件,该怎么做呢?
可以使用 SSLContext 来指定 jks 文件,只需要把清单 3 的代码片段替换到清单 1 的“SSLServerSocketFactory ssf”生成处;把清单 4 的代码片段替换到清单 2 的“SSLSocketFactory sf”生成处,再稍作代码调整即可。
(注:实际上,在使用 SSLSocketFactory.getDefault() 或者 SSLServerSocketFactory.getDefault() 创建套接字的时候,程序内部已经使用了默认的 context,其参数就是通过系统属性指定的 )
清单 3. SSLContext 指定证书库
// 相关的 jks 文件及其密码定义
private final static String CERT_STORE="D:/test_server_cert.jks";
private final static String CERT_STORE_PASSWORD="Testpassw0rd";
// 载入 jks 文件
FileInputStream f_certStore=new FileInputStream(CERT_STORE);
KeyStore ks=KeyStore.getInstance("jks");
ks.load(f_certStore, CERT_STORE_PASSWORD.toCharArray());
f_certStore.close(); // 创建并初始化证书库工厂
String alg=KeyManagerFactory.getDefaultAlgorithm();
KeyManagerFactory kmFact=KeyManagerFactory.getInstance(alg);
kmFact.init(ks, CERT_STORE_PASSWORD.toCharArray()); KeyManager[] kms=kmFact.getKeyManagers(); // 创建并初始化 SSLContext 实例
SSLContext context=SSLContext.getInstance("SSL");
context.init(kms, null, null);
SSLServerSocketFactory ssf=(SSLServerSocketFactory)context.getServerSocketFactory();
清单 4. SSLContext 指定信任库
// 相关的 jks 文件及其密码定义
private final static String TRUST_STORE="D:/test_client_trust.jks";
private final static String TRUST_STORE_PASSWORD="Testpassw0rd"; // 载入 jks 文件
FileInputStream f_trustStore=new FileInputStream(TRUST_STORE);
KeyStore ks=KeyStore.getInstance("jks");
ks.load(f_trustStore, TRUST_STORE_PASSWORD.toCharArray());
f_trustStore.close(); // 创建并初始化信任库工厂
String alg=TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmFact=TrustManagerFactory.getInstance(alg);
tmFact.init(ks); TrustManager[] tms=tmFact.getTrustManagers(); // 创建并初始化 SSLContext 实例
SSLContext context=SSLContext.getInstance("SSL");
context.init(null, tms, null);
SSLSocketFactory sf=context.getSocketFactory();
当然,如果要在服务端或者客户端同时使用证书库和信任库,可将清单 3 和清单 4 的代码用在同一处 context.init() 的地方。
这里需要说明的是,如果 context.init() 的第一个 KeyManager[] 参数为 null,则表示不提供证书;如果第二个 TrustManager[] 参数为 null,
则会寻找系统默认提供的信任库 ( 例如:JRE 安装目录下的 lib/security/cacerts)。
使用 X509 证书信任管理器
X509TrustManager 接口扩展了 TrustManager 接口,使用 TrustManager 接口,我们已经可以在程序中自定义信任库了,但如果对方的证书不在信任库中,则通信会直接宣告失败。
如果希望能自定义信任库的一些行为 ( 例如:检验对方证书,针对异常做一些处理 ),我们可以使用 X509TrustManager 接口,实现自己的方法。
如清单 5 所示,假定我们要在客户端程序使用 X509TrustManager,那么就可以在 checkServerTrusted() 函数里做一些事情,检测到服务端证书异常的话,就可以做一些自己的处理。CheckClientTrusted() 则是用于服务端检测客户端的证书情况。
将清单 5 的代码替换到清单 4 的 TrustManager[] tms 的生成处,并对代码稍作调整即可。
清单 5. X509TrustManager 的使用
// 使用自定义的 MyTrustManager 产生信任库
TrustManager[] tms=new TrustManager[]{new MyTrustManager()}; class MyTrustManager implements X509TrustManager{
// 相关的 jks 文件及其密码定义
private final static String TRUST_STORE="D:/test_client_trust.jks";
private final static String TRUST_STORE_PASSWORD="Testpassw0rd";
X509TrustManager xtm; public MyTrustManager() throws Exception {
// 载入 jks 文件
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream(TRUST_STORE),TRUST_STORE_PASSWORD.toCharArray());
TrustManagerFactory tmf =TrustManagerFactory.getInstance("SunX509", "SunJSSE");
tmf.init(ks);
TrustManager[] tms = tmf.getTrustManagers();
// 筛选出 X509 格式的信任证书
for (int i = 0; i < tms.length; i++) {
if (tms[i] instanceof X509TrustManager) {
xtm = (X509TrustManager) tms[i];
return;
}
}
}
// 服务端检验客户端证书的接口
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException{
}
// 客户端检验服务端证书的接口
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException{
try{
xtm.checkServerTrusted(chain, authType);
}catch(CertificateException excep){
System.out.println(excep.getMessage());
throw excep;
}
}
// 获取可接受的发行者
public X509Certificate[] getAcceptedIssuers() {
//return xtm.getAcceptedIssuers();
return null;
}
}
注意:
1. 当服务端代码 setNeedClientAuth(False) 时,客户端的 MyTrustManager 实现了 X509TrustManager 后,如果 checkServerTrusted() 方法的实现为空,则无论服务端使用什么证书,客户端都会默认接受;如果要对服务端证书进行检查,还需要像清单 5 中的代码片段那样,捕捉异常并处理。
2.getAcceptedIssuers() 方法通常不需要具体实现,但是当服务端要求检验客户端身份,也即 setNeedClientAuth(True) 时,服务端需也需要具体实现 X509TrustManager,且 getAcceptedIssuers() 方法要如清单 5 中注释部分代码那样实现。
调试 SSL/TSL 程序
打开调试开关观察通信日志
图 2 描述了 SSL/TSL 通信的握手过程。在实际编写程序的时候,可能会在这些环节遇到问题,导致无法通信,排查起来往往令人无从下手。这个时候我们可以将 SSL/TSL 通信的握手日志开关打开,进行观察。
图 2.SSL 通信协议握手过程
(注:图片引自 《Java™ Secure Socket Extension (JSSE) Reference Guide》)
打开日志开关的方法同样是通过设定系统属性,可以从命令行添加虚拟机参数:
-Djavax.net.debug=ssl,handshake
当然也可以使用 System.setProperty() 方法在代码中打开该开关。
打开日志开关后,可以搜索“ClientHello”、“ServerHello”等关键字;或者搜索“*** ”( 三个星号和一个空格 ) ——这是握手阶段每一个步骤日志打印的开始标志。通过阅读日志来定位问题。更详细的开关信息,请参阅 JSSE 指导中的“Debugging Utilities”章节:
选择通信的 Cipher Suites
有的时候为了做实验,我们会选用特定的 Cipher Suites,我们可以使用 SSLServerSocket 或 SSLSocket 的 getEnabledCipherSuites() 观察到默认支持的所有加密套件的信息,然后使用 setEnabledCipherSuites() 进行设置。
清单 6 展示了从服务端过滤掉所有的匿名加密套件的代码。
清单 6. 过滤所有的匿名加密套件
String enabled[]=serverSocket.getEnabledCipherSuites();
Set<String> filter = new LinkedHashSet<String>();
for(int i = 0; i < enabled.length; i++) {
if(enabled[i].indexOf("anon")<0){
filter.add(enabled[i]);
}
}
serverSocket.setEnabledCipherSuites(filter.toArray(new String[filter.size()]));
参考资料
学习
- 《Java™ Secure Socket Extension (JSSE) Reference Guide》:Oracle 网站提供的 JSSE 指导。
- 《keytool - Key and Certificate Management Tool》:Oracle 网站提供的 keytool 使用帮助。
- 《Public key certificate》:维基百科网站关于数字证书的讲解。
- 《Cipher suite》:维基百科网站关于加密套件的介绍。
- 《J2EE 探索者:用 JAAS 和 JSSE 实现 Java 安全性》:developerworks 网站上一篇与 JAAS 和 JSSE 有关的文章。
- 《将 JSSE 用于安全套接字通信》:developerworks 网站上一篇介绍 JDK1.4 中 JSSE 相关的教程,也是很不错的参考资料。
- 《为高级 JSSE 开发人员定制 SSL》:developerworks 网站上另一篇介绍 JDK1.4 中 JSSE 相关文章。
- developerWorks Java 技术专区:这里有数百篇关于 Java 编程各个方面的文章。
讨论
- 加入 developerWorks 中文社区。查看开发人员推动的博客、论坛、组和维基,并与其他 developerWorks 用户交流。
本文转自http://www.ibm.com/developerworks/cn/java/j-lo-socketkeytool/
Java 安全套接字编程以及keytool 使用最佳实践的更多相关文章
- Java 安全套接字编程以及 keytool 使用最佳实践
Java 安全套接字编程以及 keytool 使用最佳实践 http://www.ibm.com/developerworks/cn/java/j-lo-socketkeytool/
- Java安全套接字扩展——JSSE
上节已经介绍了SSL/TLS协议的通信模式,而对于这些底层协议,如果要每个开发者都自己去实现显然会带来不必要的麻烦,正是为了解决这个问题Java为广大开发者提供了Java安全套接字扩展--JSSE,它 ...
- 关于shell脚本编程的10个最佳实践
每一个在UNIX/Linux上工作的程序员可能都擅长shell脚本编程.但大家解决问题的方式却不尽相同,这要取决于对专业知识的掌握程度.使 用命令的种类.看待问题的方式等等.对于那些处在shell脚本 ...
- shell脚本编程的10个最佳实践
摘要:每一个在UNIX/Linux上工作的程序员可能都擅长shell脚本编程.对于那些处在shell脚本编程初级阶段的程序员来说,遵循一些恰当的做法可以帮助你更快.更好的学习这些编程技巧. 每一个在U ...
- java 如何重写equal 和hashcode方法(最佳实践)
先看完理解这篇:Java hashCode() 和 equals()的若干问题解答 实现高质量的equals方法的诀窍包括 使用==操作符检查“参数是否为这个对象的引用”: 使用instanceof操 ...
- jQuery编程代码规范的最佳实践
好像是feedly订阅里看到的文章,读完后觉得非常不错,译之备用,多看受益. 加载jQuery 1.坚持使用CDN来加载jQuery,这种别人服务器免费帮你托管文件的便宜干嘛不占呢.点击查看使用C ...
- Java之HTTP网络编程(一):TCP/SSL网页下载
目录 一.简介:HTTP程序设计 1.HTTP系统设计 2.HTTP客户端工作过程 3.HTTP服务端工作过程 二.基于TCP Socket的HTTP网页下载 三.基于SSL Socket的HTTPS ...
- 20155304 2016-2017-2 《Java程序设计》实验三 敏捷开发与XP实践
实验三 敏捷开发与XP实践 实验内容 XP基础 XP核心实践 相关工具 实验步骤 (一)敏捷开发与XP 软件工程是把系统的.有序的.可量化的方法应用到软件的开发.运营和维护上的过程.软件工程包括下列领 ...
- 《转载》Java异常处理的10个最佳实践
本文转载自 ImportNew - 挖坑的张师傅 异常处理在编写健壮的 Java 应用中扮演着非常重要的角色.异常处理并不是功能性需求,它需要优雅地处理任何错误情况,比如资源不可用.非法的输入.nul ...
随机推荐
- http://lib.csdn.net/article/reactnative/40118
http://lib.csdn.net/article/reactnative/40118 ES6学习笔记(四)--数值与数组 作者:SirM2z 数值的扩展 Number.isFinite(), N ...
- 如何创建线程第一种继承Thread类
步骤 1:定义一个类 继承Thread类.2:重写Thread类的run方法.3:直接创建Thread的子类对象创建线程.4:调用start方法开启线程并调用线程的任务run方法执行.-------- ...
- 2019.04.09 电商24 订单模快 ORM
前面三个模块已近结束,现在看是订单模块的.想一下淘宝上的订单,在购物车中选中,提交,跳转到订单界面. 获取传过来的信息,那也要建立一个订单表,当我支付的时候,也要获取一些数据,将这些数据放到这个表中 ...
- nodejs+mysql入门实例(增)
var userAddSql = 'INSERT INTO userinfo(id,username,pwd) VALUES(0,?,?)'; var userAddSql_Params = ['Wi ...
- React项目中使用Mobx状态管理(二)
并上一节使用的是普通的数据状态管理,不过官方推荐使用装饰器模式,而在默认的react项目中是不支持装饰器的,需要手动启用. 官方参考 一.添加配置 官方提供了四种方法, 方法一.使用TypeScrip ...
- Nodejs中原生遍历文件夹
最近在听老师讲的node课程,有个关于把异步变为同步读取文件夹的知识点做一些笔记, 让迭代器逐个自执行.
- EL语言表达式 (三)【EL中的算术运算以及判断EL对象是否为空】
一.EL中的算术运算 EL和其他语言一样,同样也提供了基本的算术运算(加.减.乘.除和取余),如下图. 运算符 功能 示例 结果 + 加 ${19+1} 20 - 减 ${66-30} 36 * 乘 ...
- JavaScript 深入了解对象中的属性
本篇主要介绍JS中对象的属性,包括:属性的分类.访问方式.检测属性.遍历属性以及属性特性等内容. 目录 1. 介绍:描述属性的命名方式.查找路径以及分类 2. 属性的访问方式:介绍'.'访问方式.'[ ...
- SnmpTools配置
上网搜索了很多文档,但是snmptools一直没有配置好,原因就是64机器,网上的说法大多直接复制过来的,或者就没有考虑64位机器.经过仔细搜索和测试,一下是详细的配置过程: Index 安装 如果是 ...
- win10系统进入BIOS
按住shift+重启,在重启过程中界面会出现“疑难解答”,点击后,在新的界面点击“高级选项”,之后在新界面上点击“UEFI固件设置”,最后点击重启,重启过程中点击Delete键,就进入了BIOS界面了 ...