Java加密技术(八)中,我们模拟了一个基于RSA非对称加密网络的安全通信。现在我们深度了解一下现有的安全网络通信——SSL。
    我们需要构建一个由CA机构签发的有效证书,这里我们使用上文中生成的自签名证书zlex.cer

    这里,我们将证书导入到我们的密钥库。

  1. keytool -import -alias www.zlex.org -file d:/zlex.cer -keystore d:/zlex.keystore

其中

-import表示导入

-alias指定别名,这里是www.zlex.org

-file指定算法,这里是d:/zlex.cer

-keystore指定存储位置,这里是d:/zlex.keystore

在这里我使用的密码为654321



控制台输出:

  1. 输入keystore密码:
  2. 再次输入新密码:
  3. 所有者:CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN
  4. 签发人:CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN
  5. 序列号:4a1e48df
  6. 有效期: Thu May 28 16:18:39 CST 2009 至Wed Aug 26 16:18:39 CST 2009
  7. 证书指纹:
  8. MD5:19:CA:E6:36:E2:DF:AD:96:31:97:2F:A9:AD:FC:37:6A
  9. SHA1:49:88:30:59:29:45:F1:69:CA:97:A9:6D:8A:CF:08:D2:C3:D5:C0:C4
  10. 签名算法名称:SHA1withRSA
  11. 版本: 3
  12. 信任这个认证? [否]:  y
  13. 认证已添加至keystore中

OK,最复杂的准备工作已经完成。

接下来我们将域名www.zlex.org定位到本机上。打开C:\Windows\System32\drivers\etc\hosts文件,将www.zlex.org绑定在本机上。在文件末尾追加127.0.0.1       www.zlex.org。现在通过地址栏访问http://www.zlex.org,或者通过ping命令,如果能够定位到本机,域名映射就搞定了。

现在,配置tomcat。先将zlex.keystore拷贝到tomcat的conf目录下,然后配置server.xml。将如下内容加入配置文件

  1. <Connector
  2. SSLEnabled="true"
  3. URIEncoding="UTF-8"
  4. clientAuth="false"
  5. keystoreFile="conf/zlex.keystore"
  6. keystorePass="123456"
  7. maxThreads="150"
  8. port="443"
  9. protocol="HTTP/1.1"
  10. scheme="https"
  11. secure="true"
  12. sslProtocol="TLS" />

注意clientAuth="false"测试阶段,置为false,正式使用时建议使用true。现在启动tomcat,访问https://www.zlex.org/

显然,证书未能通过认证,这个时候你可以选择安装证书(上文中的zlex.cer文件就是证书),作为受信任的根证书颁发机构导入,再次重启浏览器(IE,其他浏览器对于域名www.zlex.org不支持本地方式访问),访问https://www.zlex.org/,你会看到地址栏中会有个小锁,就说明安装成功。所有的浏览器联网操作已经在RSA加密解密系统的保护之下了。但似乎我们感受不到。

这个时候很多人开始怀疑,如果我们要手工做一个这样的https的访问是不是需要把浏览器的这些个功能都实现呢?不需要!



