Viquillibres
cawikibooks
https://ca.wikibooks.org/wiki/Portada
MediaWiki 1.47.0-wmf.6
first-letter
Media
Especial
Discussió
Usuari
Usuari Discussió
Viquillibres
Viquillibres Discussió
Fitxer
Fitxer Discussió
MediaWiki
MediaWiki Discussió
Plantilla
Plantilla Discussió
Ajuda
Ajuda Discussió
Categoria
Categoria Discussió
Portal
Portal Discussió
Viquiprojecte
Viquiprojecte Discussió
TimedText
TimedText talk
Mòdul
Mòdul Discussió
Event
Event talk
Construcció d'Aplicacions de Programari Lliure en J2EE/Accés a bases de dades amb JDBC i seguretat amb SSL
0
2151
76656
73236
2026-06-15T08:53:29Z
Leptictidium
56
76656
wikitext
text/x-wiki
{{navegar|llibre=Construcció d'Aplicacions de Programari Lliure en J2EE
|actual=Accés a bases de dades amb JDBC i seguretat amb SSL
|anterior=HTTP, HTML, Servlets i Java Server Pages
|següent=Tècniques avançades: mapatge objecte-relacional, test unitaris i inversió de control
}}
==JDBC==
===Què és JDBC?===
JDBC és un conjunt de métodes i interficies que ens permeten accedir i manipular bases de dades. Gràcies a ells podem utilitzar codi SQL dins del codi Java. Podeu visitar la [http://java.sun.com/products/jdbc/ web oficial] o mirar el [http://java.sun.com/docs/books/tutorial/jdbc/index.html tutorial]
JDBC està pensat per a no tenir que patir sobre el funcionament del SGBD que volem utilitzar, ja que per a nosaltres, tot el procés de comunicació ens serà transparent. L'encarregat de entendres amb el SGBD serà el controlador (driver) específic de cada JDBC.
Existeixen 4 tipus de controladors:
#Bridging drivers
#Native API Partly Java Drivers
#Net Protocol All-Java Drivers
#Native-Protocol All-Java Drivers
===Ús de JDBC===
====Càrrega del controlador====
Per poder utilitzar el controlador del SGBD que hem escollit, primer hem de carregar el controlador. Per fer-ho s'utilitza el següent codi:
<pre>
try{
class.forName("org.postgresql.Driver");
}
catch(ClassNotFoundException ex){
//Codi de l'excepció
}
</pre>
El mètode <code>forname</code> carrega el controlador que correspon a la cadena que passem per paràmetres. En aquest cas estariam carregant el controlador del SGBD ''postgresql''.
Els noms del controladors dels diferents SGBD els podeu trobar en el següent enllaç: [http://www.newfire.com/newfire/html/doc/config/driver.html Noms controladors JDBC]
====Establir connexió====
Un cop carregat el controlador podem establir una connexió. Per poder-ho fer haurem de saber la URL (Universal Resource Locator).
La forma de definir la URL varia depenent del SGBD. Podeu veure diferents exemples en el següent enllaç: [http://www.newfire.com/newfire/html/doc/config/driver.html Formats de URL per a connexions JDBC]
Per obtenir la connexió farem us del mètode <tt>DriveMangaer.getConnection()</tt>. Aquest mètode ens retornara un objecte del tipus <tt>Connection</tt> que conté una connexió establerta amb la BD. Podem demanar la connexió especificant diferent nombre de paràmentres, però el més habitual és fer-ho així:
<pre>
Connection connexio = DriverManager.getConnection("URL","nomUsuari","clau d'accés");
</pre>
Per poder veure les diferents variants a l'hora de obtindre connexions podeu consultar l'[http://java.sun.com/j2se/1.4.2/docs/api/java/sql/DriverManager.html API]
Quan la connexió ja no sigui necessària hem de utilitzar el mètode <tt>close</tt> de l'objecte <tt>Connection</tt>per alliberar-la.
<pre>connexio.close()</pre>
====Accés a la BD====
Ara què disposem de l'objecte <tt>Connection</tt> podem utilitzar comandes SQL.
JDBC ens proporciona tres tipus d'objecte per aconseguir aquest proposit.
#Statement: Ens permet fer una consulta o manipulació de dades (SELECT, CREATE o UPDATE)
#PreparedStatement: Ens permet precompilar la comanda per a oferir millors prestacions
#CallableStatement: Ens permet cridar a procediments definits en la BD.
Per a obtenir aquests objectes ho farem desde l'objecte del tipus <tt>Connection</tt>
<pre>
Statement stmt = connexio.createStatement();
</pre>
Un cop creat el podem utilitzar per a executar commandes SQL dins la BD. Per fer consultes utilitzarem el mètode <tt>executeQuery</tt>
<pre>
ResultsetSet rs = stmt.executeQuery("SELECT * FROM TAULA");
</pre>
El paràmetre d'aquest mètode és una cadena que representa la consulta SQL.
El resultat el recollim dins d'un objecte ResultSet. Aquest tipus d'objecte el veurem en més detall en el següent apartat.
Disposem d'un altre mètode per a executar commandes que no retornen cap resultat (DELETE, UPDATE, ...).
<pre>
int nombreFilesAfectades = stmt.executeUpdate("DELETE FROM TAULA1 WHERE id like 'id-100%');
</pre>
El mètode <tt>executeUpdate</tt> ens retorna el nombre de files afectades per la commanda que li passem per paràmetre.
Si a priori no sabem si la nostra comanda ens ha de retornar un resultat, podem utilitzar el mètode <tt>execute</tt>
que ens retorna <tt>true</tt> si aquest té resultat i <tt>false</tt> en altre cas.
Per recollir el valor o el nombre de línies afectades ho farem amb els mètodes <tt>getResultSet()</tt> i <tt>getUpdateCount()</tt> respectivament.
En cas que necessitem executar la mateixa ordre amb diferents valors, podem fer ús del <tt>preparedStatement</tt>. Aquest també l'obtenim de l'objecte <tt>Connection</tt>
<pre>
PreparedStatement preStmt = connexio.prepareStatement("UPDATE TAULA1 SET CAMP1 = ? WHERE ID = ?");
</pre>
En aquest cas el mètode que ens proporciona el PreparedStatement té un pàrametre que és la commanda SQL. Aquesta commanda es enviada al controlador de la BD i precompilada. Ara hem assignar valor a aquells llocs on hem situat el caràcter <tt>?</tt>. Per fer-ho utilitzarem els mètodes que té el <tt>PreparedStatement</tt> per aquesta finalitat. Són del tipus <tt>setXXX(valor)</tt>. Per a saber amb quins mètodes compta pots visitar l'[http://java.sun.com/j2se/1.3/docs/api/java/sql/PreparedStatement.html API].
<pre>
preStmt.setInt(12);
preStmt.setString("id-1000");
</pre>
Ara resta executar la commanda:
<pre>
int nombreFilesAfectades = preStmt.executeUpdate();
</pre>
Per a cridar procediments ho farem mitjançant l'objecte <tt>CallableStatement</tt>. Aquest objecte conté una crida a un procediment definit a la BD com per exemple el següent:
<pre>
create procedure MOSTRA_TAULA as
SELECT
t1.CAMP1,
t2.CAMP2,
t2.CAMP3 cmp2
FROM
TAULA1 t1,
TAULA2 t2
WHERE
t1.CAMP1 = t2.CAMP2 and
t1.CAMP1='clau'
ORDER BY
t2.CAMP3
</pre>
La sintaxis del procediment canvia depenent del SGBD, alguns requereixen anar entre <tt>begin</tt> i <tt>end</tt>, etc.
Per crear el <tt>CallableStatement</tt> també ho farem a partir de l'objecte del tipus<tt>Connection</tt>.
Un cop crean només hem d'executar la commanda mitjançant els mètodes que vists amb anterioritat.
<pre>
CallableStatement callStmt = con.prepareCall("{call MOSTRA_TAULA}");
ResultSet rs = callStmt.executeQuery();
</pre>
Quan ja no necessitem els objectes de sentencia els hem de tancat mitjançant el mètode <tt>close</tt>
<pre>
stmt.close();
</pre>
====Resultats====
L'execució d'una commanda SQL ens retorna una taula accessible mitjançant un objecte del tipus ResultSet. Aquest objecte ens propociona un conjunt de mètodes i propietats per a llegir les dades.
El següent codi permet veure com accedim a les dades de dins del <tt>ResultSet</tt>:
<pre>
Statement stmt = connexio.createStatement();
ResultsetSet resultSet = stmt.executeQuery("SELECT * FROM TAULA");
while (resultSet.next()){
System.out.println("El id és: "+ resultSet.getString("ID"));
System.out.println("El valor del camp1 és: "+ resultSet.getInt("CAMP1"));
System.out.println("El valor del camp2 és: "+ resultSet.getString("CAMP2");
}
resultSet.close();
stmt.close();
</pre>
El mètode <tt>next()</tt> accedeix una a una a les fileres que contenen les dades. Els metodes <tt>getXXX("nom columna")</tt> ens retornen el valor de la columna especificada per paràmetre en la fila actual. Per veure els diferents tipus de retorn podeu consultar l'[http://java.sun.com/j2se/1.4.2/docs/api/java/sql/ResultSet.html API].
L'accés a les dades només es fa en una direcció. Per tant hem de processar les dades segons anem recorrent la taula. No és fins a la versió 2.0 de JDBC que ens permet despaçar-nos lliurement per tots els resultats.
Gràcies a aquestes millores comptem amb mètodes com:
#first: Ens porta a la primera fila
#absolute(número_de_fila): Ens porta a una fila específica.
#previous: Ens porta a la fila anterior.
...
És en aquesta versió on apareix també la possibilitat de actualitzar o inserir noves files.
<pre>
resultSet.absolute(100); //Ens posicionem a la fila nº100
resultSet.updateInt("CAMP1", 2); // Assigna a camp1 amb el valor sencer 2
resultSet.updateString("CAMP2","NOUVALOR"); //Assigna a camp2 amb el valor NOUVALOR
resultSet.updateRow(); // Actualitza amb els nous valors
</pre>
<pre>
resultSet.moveToInsertRow(); // Ens posiciona a una nova fila
resultSet.updateString("ID","id-100000"); //Assigna a ID el valor id-100000
resultSet.updateInt("CAMP1", 20); // Assigna a camp1 amb el valor sencer 20
resultSet.updateString("CAMP2","NOUVALOR"); //Assigna a camp2 amb el valor NOUVALOR
resultSet.insertRow(); //Afegeix la fila
resultSet.moveToCurrentRow(); //Ens desplaça a la fila actual
</pre>
Per defecte el <tt>ResultSet</tt> que ens retorna l'<tt>Statement</tt> no ens permet fer aquestes operacions. Aquestes habilitats les haurem d'especificar a l'hora de crear l'<tt>Statement</tt>.
<pre>
Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE);
</pre>
====Accés a les metadades====
Les metadades no són res més que informació referent al SGBD. Per obtenir aquestes dades tenim l'objecte anomenat <tt>DataBaseMetaData</tt>.
Aquest objecte el podem recollir a partir de l'objecte <tt>Connection</tt>
<pre>
DataBaseMetaData dbmd = connexio.getMetaData();
</pre>
Per a veure la informació que podeu obtenir mitjançant el <tt>BaseDataMetaData</tt> podeu consultar l[http://java.sun.com/j2se/1.4.2/docs/api/java/sql/DatabaseMetaData.html API]
A més a més de obtenir informació relativa al SGBD podem obtenir informació sobre els resultats d'una consulta.
Aquestes dades les obtenim mitjançant l'objecte <tt>ResultSetMetaData</tt>
<pre>
ResultSet resultSet = stmt.executeQuery(SELECT * from TAULA1);
ResultSetMetaData rsmd = resultSet.getMetaData();
if (rsmd.isReadOnly){
System.out.println("El ResultSet no és modificable");
}
</pre>
Per a veure la informació que podeu obtenir mitjançant el <tt>ResultSetMetaData</tt> podeu consultar l[http://java.sun.com/j2se/1.4.2/docs/api/java/sql/ResultSetMetaData.html API]
Tornar a la plana principal: [[Construcció d'Aplicacions de Programari Lliure en J2EE]]
===Connexions sobre Tomcat===
Si pretem utilitzar JDBC en una aplicació web podem utilitzar les capacitats de tomcat per poder gestionar connexions. Per fer-ho utilitzarem JNDI DataSource.
En el [http://tomcat.apache.org/tomcat-5.5-doc/jndi-datasource-examples-howto.html JNDI Datasource HOW-TO] podem veure en profunditat com funciona aquest sistema.
Seguirem el següent exemple:
====Configuració del tomcat====
Configurarem el tomcat perquè aquest sigui capaç de realitzar una connexio a una BD MYSQL.
#Afegirem el driver de MYSQL dins del directori <tt>common/lib</tt> del Tomcat
#Editarem el fitxer server.xml (Ho farem sobre una de les configuracions de servidor dins de Eclipse)
#Afegirem un nou contexte aquest ha d'anar dins del node <HOST>
<pre>
<Context path="/exempleJDBCdataSource" docBase="exempleJDBCdataSource"
debug="5" reloadable="true" crossContext="true">
<!-- maxActive: Maximum number of dB connections in pool. Make sure you
configure your mysqld max_connections large enough to handle
all of your db connections. Set to 0 for no limit.
-->
<!-- maxIdle: Maximum number of idle dB connections to retain in pool.
Set to -1 for no limit. See also the DBCP documentation on this
and the minEvictableIdleTimeMillis configuration parameter.
-->
<!-- maxWait: Maximum time to wait for a dB connection to become available
in ms, in this example 10 seconds. An Exception is thrown if
this timeout is exceeded. Set to -1 to wait indefinitely.
-->
<!-- username and password: MySQL dB username and password for dB connections -->
<!-- driverClassName: Class name for the old mm.mysql JDBC driver is
org.gjt.mm.mysql.Driver - we recommend using Connector/J though.
Class name for the official MySQL Connector/J driver is com.mysql.jdbc.Driver.
-->
<!-- url: The JDBC connection url for connecting to your MySQL dB.
The autoReconnect=true argument to the url makes sure that the
mm.mysql JDBC Driver will automatically reconnect if mysqld closed the
connection. mysqld by default closes idle connections after 8 hours.
-->
<Resource name="jdbc/provaConnexio" auth="Container" type="javax.sql.DataSource"
maxActive="100" maxIdle="30" maxWait="10000"
username="estiu" password="estiu" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/airline"/>
</Context>
</pre>
====Creació d'una aplicació web====
#Utilitzarem l'eclipsi per a crear un nou projecte web (Dynamic Web Project).
#Crearem un nou servlet el qual tindrà els mètodes init, doGet i doPost
#Crearem un objecte <tt>DataSource</tt>
#En el mètode ini solicitarem el DataSource a partir del contexte configurat al tomcat.
#En el metode doPost obtindrem una connexió a partir de l'objecte del tipus<tt>DataSource</tt>
#Llegirem dades de la BD
<pre>
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import javax.naming.Context;
import javax.naming.InitialContext;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/**
* Servlet implementation class for Servlet: provaConnexio
*
*/
public class provaConnexio extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {
/* (non-Java-doc)
* @see javax.servlet.http.HttpServlet#HttpServlet()
*/
private DataSource origenDades = null;
public provaConnexio() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
/* (non-Java-doc)
* @see javax.servlet.http.HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter out = response.getWriter();
try{
Connection connexio = origenDades.getConnection();
Statement stmt = connexio.createStatement();
ResultSet rs = stmt.executeQuery("Select * from bitllet");
while (rs.next()){
out.println("El bitllet " + rs.getString("id") + " es per al vol " + rs.getString("idVol"));
}
}catch(SQLException ex){
ex.printStackTrace();
}
}
/* (non-Javadoc)
* @see javax.servlet.GenericServlet#init()
*/
public void init() throws ServletException {
// TODO Auto-generated method stub
super.init();
try
{
// recuperamos el contexto inicial y la referencia a la fuente de datos
System.out.println("Executo l'init");
Context ctx = new InitialContext();
origenDades = (DataSource) ctx.lookup("java:comp/env/jdbc/provaConnexio");
}
catch (Exception e)
{
throw new ServletException("Imposible recuperar java:comp/env/jdbc/tutoriales",e);
}
}
}
</pre>
====Configuració de l'aplicació web====
Ens resta configurar el fitxer <tt>web.xml</tt> de l'aplicació.
Inclourem el següent codi dins del node <webapp> del .
<pre>
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>
exempleJDBCdataSource</display-name>
<resource-ref>
<description>DB Connection</description>
<res-ref-name>jdbc/provaConnexio</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
<servlet>
<description>
</description>
<display-name>
provaConnexio</display-name>
<servlet-name>provaConnexio</servlet-name>
<servlet-class>
provaConnexio</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>provaConnexio</servlet-name>
<url-pattern>/provaConnexio</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
</web-app>
</pre>
Ara la nostra connexió prodrà fer ús d'aquest recurs.
==Seguretat==
Les dades que circulen desde el servidor (Aplicació web) al client (Navegador) poden ser interceptades per una tercera persona si aquest té accés als paquets HTTP que circulen per la xarxa. Aquests paquets poden ser llegits i en poden extreure la informació.
En molts cops hem de fer circular dades secretes (claus d'accés, dades personals, etc). Per aquest motiu és necessari tenir un protocol per poder a poder fer connexions segures. Aquest és HTTPS (HTTP + SSL).
===SSL===
SSL (Secure Socket Layer) és un sistema de xifratge dissenyat per l'empresa Netscape Communications, que permet xifrar connexions. Es basa en un sistema criptogràfic asimètric i en el concepte dels certificats.
Per a més informació polseu [http://es.wikipedia.org/wiki/SSL aquí]
===SSL mitjançant Apache===
Aquesta configuració protegirà les aplicacions web mitjançant connexions SSL.
El primer pas serà configurar l'apache perquè redireccioni les peticions HTTPS del port 443 cap a el nostre port 8080 que és on tenim l'aplicació web.
Per defecte l'apache ja ve preparat i configurat per suportar SSL.
Per fer la unió utilitzarem els paràmetres ProxyPass i el ProxyPassReverse.
#Obrirem el fitxer <tt>/etc/httpd/cond.d/ssl.conf (Aquest fitxer descriu el host virtual protegit amb SSL)
#Inserirem les següents línies dins de l'àmbit del <tt>virtualhost</tt>
<pre>
ProxyPass / http://127.0.0.1:8080/
ProxyPassReverse / http://127.0.0.1:8008
</pre>
#Engegarem el apache <tt>/etc/init.d/httpd start</tt>
Amb això consguim que totes les peticions realitzades al port 443 les redireccioni al 8080.
Els certificats que utilitza Apache són els que venen per defecte en la instal·lació. Però modificant les línies d'aquest fitxer ho configuraríem perquè utilitzés els certificats propis.
===Tomcat amb SSL===
====Crear un certificat RSA per a Tomcat====
Per provar SSL en Tomcat necessitarem un certificat. El procediment normal seria generar una petició de certificat perquè una Autoritat de certificació ens signés la nostra clau pública i així poder demostrar que som qui diem que som.
El que farem nosaltres serà crear un certificat autosignat. És a dir no fem us dels serveis de cap (CA) per a que ens signi el certificat.
keytool crea parells de claus públiques y privadas, certificats autosignats, i gestiona magatzems de claus
<pre>
keytool -genkey -alias tomcat -keyalg RSA
</pre>
Aquesta comanda ens generarà un certificat autosignat per a que Tomcat el pugui fer servir. Ens demanarà un password, que per defecte es <tt>changeit</tt> i aleshores un serie de paràmetres del certificat.
<pre>
Enter keystore password: changeit
What is your first and last name?
[Unknown]: alex
What is the name of your organizational unit?
[Unknown]: Universitat d'estiu
What is the name of your organization?
[Unknown]: UdL
What is the name of your City or Locality?
[Unknown]: Lleida
What is the name of your State or Province?
[Unknown]: lleida
What is the two-letter country code for this unit?
[Unknown]: ES
Is CN="alex ", OU=Universitat d'estiu, O=UdL, L=Lleida, ST=lleida, C=ES correct?
[no]: yes
Enter key password for <tomcat>
(RETURN if same as keystore password):
</pre>
Aquest queda emmagatzemat en el fitxer <tt>.keystore</tt> dins del nostre <tt>$HOME</tt>
En el cas d'haver assignat un password diferent a l'hora entrar les dades del certificat, l'haurem d'especificar quan configurem el TOMCAT.
També és possible importar certificats generats amb OPENSSL:
Per a generar una petició i una nova clau.
<pre>
openssl req -new -out REQ.pem -keyout KEY.pem
</pre>
Per a generar un certificat x509 auto-signat desde una peticó de certidicat utilitzant la clau.
<pre>
openssl req -x509 -in REQ.pem -key KEY.pem -out CERT.pem
</pre>
Importa el certificat al <tt>keystore</tt>
<pre>
keytool -import -v -trustcacerts -alias tomcat -file CERT.pem
</pre>
====Configuració del Tomcat====
Falta dir-li a Tomcat que utilitzi SSL. Hem de configurar el fitxer <tt>PATH al TOMCAT/conf/server.xml</tt>
Aquí hem d'habilitar el següent connector
<pre>
<Connector port="8443" maxHttpHeaderSize="8192"
maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" disableUploadTimeout="true"
acceptCount="100" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS" />
</pre>
En el cas d'haver canviat el password del keystore afegiriam el paràmetre <tt>keystorePass</tt>.
Si el que hem canviat és la ubicació de del fitxer <tt>keystore</tt> ho especificarem mitjançant el paràmetre <tt>keystoreFile</tt>
Ex:
<pre>
<Connector port="8443" maxHttpHeaderSize="8192"
maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" disableUploadTimeout="true"
acceptCount="100" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS"
keystorePass="noupasswd" keystoreFile="/usr/local/keystores/keystore1"/>
</pre>
Per aprofundir en la qüestió podeu visitar la [http://tomcat.apache.org/tomcat-5.5-doc/ssl-howto.html HOW-TO] de Tomcat que parla de la configuració amb SSL
gl7tyy87iizxstquj37bl8609fvhxh3