前言

示例 使用SpringBoot模拟服务端和客户端,使用okhttp作为httpClient工具。

如果对https相关理论不太熟悉和理解的可以看上一篇数据和安全①加解密理论概述

Okhttp https设置 HTTPS - OkHttp

证书在线格式转换 证书格式转换

私钥格式转换 KEY私钥格式转换工具-中国数字证书CHINASSL

证书工具openssl和keytool

keytools没办法签发证书,openssl能够进行签发和证书链的管理;

现有的证书大都采用X-509规范,所有不同格式证书导出后格式内容一致。

openssl默认采用pem格式,采用base64编码。

注意openssl有些命令在wins环境并不生效,需要在linux环境执行。

/index.html openssl官网

Apache Tomcat 9 (9.0.53) - SSL/TLS Configuration How-To tomcat keytool使用

配置前提

新建目录 newcerts 、private; crl、certs

新建文件index.txt、serial

serial文件写入01;

如图,修改openssl的配置文件openssl.cfg ;设置自己的CA目录,win配置文件路径 C:\OpenSSL-Win64\bin

#### 文件夹说明
dir:默认的ssl工作目录,可以修改默认目录,这里是安装完默认的certs:存放已经签发的证书
newcerts:存放CA新生成的证书
private:存放私钥
crl:存放已经吊销的证书
index.txt:已签发证书的文本数据库文件
serial:序列号存储文件,序列号为16进制数存储供证书签发使用序列号做参考.rand:私有随机文件
生成随机数命令:openssl rand -out xxx/.rand 1024
1024表示随机数长度
在生成证书的临时目录里创建默认配置目录文件命令,一键梭哈:
mkdir -p ./demoCA/certs; mkdir -p ./demoCA/crl; mkdir ./democA/newcerts; mkdir -p ./demoCA/private; touch ./demoCA/index.txt; touch ./demoCA/serial; echo 01 > ./demoCA/serial;

单向验证:

服务端:服务端私钥和证书(ca证书或者服务端证书均可)即可(根据私钥可以生成公钥)

客户端:客户端是需要证书、ca证书或者服务端证书均可;

猜测:私钥和公钥随用随时生成,毕竟不需要验证客户端证书。

问题:注册信息要相同啊、除了Common Name

The countryName field needed to be the same in the

1、生成ca私钥和ca证书

#生成ca私钥  放入private文件夹
openssl genrsa -out ca.key 2048
#根据ca私钥生成ca证书、私钥路径要写对不然找不到
##-subj "/C=CN/ST=beijing/L=beijing/O=beijing/OU=bj/CN=*.test.com/EM=test"
## 域名重要  *.test.com
openssl req -new -x509 -days 3650 -key C:/ssl/ca/private/ca.key -out ca.crt

字段

说明

示例

Country Name

ISO国家代码(两位字符)

CN

State or Province Name

所在省份

beijing

Locality Name

所在城市

beijing

Organization Name

公司名称

bj

Organizational Unit Name

部门名称

IT Dept.

Common Name

申请证书的域名

aa.test.com

Email Address

不需要输入

A challenge password

不需要输入

2、生成服务端私钥和服务端证书

##创建服务器私钥
openssl genrsa -out server.key 2048
## 根据服务器私钥生成证书请求  信息要相同/a challenge password  不需要输入、后面也不需要输入
### 信息要相同 CN可以不同 aa.test.com
## -subj "/C=CN/ST=beijing/L=beijing/O=beijing/OU=bj/CN=aa.test.com/EM=test"
openssl req -new -days 365 -key server.key -out server.csr
## 使用ca证书签署服务端证书
openssl ca -in server.csr -out server.crt -cert ca.crt -keyfile    C:/ssl/ca/private/ca.key 

生成ca签发证书以后、serial的01变为02;index.txt新加了签发证书的信息;newcert文件夹多了一个01.pem文件、和server.crt内容相同;

3、通过服务端私钥和服务端证书转jks

1、在线传证书和服务器私钥转jks 证书格式转换

2、使用openssl和keytool转jks

## 转换成pkcs12格式  wins执行卡死不出现结果,切换linux执行
## 输入一个导出密码,然后确认  123456
openssl pkcs12 -export -in server.crt -inkey server.key -out server.p12## 转换成jks、输入dt和sourese keystroe密码  123456,同下第四步的配置
##keystore包括私钥和证书,可能是整个证书链,一个keystore中还可以包含多个证书、私钥。
###一个keystore会有一个总的密码。keystore中的每个key(私钥)还可以设一个单独的密码。keytool -importkeystore -srckeystore server.p12 -destkeystore server.jks -srcstoretype pkcs12 -deststoretype jks