接着上篇内容,给出如下代码实现:

  1. import java.io.FileInputStream;
  2. import java.security.KeyStore;
  3. import java.security.PrivateKey;
  4. import java.security.PublicKey;
  5. import java.security.Signature;
  6. import java.security.cert.Certificate;
  7. import java.security.cert.CertificateFactory;
  8. import java.security.cert.X509Certificate;
  9. import java.util.Date;
  10. import javax.crypto.Cipher;
  11. import javax.net.ssl.HttpsURLConnection;
  12. import javax.net.ssl.KeyManagerFactory;
  13. import javax.net.ssl.SSLContext;
  14. import javax.net.ssl.SSLSocketFactory;
  15. import javax.net.ssl.TrustManagerFactory;
  16. /**
  17. * 证书组件
  18. *
  19. * @author 梁栋
  20. * @version 1.0
  21. * @since 1.0
  22. */
  23. public abstract class CertificateCoder extends Coder {
  24. /**
  25. * Java密钥库(Java Key Store,JKS)KEY_STORE
  26. */
  27. public static final String KEY_STORE = "JKS";
  28. public static final String X509 = "X.509";
  29. public static final String SunX509 = "SunX509";
  30. public static final String SSL = "SSL";
  31. /**
  32. * 由KeyStore获得私钥
  33. *
  34. * @param keyStorePath
  35. * @param alias
  36. * @param password
  37. * @return
  38. * @throws Exception
  39. */
  40. private static PrivateKey getPrivateKey(String keyStorePath, String alias,
  41. String password) throws Exception {
  42. KeyStore ks = getKeyStore(keyStorePath, password);
  43. PrivateKey key = (PrivateKey) ks.getKey(alias, password.toCharArray());
  44. return key;
  45. }
  46. /**
  47. * 由Certificate获得公钥
  48. *
  49. * @param certificatePath
  50. * @return
  51. * @throws Exception
  52. */
  53. private static PublicKey getPublicKey(String certificatePath)
  54. throws Exception {
  55. Certificate certificate = getCertificate(certificatePath);
  56. PublicKey key = certificate.getPublicKey();
  57. return key;
  58. }
  59. /**
  60. * 获得Certificate
  61. *
  62. * @param certificatePath
  63. * @return
  64. * @throws Exception
  65. */
  66. private static Certificate getCertificate(String certificatePath)
  67. throws Exception {
  68. CertificateFactory certificateFactory = CertificateFactory
  69. .getInstance(X509);
  70. FileInputStream in = new FileInputStream(certificatePath);
  71. Certificate certificate = certificateFactory.generateCertificate(in);
  72. in.close();
  73. return certificate;
  74. }
  75. /**
  76. * 获得Certificate
  77. *
  78. * @param keyStorePath
  79. * @param alias
  80. * @param password
  81. * @return
  82. * @throws Exception
  83. */
  84. private static Certificate getCertificate(String keyStorePath,
  85. String alias, String password) throws Exception {
  86. KeyStore ks = getKeyStore(keyStorePath, password);
  87. Certificate certificate = ks.getCertificate(alias);
  88. return certificate;
  89. }
  90. /**
  91. * 获得KeyStore
  92. *
  93. * @param keyStorePath
  94. * @param password
  95. * @return
  96. * @throws Exception
  97. */
  98. private static KeyStore getKeyStore(String keyStorePath, String password)
  99. throws Exception {
  100. FileInputStream is = new FileInputStream(keyStorePath);
  101. KeyStore ks = KeyStore.getInstance(KEY_STORE);
  102. ks.load(is, password.toCharArray());
  103. is.close();
  104. return ks;
  105. }
  106. /**
  107. * 私钥加密
  108. *
  109. * @param data
  110. * @param keyStorePath
  111. * @param alias
  112. * @param password
  113. * @return
  114. * @throws Exception
  115. */
  116. public static byte[] encryptByPrivateKey(byte[] data, String keyStorePath,
  117. String alias, String password) throws Exception {
  118. // 取得私钥
  119. PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password);
  120. // 对数据加密
  121. Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
  122. cipher.init(Cipher.ENCRYPT_MODE, privateKey);
  123. return cipher.doFinal(data);
  124. }
  125. /**
  126. * 私钥解密
  127. *
  128. * @param data
  129. * @param keyStorePath
  130. * @param alias
  131. * @param password
  132. * @return
  133. * @throws Exception
  134. */
  135. public static byte[] decryptByPrivateKey(byte[] data, String keyStorePath,
  136. String alias, String password) throws Exception {
  137. // 取得私钥
  138. PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password);
  139. // 对数据加密
  140. Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
  141. cipher.init(Cipher.DECRYPT_MODE, privateKey);
  142. return cipher.doFinal(data);
  143. }
  144. /**
  145. * 公钥加密
  146. *
  147. * @param data
  148. * @param certificatePath
  149. * @return
  150. * @throws Exception
  151. */
  152. public static byte[] encryptByPublicKey(byte[] data, String certificatePath)
  153. throws Exception {
  154. // 取得公钥
  155. PublicKey publicKey = getPublicKey(certificatePath);
  156. // 对数据加密
  157. Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
  158. cipher.init(Cipher.ENCRYPT_MODE, publicKey);
  159. return cipher.doFinal(data);
  160. }
  161. /**
  162. * 公钥解密
  163. *
  164. * @param data
  165. * @param certificatePath
  166. * @return
  167. * @throws Exception
  168. */
  169. public static byte[] decryptByPublicKey(byte[] data, String certificatePath)
  170. throws Exception {
  171. // 取得公钥
  172. PublicKey publicKey = getPublicKey(certificatePath);
  173. // 对数据加密
  174. Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
  175. cipher.init(Cipher.DECRYPT_MODE, publicKey);
  176. return cipher.doFinal(data);
  177. }
  178. /**
  179. * 验证Certificate
  180. *
  181. * @param certificatePath
  182. * @return
  183. */
  184. public static boolean verifyCertificate(String certificatePath) {
  185. return verifyCertificate(new Date(), certificatePath);
  186. }
  187. /**
  188. * 验证Certificate是否过期或无效
  189. *
  190. * @param date
  191. * @param certificatePath
  192. * @return
  193. */
  194. public static boolean verifyCertificate(Date date, String certificatePath) {
  195. boolean status = true;
  196. try {
  197. // 取得证书
  198. Certificate certificate = getCertificate(certificatePath);
  199. // 验证证书是否过期或无效
  200. status = verifyCertificate(date, certificate);
  201. } catch (Exception e) {
  202. status = false;
  203. }
  204. return status;
  205. }
  206. /**
  207. * 验证证书是否过期或无效
  208. *
  209. * @param date
  210. * @param certificate
  211. * @return
  212. */
  213. private static boolean verifyCertificate(Date date, Certificate certificate) {
  214. boolean status = true;
  215. try {
  216. X509Certificate x509Certificate = (X509Certificate) certificate;
  217. x509Certificate.checkValidity(date);
  218. } catch (Exception e) {
  219. status = false;
  220. }
  221. return status;
  222. }
  223. /**
  224. * 签名
  225. *
  226. * @param keyStorePath
  227. * @param alias
  228. * @param password
  229. *
  230. * @return
  231. * @throws Exception
  232. */
  233. public static String sign(byte[] sign, String keyStorePath, String alias,
  234. String password) throws Exception {
  235. // 获得证书
  236. X509Certificate x509Certificate = (X509Certificate) getCertificate(
  237. keyStorePath, alias, password);
  238. // 获取私钥
  239. KeyStore ks = getKeyStore(keyStorePath, password);
  240. // 取得私钥
  241. PrivateKey privateKey = (PrivateKey) ks.getKey(alias, password
  242. .toCharArray());
  243. // 构建签名
  244. Signature signature = Signature.getInstance(x509Certificate
  245. .getSigAlgName());
  246. signature.initSign(privateKey);
  247. signature.update(sign);
  248. return encryptBASE64(signature.sign());
  249. }
  250. /**
  251. * 验证签名
  252. *
  253. * @param data
  254. * @param sign
  255. * @param certificatePath
  256. * @return
  257. * @throws Exception
  258. */
  259. public static boolean verify(byte[] data, String sign,
  260. String certificatePath) throws Exception {
  261. // 获得证书
  262. X509Certificate x509Certificate = (X509Certificate) getCertificate(certificatePath);
  263. // 获得公钥
  264. PublicKey publicKey = x509Certificate.getPublicKey();
  265. // 构建签名
  266. Signature signature = Signature.getInstance(x509Certificate
  267. .getSigAlgName());
  268. signature.initVerify(publicKey);
  269. signature.update(data);
  270. return signature.verify(decryptBASE64(sign));
  271. }
  272. /**
  273. * 验证Certificate
  274. *
  275. * @param keyStorePath
  276. * @param alias
  277. * @param password
  278. * @return
  279. */
  280. public static boolean verifyCertificate(Date date, String keyStorePath,
  281. String alias, String password) {
  282. boolean status = true;
  283. try {
  284. Certificate certificate = getCertificate(keyStorePath, alias,
  285. password);
  286. status = verifyCertificate(date, certificate);
  287. } catch (Exception e) {
  288. status = false;
  289. }
  290. return status;
  291. }
  292. /**
  293. * 验证Certificate
  294. *
  295. * @param keyStorePath
  296. * @param alias
  297. * @param password
  298. * @return
  299. */
  300. public static boolean verifyCertificate(String keyStorePath, String alias,
  301. String password) {
  302. return verifyCertificate(new Date(), keyStorePath, alias, password);
  303. }
  304. /**
  305. * 获得SSLSocektFactory
  306. *
  307. * @param password
  308. *            密码
  309. * @param keyStorePath
  310. *            密钥库路径
  311. *
  312. * @param trustKeyStorePath
  313. *            信任库路径
  314. * @return
  315. * @throws Exception
  316. */
  317. private static SSLSocketFactory getSSLSocketFactory(String password,
  318. String keyStorePath, String trustKeyStorePath) throws Exception {
  319. // 初始化密钥库
  320. KeyManagerFactory keyManagerFactory = KeyManagerFactory
  321. .getInstance(SunX509);
  322. KeyStore keyStore = getKeyStore(keyStorePath, password);
  323. keyManagerFactory.init(keyStore, password.toCharArray());
  324. // 初始化信任库
  325. TrustManagerFactory trustManagerFactory = TrustManagerFactory
  326. .getInstance(SunX509);
  327. KeyStore trustkeyStore = getKeyStore(trustKeyStorePath, password);
  328. trustManagerFactory.init(trustkeyStore);
  329. // 初始化SSL上下文
  330. SSLContext ctx = SSLContext.getInstance(SSL);
  331. ctx.init(keyManagerFactory.getKeyManagers(), trustManagerFactory
  332. .getTrustManagers(), null);
  333. SSLSocketFactory sf = ctx.getSocketFactory();
  334. return sf;
  335. }
  336. /**
  337. * 为HttpsURLConnection配置SSLSocketFactory
  338. *
  339. * @param conn
  340. *            HttpsURLConnection
  341. * @param password
  342. *            密码
  343. * @param keyStorePath
  344. *            密钥库路径
  345. *
  346. * @param trustKeyStorePath
  347. *            信任库路径
  348. * @throws Exception
  349. */
  350. public static void configSSLSocketFactory(HttpsURLConnection conn,
  351. String password, String keyStorePath, String trustKeyStorePath)
  352. throws Exception {
  353. conn.setSSLSocketFactory(getSSLSocketFactory(password, keyStorePath,
  354. trustKeyStorePath));
  355. }
  356. }

