给个关注?宝儿!
给个关注?宝儿!
给个关注?宝儿!
1 JDBC基础
JDBC(Java Database Connectivity)是Java提供对数据库进行连接、操作的标准API。Java自身并不会去实现对数据库的连接、查询、更新等操作而是通过抽象出数据库操作的API接口(JDBC),
不同的数据库提供商必须实现JDBC定义的接口从而也就实现了对数据库的一系列操作。
2 JDBCConnection
2.1连接
Java通过java.sql.DriverManager来管理所有数据库的驱动注册,所以如果想要建立数据库连接需要先在java.sql.DriverManager中注册对应的驱动类,然后调用getConnection方法才能连接上数据库。
JDBC定义了一个叫 java.sql.Driver 的接口类负责实现对数据库的连接,所有的数据库驱动包都必须实现这个接口才能完成数据库的连接、 **java.sql.DriverManager.getConnection(xx)**其实就是简介的调用了java.sql.Driver 类的connect 方法 实现数据库连接。 数据库连接成功会返回一个叫 java.sql.Connection 的数据库连接对象, 一切对数据库的查询操作都依赖这个 Connection 对象
JDBC连接数据库的一般步骤:
1 注册驱动,Class.forName("数据库驱动的类名")
2 获取连接, DriverManager.getConnetion(xxx)
JDBC连接数据库示例代码如下
String CLASS_NAME = "com.mysql.jdbc.Driver";
String URL = "jdbc:mysql://localhost:3306/mysql"
String USERNAME = "root";
String PASSWORD = "root";
Class.forName(CLASS_NAME);// 注册JDBC驱动类
Connection connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);
2.2 数据库配置
传统的Web应用的数据库配置信息一般都是存放在WEB-INF目录下的*.properties、.yml、.xml中的,如果是Spring Boot项目的话一般都会存储在jar包中的src/main/resources/目录下。常见的存储数据库配置信息的文件路径如:WEB-INF/applicationContext.xml、WEB-INF/hibernate.cfg.xml、WEB-INF/jdbc/jdbc.properties,一般情况下使用find命令加关键字可以轻松的找出来,如查找Mysql配置信息: find 路径 -type f |xargs grep “com.mysql.jdbc.Driver”。
为什么需要Class.forName?
很多人不理解为什么第一步必须是Class.forName(CLASS_NAME);// 注册JDBC驱动类,因为他们永远不会跟进驱动包去一探究竟。
实际上这一步是利用了Java反射+类加载机制往DriverManager中注册了驱动包!
Class.forName(“com.mysql.jdbc.Driver”)实际上会触发类加载,com.mysql.jdbc.Driver类将会被初始化,所以static静态语句块中的代码也将会被执行,所以看似毫无必要的Class.forName其实也是暗藏玄机的。如果反射某个类又不想初始化类方法有两种途径:
1.
使用Class.forName(“xxxx”, false, loader)方法,将第二个参数传入false。
2.
ClassLoader.load(“xxxx”);
Class.forName可以省去吗?
连接数据库就必须Class.forName(xxx)几乎已经成为了绝大部分人认为的既定事实而不可改变,但是某些人会发现删除Class.forName一样可以连接数据库这又作何解释?
实际上这里又利用了Java的一大特性:Java SPI(Service Provider Interface),因为DriverManager在初始化的时候会调用java.util.ServiceLoader类提供的SPI机制,Java会自动扫描jar包中的META-INF/services目录下的文件,并且还会自动的Class.forName(文件中定义的类),这也就解释了为什么不需要Class.forName也能够成功连接数据库的原因了。
Mysql驱动包示例:
2.3 JDBC数据库连接总结
使用JDBC连接数据相对于PHP直接使用mysql_connect/mysqli_connect函数就可以完成数据库连接来说的确难了很多,但是其中也暗藏了很多Java的特性需要我们去深入理解。
或许您会有所疑问我们为什么非要搞明白Class.forName这个问题?这个问题和Java安全有必然的联系吗?其实这里只是想让大家明白Java反射、类加载机制、和SPI机制以及养成阅读JDK或者第三方库代码的习惯,也希望不明白上述机制的朋友深入去理解思考下。
学习完本节后希望您能去思考如下问题:
1.
SPI机制是否有安全性问题?
2.
Java反射有那些安全问题?
3.
Java类加载机制是什么?
4.
数据库连接时密码安全问题?
5.
使用JDBC如何写一个通用的数据库密码爆破模块?
3 Datasource 数据源
3.1. DataSource
在真实的Java项目中通常不会使用原生的JDBC的DriverManager去连接数据库,而是使用数据源(javax.sql.DataSource)来代替DriverManager管理数据库的连接。一般情况下在Web服务启动时候会预先定义好数据源,有了数据源程序就不再需要编写任何数据库连接相关的代码了,直接引用DataSource对象即可获取数据库连接了。
常见的数据源有:DBCP、C3P0、Druid、Mybatis DataSource,他们都实现于javax.sql.DataSource接口。
3.2 Spring MVC 数据源
在Spring MVC中我们可以自由的选择第三方数据源,通常我们会定义一个DataSource Bean用于配置和初始化数据源对象,然后在Spring中就可以通过Bean注入的方式获取数据源对象了。
在基于XML配置的SpringMVC中配置数据源:
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
....
/>
如上,我们定义了一个id为dataSource的Spring Bean对象,username和password都使用了 j d b c . X X X 表示,很明显 {jdbc.XXX}表示,很明显 jdbc.XXX表示,很明显{jdbc.username}并不是数据库的用户名,这其实是采用了Spring的property-placeholder制定了一个properties文件,使用${jdbc.username}其实会自动自定义的properties配置文件中的配置信息。
<context:property-placeholder location="classpath:/config/jdbc.properties"/>
jdbc.properties内容:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mysql?autoReconnect=true&zeroDateTimeBehavior=round&useUnicode=true&characterEncoding=UTF-8&useOldAliasMetadataBehavior=true&useOldAliasMetadataBehavior=true&useSSL=false
jdbc.username=root
jdbc.password=root
在Spring中我们只需要通过引用这个Bean就可以获取到数据源了,比如在Spring JDBC中通过注入数据源(ref=“dataSource”)就可以获取到上面定义的dataSource。
<!-- jdbcTemplate Spring JDBC 模版 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" abstract="false" lazy-init="false">
<property name="dataSource" ref="dataSource"/>
</bean>
SpringBoot配置数据源:
在SpringBoot中只需要在application.properties或application.yml中定义spring.datasource.xxx即可完成DataSource配置。
spring.datasource.url=jdbc:mysql://localhost:3306/mysql?autoReconnect=true&zeroDateTimeBehavior=round&useUnicode=true&characterEncoding=UTF-8&useOldAliasMetadataBehavior=true&useOldAliasMetadataBehavior=true&useSSL=false
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
Spring 数据源Hack
我们通常可以通过查找Spring数据库配置信息找到数据库账号密码,但是很多时候我们可能会找到非常多的配置项甚至是加密的配置信息,这将会让我们非常的难以确定真实的数据库配置信息。某些时候在授权渗透测试的情况下我们可能会需要传个shell尝试性的连接下数据库(高危操作,请勿违法!)证明下危害,那么您可以在webshell中使用注入数据源的方式来获取数据库连接对象,甚至是读取数据库密码(切记不要未经用户授权违规操作!)。
spring-datasource.jsp获取数据源/执行SQL语句示例
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="org.springframework.context.ApplicationContext" %>
<%@ page import="org.springframework.web.context.support.WebApplicationContextUtils" %>
<%@ page import="javax.sql.DataSource" %>
<%@ page import="java.sql.Connection" %>
<%@ page import="java.sql.PreparedStatement" %>
<%@ page import="java.sql.ResultSet" %>
<%@ page import="java.sql.ResultSetMetaData" %>
<%@ page import="java.util.List" %>
<%@ page import="java.util.ArrayList" %>
<%@ page import=