4、服务端配置

SpringBoot服务端开启,别的不需要,网上的太假

server:port: 11001ssl:key-store: classpath:127.0.0.1.jkskey-store-password: 123456key-store-type: jkskey-password: 123456

5、客户端配置

1、postman测试

setting---General SSL certificate verification 关闭证书校验

2、浏览器直接访问

3、客户端配置ca证书或者服务端证书

Java代码实现

Java核心代码类

TrustManagerFactory,它主要是用来导入自签名证书,用来验证来自服务器的连接。
KeyManagerFactory,当开启双向验证时,用来导入客户端的密钥对。
SSLContext,SSL 上下文,使用上面的两个类进行初始化,就是个上下文环境。
SSLSocketFactory :通过sslContext得到
SSLSocketFactory可以包含TrustManagerFactory和KeyManagerFactory

主要代码如下,详细代码放入github :https://github.com/zhouxiaohei/spring-ssl-demo

## 根据证书,生成TrustManagerFactory
private void createAndInitTrustManagerFactory() {if(StringUtils.isEmpty(caCertContent)){throw new IllegalArgumentException("服务端证书不可为空");}try {KeyStore caKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());caKeyStore.load(null, null);Certificate certificate = readCertFile(caCertContent);caKeyStore.setCertificateEntry(CA_CRT_ALIAS, certificate);//初始化trustManagerFactorytrustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());trustManagerFactory.init(caKeyStore);} catch (Exception e) {log.error("初始化TrustManagerFactory失败,证书内容{}", caCertContent, e);throw new RuntimeException(e);}
}
## 根据TrustManagerFactory初始化SSLContextpublic static class SSLParams {public SSLSocketFactory sSLSocketFactory;public X509TrustManager trustManager;
}public SSLParams getSSLParams(){try {SSLParams sslParams = new SSLParams();//得到ssl上下文SSLContext sslContext = SSLContext.getInstance("TLS");sslContext.init(null, trustManagerFactory.getTrustManagers(), null);sslParams.trustManager = getX509TrustManager();sslParams.sSLSocketFactory = sslContext.getSocketFactory();return sslParams;} catch (NoSuchAlgorithmException e) {e.printStackTrace();} catch (KeyManagementException e) {e.printStackTrace();}return null;
}
private X509TrustManager getX509TrustManager(){X509TrustManager x509TrustManager = (X509TrustManager) trustManagerFactory.getTrustManagers()[0];return x509TrustManager;
}
### 根据SSLParams 设置okhttpClient的Https初始化参数
OkHttpClient client = okHttpClient.newBuilder().sslSocketFactory(sslParams.sSLSocketFactory, sslParams.trustManager).hostnameVerifier( (a,b) -> true).build();  // 校验hostname,返回true
Request request = new Request.Builder().url("https://aa.test.com:11001/demo/bootswagger/person/123").get().build();
Response response = client.newCall(request).execute();
System.out.println(response.body().string());

 单向认证之-信任所有证书

public class TrustAllCerts implements X509TrustManager {@Overridepublic void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {}@Overridepublic void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {}@Overridepublic X509Certificate[] getAcceptedIssuers() {return new X509Certificate[0];}public static SSLSocketFactory createSSLSocketFactory() {SSLSocketFactory ssfFactory = null;try {SSLContext sc = SSLContext.getInstance("TLS");sc.init(null, new TrustManager[]{new TrustAllCerts()}, new SecureRandom());ssfFactory = sc.getSocketFactory();} catch (Exception e) {}return ssfFactory;}
}## 调用public static void testOneWayAllHttps(String url){try {// 方式1  过期
//            OkHttpClient client = okHttpClient.newBuilder().
//                    sslSocketFactory(ClientCredentials.createSSLSocketFactory())
//                    .hostnameVerifier( (a,b) -> true).build();  // 校验hostname,返回true// 方式2SSLContext sslContext = SSLContext.getInstance("TLS");TrustAllCerts trustAllCerts = new TrustAllCerts();sslContext.init(null, new TrustManager[]{trustAllCerts}, new SecureRandom());// sslContext初始化 TrustManager 为什么必须填  unable to find valid certification path to requested target//sslContext.init(null, null, new SecureRandom());OkHttpClient client = okHttpClient.newBuilder().sslSocketFactory(sslContext.getSocketFactory(),  trustAllCerts).hostnameVerifier( (a,b) -> true).build();  // 校验hostname,返回trueRequest request = new Request.Builder().url(url).get().build();Response response = client.newCall(request).execute();System.out.println(response.body().string());} catch (IOException e) {e.printStackTrace();} catch (NoSuchAlgorithmException e) {e.printStackTrace();} catch (KeyManagementException e) {e.printStackTrace();}}

双向认证:

前提:服务端证书和客户端证书都由同一个ca证书签发

服务端:ca证书、客户端证书、服务端证书、服务端私钥

客户端: ca或者服务端证书、客户端证书、客户端私钥

需要文件:ca.crt、server.key、server.crt、client.key、client.crt

注意事项:

tomcat应该只支持JKS,PKCS11或 PKCS12格式的密钥库,不支持pem

java.io.IOException: Failed to load keystore type [pem] with path 。。。due to [pem not found]

前面的1、2、3步骤不变,沿用ca证书和服务端jks

4、生成客户端私钥和证书-- 和服务端生成方式一样

##创建客户端私钥
openssl genrsa -out client.key 2048
#### 信息要相同 CN可以不同 bb.test.com
## -subj "/C=CN/ST=beijing/L=beijing/O=beijing/OU=bj/CN=bb.test.com/EM=test"
openssl req -new -days 365 -key client.key -out client.csr
## 使用ca证书签署客户端证书
openssl ca -in client.csr -out client.crt -cert ca.crt -keyfile C:/ssl/ca/private/ca.key 

5、将客户端证书添加到服务端jks中

## 将客户端证书导入服务端jks
keytool -import -v -file client.cer -keystore server.jks
### 查看证书文件  cer和crt证书文件一模一样,顶多去掉前缀
### 查看jks的证书文件,如下图
keytool -list -rfc -keystore server.jks -storepass 123456
## 服务端信任客户端证书、客户端证书信任服务端不行,要将根证书导入秘钥库
## 要将ca证书也导入证书库
keytool -import -file ca.crt -alias ca -keystore server.jks -storepass 123456

如果没有添加根证书到jks秘钥库,报错如下:

SSLHandshakeException: Received fatal alert: bad_certificate

6、配置SpingBoot server

ssl:key-store: classpath:server.jkskey-store-password: 123456key-store-type: jkskey-password: 123456## 开启双向验证trust-store: classpath:server.jkstrust-store-password: 123456trust-store-type: jksclient-auth: need

7、访问地址https://aa.test.com:11001/demo/bootswagger/person/12

双向验证,浏览器就不好玩了。

8、使用postman添加客户端证书和客户端私钥正常访问接口;

双向验证java代码实现

客户端JKS模式

客户端配置,根据客户端证书和私钥生成客户端jks证书;

通过上面的方式用在线工具或者使用openssl和keytool转jks均可;

代码解读、通过ca证书或者服务端证书生成trustManagerFactory

通过client.jks得到keyManagerFactory然后初始化SSLContext ,最后得到SSLParams 设置okhttpClient的Https初始化参数

@Slf4j
public class JksClientCredentials {private TrustManagerFactory trustManagerFactory;private KeyManagerFactory keyManagerFactory;private static final String CA_CRT_ALIAS = "caCert-cert";private String caCertContent;private FileInputStream clientJksStream;private String keyStorePass;public JksClientCredentials(String caCertContent, FileInputStream clientJksStream, String keyStorePass) {this.caCertContent = caCertContent;this.clientJksStream = clientJksStream;this.keyStorePass = keyStorePass;}public static class SSLParams {public SSLSocketFactory sSLSocketFactory;public X509TrustManager trustManager;}public JksClientCredentials.SSLParams getSSLParams(){try {// 1、通过证书得到TrustManagerFactorycreateAndInitTrustManagerFactory();//2、如果客户端证书和私钥存在,得到KeyManagerFactorycreateAndInitKeyManagerFactory();//3、Okhttp取消了,单参数方法,返回多参数用于Okhttp初始化JksClientCredentials.SSLParams sslParams = new JksClientCredentials.SSLParams();//得到ssl上下文SSLContext sslContext = SSLContext.getInstance("TLS");sslContext.init(keyManagerFactory == null ? null : keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom());sslParams.trustManager = getX509TrustManager();sslParams.sSLSocketFactory = sslContext.getSocketFactory();return sslParams;} catch (NoSuchAlgorithmException e) {e.printStackTrace();} catch (KeyManagementException e) {e.printStackTrace();}return null;}/*** @Author JackZhou* @Description  证书文件的标签说明不需要处理* @Date 2020/6/4 15:08**/private void createAndInitTrustManagerFactory() {if(StringUtils.isEmpty(caCertContent)){throw new IllegalArgumentException("服务端证书不可为空");}try {KeyStore caKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());caKeyStore.load(null, null);Certificate certificate = getCertificate(caCertContent);caKeyStore.setCertificateEntry(CA_CRT_ALIAS, certificate);//初始化trustManagerFactorytrustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());trustManagerFactory.init(caKeyStore);} catch (Exception e) {log.error("初始化TrustManagerFactory失败,证书内容{}", caCertContent, e);throw new RuntimeException(e);}}private void createAndInitKeyManagerFactory(){try{KeyStore clientKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());clientKeyStore.load(clientJksStream, keyStorePass.toCharArray());keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());keyManagerFactory.init(clientKeyStore, keyStorePass.toCharArray());}catch (Exception e){log.error("初始化KeyManagerFactory失败", e);throw new RuntimeException(e);}}private Certificate getCertificate(String content) throws CertificateException {CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");Certificate certificate = certificateFactory.generateCertificate(new ByteArrayInputStream(content.getBytes()));return  certificate;}private X509TrustManager getX509TrustManager(){X509TrustManager x509TrustManager = (X509TrustManager) trustManagerFactory.getTrustManagers()[0];return x509TrustManager;}
}### 调用方法
// JKS双向验证方式成功
public static void testTwoWayHttpsJks(){try {String caCertContent = FileUtils.readFile("src/main/resources/httpsClient/ca.crt");String clientJksfilePath = "src/main/resources/httpsClient/client.jks";JksClientCredentials jksClientCredentials = new JksClientCredentials(caCertContent, new FileInputStream(clientJksfilePath), "123456");JksClientCredentials.SSLParams sslParams = jksClientCredentials.getSSLParams();OkHttpClient client = okHttpClient.newBuilder().sslSocketFactory(sslParams.sSLSocketFactory, sslParams.trustManager).hostnameVerifier( (a,b) -> true).build();  // 校验hostname,返回trueRequest request = new Request.Builder().url("https://aa.test.com:11001/demo/bootswagger/person/123").get().build();Response response = client.newCall(request).execute();System.out.println(response.body().string());} catch (IOException e) {e.printStackTrace();}
}

客户端PEM模式

原生pem的RSA秘钥加载报错

java.security.InvalidKeyException: IOException : algid parse error, not a sequence

把私钥改成pkcs8 格式才行

openssl pkcs8 -topk8 -inform PEM -in private.key -outform pem -nocrypt -out pkcs8.pem  

Pem代码实现如下

@Slf4j
public class PemClientCredentials {private static final String CA_CRT_ALIAS = "caCert-cert";private static final String CRT_ALIAS = "cert";private TrustManagerFactory trustManagerFactory;private KeyManagerFactory keyManagerFactory;private String caCertContent;private String privateKeyContent;private String clientCertContent;public PemClientCredentials(String caCertContent, String privateKeyContent, String clientCertContent) {this.caCertContent = caCertContent;this.privateKeyContent = privateKeyContent;this.clientCertContent = clientCertContent;}public static class SSLParams {public SSLSocketFactory sSLSocketFactory;public X509TrustManager trustManager;}public SSLParams getSSLParams(){try {// 1、通过证书得到TrustManagerFactorycreateAndInitTrustManagerFactory();//2、如果客户端证书和私钥存在,得到KeyManagerFactorycreateAndInitKeyManagerFactory();//3、Okhttp取消了,单参数方法,返回多参数用于Okhttp初始化SSLParams sslParams = new SSLParams();//得到ssl上下文SSLContext sslContext = SSLContext.getInstance("TLS");sslContext.init(keyManagerFactory == null ? null : keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);sslParams.trustManager = getX509TrustManager();sslParams.sSLSocketFactory = sslContext.getSocketFactory();return sslParams;} catch (NoSuchAlgorithmException e) {e.printStackTrace();} catch (KeyManagementException e) {e.printStackTrace();}return null;}/*** @Author JackZhou* @Description  证书文件的标签说明不需要处理* @Date 2020/6/4 15:08**/private void createAndInitTrustManagerFactory() {if(StringUtils.isEmpty(caCertContent)){throw new IllegalArgumentException("服务端证书不可为空");}try {KeyStore caKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());caKeyStore.load(null, null);Certificate certificate = readCertFile(caCertContent);caKeyStore.setCertificateEntry(CA_CRT_ALIAS, certificate);//初始化trustManagerFactorytrustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());trustManagerFactory.init(caKeyStore);} catch (Exception e) {log.error("初始化TrustManagerFactory失败,证书内容{}", caCertContent, e);throw new RuntimeException(e);}}private X509TrustManager getX509TrustManager(){X509TrustManager x509TrustManager = (X509TrustManager) trustManagerFactory.getTrustManagers()[0];return x509TrustManager;}private void createAndInitKeyManagerFactory(){if(StringUtils.isEmpty(privateKeyContent) || StringUtils.isEmpty(clientCertContent)){return;}try{KeyStore clientKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());clientKeyStore.load(null, null);char[] passwordCharArray = "".toCharArray();Certificate certificate = readCertFile(clientCertContent);clientKeyStore.setCertificateEntry(CRT_ALIAS, certificate);clientKeyStore.setKeyEntry("private-key", readPrivateKeyFile(privateKeyContent), passwordCharArray, new Certificate[]{certificate});keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());keyManagerFactory.init(clientKeyStore, passwordCharArray);}catch (Exception e){log.error("初始化KeyManagerFactory失败", e);throw new RuntimeException(e);}}private static PrivateKey readPrivateKeyFile(String fileContent) throws Exception {RSAPrivateKey privateKey = null;if (fileContent != null && !fileContent.isEmpty()) {fileContent = fileContent.replace("-----BEGIN PRIVATE KEY-----\n", "").replace("-----BEGIN PRIVATE KEY-----\r\n", "").replace("-----END PRIVATE KEY-----", "").replaceAll("\\s", "");byte[] decoded = Base64.decodeBase64(fileContent);KeyFactory keyFactory = KeyFactory.getInstance("RSA");privateKey = (RSAPrivateKey) keyFactory.generatePrivate(new PKCS8EncodedKeySpec(decoded));}return privateKey;}private X509Certificate readCertFile(String fileContent) throws Exception {X509Certificate certificate = null;if (fileContent != null && !fileContent.trim().isEmpty()) {fileContent = fileContent.replace("-----BEGIN CERTIFICATE-----\n", "").replace("-----BEGIN CERTIFICATE-----\r\n", "").replace("-----END CERTIFICATE-----", "");byte[] decoded = Base64.decodeBase64(fileContent);CertificateFactory certFactory = CertificateFactory.getInstance("X.509");certificate = (X509Certificate) certFactory.generateCertificate(new ByteArrayInputStream(decoded));}return certificate;}}public static void testTwoWayHttps(){try {// ca和server 证书都行String caCertContent = FileUtils.readFile("src/main/resources/httpsClient/ca.crt");//String caCertContent = FileUtils.readFile("src/main/resources/httpsClient/server.crt");String clientCertContent = FileUtils.readFile("src/main/resources/httpsClient/client.crt");String clientKeyContent = FileUtils.readFile("src/main/resources/httpsClient/target_pkcs8_privatekey.key");//String clientKeyContent = FileUtils.readFile("src/main/resources/httpsClient/client.key");PemClientCredentials credentials = new PemClientCredentials(caCertContent, clientKeyContent, clientCertContent);PemClientCredentials.SSLParams sslParams = credentials.getSSLParams();OkHttpClient client = okHttpClient.newBuilder().sslSocketFactory(sslParams.sSLSocketFactory, sslParams.trustManager).hostnameVerifier( (a,b) -> true).build();  // 校验hostname,返回trueRequest request = new Request.Builder().url("https://aa.test.com:11001/demo/bootswagger/person/123").get().build();Response response = client.newCall(request).execute();System.out.println(response.body().string());} catch (IOException e) {e.printStackTrace();}
}

项目已经提交到github :https://github.com/zhouxiaohei/spring-ssl-demo

数据和安全②HTTPS单向和双向认证相关推荐

  1. HTTPS原理、单向和双向认证

    参考文章:https://blog.51cto.com/11883699/2160032 https://www.songma.com/news/txtlist_i39807v.html 众所周知,W ...

  2. SSL单向、双向认证

    引用:http://cbwdkpl.blog.163.com/blog/static/453293822009814111320789/ 单向认证:客户端向服务器发送消息,服务器接到消息后,用服务器端 ...

  3. SSL Tomcat 双向认证

    基本逻辑: 1.生成服务端密钥库并导出证书: 2.生成客户端密钥库并导出证书: 3.根据服务端密钥库生成客户端信任的证书: 4.将客户端证书导入服务端密钥库: 5.将服务端证书导入浏览器. 基本思路解 ...

  4. TLS/SSL双向认证

    相关文章: openssl genrsa 命令详解 一.PKI.CA.TLS/SSL.OpenSSL等概念及原理 CA 证书签发机构,自己持有私钥,创建根证书,并把根证书发送给操作系统厂商,内置于操作 ...

  5. Https单向认证和双向认证介绍

    一.Http HyperText Transfer Protocol,超文本传输协议,是互联网上使用最广泛的一种协议,所有WWW文件必须遵循的标准.HTTP协议传输的数据都是未加密的,也就是明文的,因 ...

  6. 干货 | 图解 https 单向认证和双向认证!

    一.Http HyperText Transfer Protocol,超文本传输协议,是互联网上使用最广泛的一种协议,所有WWW文件必须遵循的标准.HTTP协议传输的数据都是未加密的,也就是明文的,因 ...

  7. Https单向认证和双向认证 认识和区别

    一.Http HyperText Transfer Protocol,超文本传输协议,是互联网上使用最广泛的一种协议,所有WWW文件必须遵循的标准.HTTP协议传输的数据都是未加密的,也就是明文的,因 ...

  8. 详细理解 https 单向认证和双向认证原理

    HTTP 简介: HyperText Transfer Protocol,超文本传输协议,是互联网上使用最广泛的一种协议,所有WWW文件必须遵循的标准.HTTP协议传输的数据都是未加密的,也就是明文的 ...

  9. https 单向认证和双向认证

    转载自   https 单向认证和双向认证 一.Http HyperText Transfer Protocol,超文本传输协议,是互联网上使用最广泛的一种协议,所有WWW文件必须遵循的标准.HTTP ...

最新文章

  1. 苹果支付和ios安全 - 你需要知道的
  2. oracle 学习——巨人的肩膀
  3. Java 垃圾回收算法之G1
  4. Codeforces #471
  5. 二分法的应用:POJ1064 Cable master
  6. python3 通过百度地图API获取城市POI点并存于CSV格式
  7. Java千百问_07JVM架构(001)_java内存模型是什么样的
  8. Oracle作业5——多表查询、子查询
  9. 【BZOJ 1491】 [NOI2007]社交网络
  10. html去除分页符,分页符怎么删除 删除分页符的两种方法
  11. App的开发成本是多少?
  12. 网路连接配置和DNS服务?解决无线网卡连接WIFI问题(硬件和驱动没问题)?
  13. linux平台使用yum安装mysql
  14. C++迷宫最短路径问题BFS
  15. 黄油刀 Butterknife的使用准备工作
  16. GitHub上传超过100M的单个文件(包括处理和解决)
  17. java string时间类型天数运算
  18. linux 下dump文件放在那里,怎么查看dump文件目录
  19. 微信已经成为电商最重要的一个通道
  20. python哈姆雷特词频统计_【Python】哈姆雷特字数统计

热门文章

  1. 经典排序算法-----归并排序(C语言实现)
  2. 二叉树的层序遍历-Java
  3. Must call super constructor in derived class before accessing or returning from derived const
  4. java如何获取系统的桌面路径
  5. 能源价格风险管理matlab源代码 经济物理学、电价、风险管理、均值回归
  6. Android数据库高手秘籍(六)——LitePal的修改和删除操作
  7. 你可以成为测试界的李子柒
  8. python键盘键值表_Python怎么记录键盘鼠标敲击次数|Python统计鼠标点击次数 - PS下...
  9. PPT实用功能——布尔运算
  10. 解析xml的几种方法,他们的原理,比较 以及JAVA源码