增加了configSSLSocketFactory方法供外界调用,该方法为HttpsURLConnection配置了SSLSocketFactory。当HttpsURLConnection配置了SSLSocketFactory后,我们就可以通过HttpsURLConnection的getInputStream、getOutputStream,像往常使用HttpURLConnection做操作了。尤其要说明一点,未配置SSLSocketFactory前,HttpsURLConnection的getContentLength()获得值永远都是-1



给出相应测试类:

  1. import static org.junit.Assert.*;
  2. import java.io.DataInputStream;
  3. import java.io.InputStream;
  4. import java.net.URL;
  5. import javax.net.ssl.HttpsURLConnection;
  6. import org.junit.Test;
  7. /**
  8. *
  9. * @author 梁栋
  10. * @version 1.0
  11. * @since 1.0
  12. */
  13. public class CertificateCoderTest {
  14. private String password = "123456";
  15. private String alias = "www.zlex.org";
  16. private String certificatePath = "d:/zlex.cer";
  17. private String keyStorePath = "d:/zlex.keystore";
  18. private String clientKeyStorePath = "d:/zlex-client.keystore";
  19. private String clientPassword = "654321";
  20. @Test
  21. public void test() throws Exception {
  22. System.err.println("公钥加密——私钥解密");
  23. String inputStr = "Ceritifcate";
  24. byte[] data = inputStr.getBytes();
  25. byte[] encrypt = CertificateCoder.encryptByPublicKey(data,
  26. certificatePath);
  27. byte[] decrypt = CertificateCoder.decryptByPrivateKey(encrypt,
  28. keyStorePath, alias, password);
  29. String outputStr = new String(decrypt);
  30. System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);
  31. // 验证数据一致
  32. assertArrayEquals(data, decrypt);
  33. // 验证证书有效
  34. assertTrue(CertificateCoder.verifyCertificate(certificatePath));
  35. }
  36. @Test
  37. public void testSign() throws Exception {
  38. System.err.println("私钥加密——公钥解密");
  39. String inputStr = "sign";
  40. byte[] data = inputStr.getBytes();
  41. byte[] encodedData = CertificateCoder.encryptByPrivateKey(data,
  42. keyStorePath, alias, password);
  43. byte[] decodedData = CertificateCoder.decryptByPublicKey(encodedData,
  44. certificatePath);
  45. String outputStr = new String(decodedData);
  46. System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);
  47. assertEquals(inputStr, outputStr);
  48. System.err.println("私钥签名——公钥验证签名");
  49. // 产生签名
  50. String sign = CertificateCoder.sign(encodedData, keyStorePath, alias,
  51. password);
  52. System.err.println("签名:\r" + sign);
  53. // 验证签名
  54. boolean status = CertificateCoder.verify(encodedData, sign,
  55. certificatePath);
  56. System.err.println("状态:\r" + status);
  57. assertTrue(status);
  58. }
  59. @Test
  60. public void testHttps() throws Exception {
  61. URL url = new URL("https://www.zlex.org/examples/");
  62. HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
  63. conn.setDoInput(true);
  64. conn.setDoOutput(true);
  65. CertificateCoder.configSSLSocketFactory(conn, clientPassword,
  66. clientKeyStorePath, clientKeyStorePath);
  67. InputStream is = conn.getInputStream();
  68. int length = conn.getContentLength();
  69. DataInputStream dis = new DataInputStream(is);
  70. byte[] data = new byte[length];
  71. dis.readFully(data);
  72. dis.close();
  73. System.err.println(new String(data));
  74. conn.disconnect();
  75. }
  76. }

注意testHttps方法,几乎和我们往常做HTTP访问没有差别,我们来看控制台输出:

  1. <!--
  2. Licensed to the Apache Software Foundation (ASF) under one or more
  3. contributor license agreements.  See the NOTICE file distributed with
  4. this work for additional information regarding copyright ownership.
  5. The ASF licenses this file to You under the Apache License, Version 2.0
  6. (the "License"); you may not use this file except in compliance with
  7. the License.  You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. -->
  15. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
  16. <HTML><HEAD><TITLE>Apache Tomcat Examples</TITLE>
  17. <META http-equiv=Content-Type content="text/html">
  18. </HEAD>
  19. <BODY>
  20. <P>
  21. <H3>Apache Tomcat Examples</H3>
  22. <P></P>
  23. <ul>
  24. <li><a href="servlets">Servlets examples</a></li>
  25. <li><a href="jsp">JSP Examples</a></li>
  26. </ul>
  27. </BODY></HTML>

通过浏览器直接访问https://www.zlex.org/examples/你也会获得上述内容。也就是说应用甲方作为服务器构建tomcat服务,乙方可以通过上述方式访问甲方受保护的SSL应用,并且不需要考虑具体的加密解密问题。甲乙双方可以经过相应配置,通过双方的tomcat配置有效的SSL服务,简化上述代码实现,完全通过证书配置完成SSL双向认证!

ssl的更多相关文章

  1. 怎么让网站在本地支持SSL?

    打开vs,点击项目,查看属性,打开ssl 如果有什么危险提示,就允许 右击项目,选择属性 运行项目

  2. Could not create SSL connection through proxy serve-svn

    RA layer request failedsvn: Unable to connect to a repository at URL xxxxxx 最后:Could not create SSL ...

  3. 协议森林17 我和你的悄悄话 (SSL/TLS协议)

    作者:Vamei 出处:http://www.cnblogs.com/vamei 转载请先与我联系. TLS名为传输层安全协议(Transport Layer Protocol),这个协议是一套加密的 ...

  4. Paypal开发中遇到请求被中止: 未能创建 SSL/TLS 安全通道及解决方案

    最近在基于ASP.NET上开发了Paypal支付平台,在ASP.NET开发的过程中没有遇到这个问题,但是引用到MVC开发模式中的时候就出现了"未能创建 SSL/TLS 安全通道及解决方案&q ...

  5. 【原创】免费申请SSL证书【用于HTTPS,即是把网站从HTTP改为HTTPS,加密传输数据,保护敏感数据】

    今天公司有个网站需要改用https访问,所以就用到SSL证书.由于沃通(以前我是在这里申请的)暂停了免费的SSL证书之后,其网站推荐了新的一个网站来申请证书,所以,今天因为刚好又要申请一个证书,所以, ...

  6. 【微信小程序开发】之如何获取免费ssl证书【图文步骤】

    微信小程序要求所有网络请求都走ssl加密,因此我们开发服务端接口需要配置为https 这篇文章介绍一下如何 在 startssl 申请一个免费的ca证书. 1. 打开网站  https://www.s ...

  7. 免费SSL证书 之Let’s Encrypt申请与部署(Windows Nginx)

    我着着皇帝的新衣,但是你看不见    有一颗愿意等待的心,说明你对未来充满希望.有一颗充满希望的心,那么等待又算什么.人就是在等待与希望中度过,我们永远要对未来充满信心! 读在最前面: 1.本文案例为 ...

  8. Windows下Nginx配置SSL实现Https访问(包含证书生成)

    Vincent.李   Windows下Nginx配置SSL实现Https访问(包含证书生成) Windows下Nginx配置SSL实现Https访问(包含证书生成) 首先要说明为什么要实现https ...

  9. Jexus服务器SSL二级证书安装指南

    申请获得服务器证书有三张,一张服务器证书,二张中级CA证书.在Android微信中访问Https,如果服务器只有一张CA证书,就无法访问. 获取服务器证书中级CA证书: 为保障服务器证书在客户端的兼容 ...

  10. Netty5使用自签证书实现SSL安全连接

    这次使用的Netty是最新的5.0 Alpha2版本,下载地址是:http://dl.bintray.com/netty/downloads/netty-5.0.0.Alpha2.tar.bz2,发布 ...

随机推荐

  1. IOS开发基础知识碎片-导航

    1:IOS开发基础知识--碎片1 a:NSString与NSInteger的互换 b:Objective-c中集合里面不能存放基础类型,比如int string float等,只能把它们转化成对象才可 ...

  2. couchDB视图

    视图是设计文档的一部分. 视图函数 map函数 Map方法的参数只有一个,就是当前的文档对象.Map方法的实现需要根据文档对象的内容,确定是否要输出结果. 如果需要输出的话,可以通过emit来完成. ...

  3. Oracle update和order by

    今天遇到一个关于SQL转换成Oracle语句的问题,描述如下: select * from emp order by deptno; select * from dept; Sql Server: u ...

  4. 用css改变默认的checkbox样式

    自己常用的改变checkbox样式的两个方法: 一.利用background用图片代替checkbox效果 缺点:你首先得有一张好看的图片 优点:浏览器兼容性好 <!doctype html&g ...

  5. linux top命令结果参数详解

    非常详细的top结果说明文档. http://www.cnblogs.com/sbaicl/articles/2752068.html http://bbs.linuxtone.org/forum.p ...

  6. Unity性能优化(4)-官方教程Optimizing graphics rendering in Unity games翻译

    本文是Unity官方教程,性能优化系列的第四篇<Optimizing graphics rendering in Unity games>的翻译. 相关文章: Unity性能优化(1)-官 ...

  7. 有理数的稠密性(The rational points are dense on the number axis.)

    每一个实数都能用有理数去逼近到任意精确的程度,这就是有理数的稠密性.The rational points are dense on the number axis.

  8. 启用SQLite的Data Provider 运行WECOMPANYSITE时遇到ERROR CREATING CONTEXT 'SPRING.ROOT': ERROR THROWN BY A DEPENDENCY OF OBJECT 'SYSTEM.DATA.SQLITE'

    从网上下载的源码WeCompanySite,运行时报错 Error creating context 'spring.root': Error thrown by a dependency of ob ...

  9. [LeetCode] Maximum Size Subarray Sum Equals k 最大子数组之和为k

    Given an array nums and a target value k, find the maximum length of a subarray that sums to k. If t ...

  10. [LeetCode] Path Sum 二叉树的路径和

    Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up all ...