30. Januar 2012

XML Dokumente mit SQL vergleichen: XMLDIFF und XMLPATCH

Comparing XML with SQL: XMLDIFF and XMLPATCH
Heute möchte ich etwas über zwei ganz interessante SQL-Funktionen in der Datenbank schreiben: XMLDIFF und XMLPATCH. Fangen wir mit XMLDIFF an: Es vergleicht XML-Dokumente. Und dazu am besten mal ein Beispiel ...
<document xmlns="my-namespace">
 <leeres-xmltag></leeres-xmltag>
 <xmltag-inhalt>"</xmltag-inhalt>
</document>

<ns:document xmlns:ns="my-namespace">
 <ns:leeres-xmltag/>
 <ns:xmltag-inhalt>&quot;</ns:xmltag-inhalt>
</ns:document> 
Die Frage "Worin unterscheiden sich die beiden XML-Dokumente?" ist nur auf den ersten Blick einfach. Probieren wir die XMLDIFF-Funktion einfach mal aus ...
select xmldiff(
 xmltype('
  <document xmlns="my-namespace">
   <leeres-xmltag></leeres-xmltag>
   <xmltag-inhalt>"</xmltag-inhalt>
  </document>'
 ), xmltype('
  <ns:document xmlns:ns="my-namespace">
   <ns:leeres-xmltag/>
   <ns:xmltag-inhalt>&quot;</ns:xmltag-inhalt>
  </ns:document>'
 )
) from dual
/ 

XMLDIFF(XMLTYPE('<DOCUMENTXMLNS="MY-NAMESPACE"><LEERES-XMLTAG></LEERES-XMLTAG><X
--------------------------------------------------------------------------------
<xd:xdiff xsi:schemaLocation="http://xmlns.oracle.com/xdb/xdiff.xsd http://xmlns
.oracle.com/xdb/xdiff.xsd" xmlns:xd="http://xmlns.oracle.com/xdb/xdiff.xsd" xmln
s:ns="my-namespace" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <?oracle-xmldiff operations-in-docorder="true" output-model="snapshot" diff-al
gorithm="global"?>
</xd:xdiff>
Die XMLDIFF-Funktion liefert zwar etwas zurück, bei genauer Betrachtung stellt man jedoch fest, dass das Tag xd:xdiff leer ist - es wurden also keine Unterschiede zwischen den XML-Dokumenten festgestellt - und das ist auch richtig so. Denn die im Text sichtbaren Unterschiede haben nach dem XML-Standard keine Bedeutung. Ein leeres Tag darf sowohl <tag></tag> als auch <tag/> geschrieben werden. Gleiches gilt für Zeichenentities oder Namensräume. XMLDIFF ist also zunächst eine sehr interessante Funktion, um überhaupt Unterschiede zwischen XML-Dokumenten zu erkennen. Doch XMLDIFF leistet noch mehr: Nehmen wir mal zwei XML-Dokumente, die sich tatsächlich unterscheiden ...
select xmldiff(
 xmltype('
  <document xmlns="my-namespace">
   <tag>Text 1</tag>
   <tag>Text 3</tag>
  </document>'
 ), xmltype('
  <ns:document xmlns:ns="my-namespace">
   <ns:tag>Text 1</ns:tag>
   <ns:tag>Text 2</ns:tag>
   <ns:tag option="a">Text 3</ns:tag>
  </ns:document>'
 )
) as xml_diff from dual
/ 

XML_DIFF
------------------------------------------------------------------------------------------
<xd:xdiff xsi:schemaLocation="http://xmlns.oracle.com/xdb/xdiff.xsd http://xmlns.oracle.co
m/xdb/xdiff.xsd" xmlns:xd="http://xmlns.oracle.com/xdb/xdiff.xsd" xmlns:ns="my-namespace"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <?oracle-xmldiff operations-in-docorder="true" output-model="snapshot" diff-algorithm="g
lobal"?>
  <xd:update-node xd:node-type="text" xd:xpath="/ns:document[1]/ns:tag[2]/text()[1]">
    <xd:content>Text 2</xd:content>
  </xd:update-node>
  <xd:append-node xd:node-type="element" xd:parent-xpath="/ns:document[1]">
    <xd:content>
      <ns:tag option="a">Text 3</ns:tag>
    </xd:content>
  </xd:append-node>
</xd:xdiff>
Wie man sehen kann, werden die Unterschiede nicht einfach nur aufgelistet: Wie sein Unix-Vorbild diff liefert auch XMLDIFF Anweisungen zurück, wie man das erste XML-Dokument in das zweite überführen kann. Und ebenso wie das Ergebnis eines Unix diff mit dem patch Werkzeug auf andere XML-Dokumente angewendet werden kann, kann auch das Ergebnis von XMLDIFF auf ein neues XML-Dokument mit Hilfe von XMLPATCH angewendet werden.
with diff as (
  -- Obiges SELECT XMLDIFF hier ...
)
select xmlpatch(
 xmltype('
  <document xmlns="my-namespace">
   <tag>Text a</tag>
   <tag>Text b</tag>
  </document>'
 ),
 xml_diff
) from diff
/ 
Die zuerst ermittelten XMLDIFF-Anweisungen ...
  • Ändere den Text des zweiten XML-Tags tag innerhalb von document auf Text 2
  • Hänge innerhalb des Tags document ein neues Tag tag mit dem Inhalt Text 3 und dem Attribut option="a" an.
... werden auf das im zweiten Parameter übergebene XML-Dokument angewendet. Das Ergebnis sieht in etwa wie folgt aus (die durch XMLPATCH ausgelösten Änderungen sind rot markiert):
XMLPATCH
------------------------------------------------------------------------------------------
<document xmlns="my-namespace">
  <tag>Text a</tag>
  <tag>Text 2</tag>
  <tag xmlns:ns="my-namespace" option="a">Text 3</tag>
</document>
Intern nutzt Oracle die Funktionen auch selbst - speziell für die in Place Schema Evolution der XML DB sind sie wichtig. Allerdings kann man sie auch selbst nutzen, wenn es darum geht, Änderungen an einem XML-Dokument zu erkennen und diese ggfs. auf andere Dokumente zu übertragen ...
Today I'd like to write something about two interesting SQL functions in the Oracle database: XMLDIFF and XMLPATCH. I'll start with XMLDIFF : As its name indicates - it compares XML documents. I'll illustrate this with an example ...
<document xmlns="my-namespace">
 <empty-xmltag></empty-xmltag>
 <xmltag-content>"</xmltag-content>
</document>

<ns:document xmlns:ns="my-namespace">
 <ns:empty-xmltag/>
 <ns:xmltag-content>&quot;</ns:xmltag-content>
</ns:document> 
What do you think? Are these XML documents equal ... or do they differ? This seems to be easy only at the first glance. But let's have XMLDIFF do the work ...
select xmldiff(
 xmltype('
  <document xmlns="my-namespace">
   <empty-xmltag></empty-xmltag>
   <xmltag-content>"</xmltag-content>
  </document>'
 ), xmltype('
  <ns:document xmlns:ns="my-namespace">
   <ns:empty-xmltag/>
   <ns:xmltag-content>&quot;</ns:xmltag-content>
  </ns:document>'
 )
) from dual
/ 

XMLDIFF(XMLTYPE('<DOCUMENTXMLNS="MY-NAMESPACE"><LEERES-XMLTAG></LEERES-XMLTAG><X
--------------------------------------------------------------------------------
<xd:xdiff xsi:schemaLocation="http://xmlns.oracle.com/xdb/xdiff.xsd http://xmlns
.oracle.com/xdb/xdiff.xsd" xmlns:xd="http://xmlns.oracle.com/xdb/xdiff.xsd" xmln
s:ns="my-namespace" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <?oracle-xmldiff operations-in-docorder="true" output-model="snapshot" diff-al
gorithm="global"?>
</xd:xdiff>
The functions' output indicates that the documents are equal - from the XML point of view. And this makes sense since the XML standard allows different markup for the same content. Empty tags, for instance, can be expressed as a single tag with a trailing slash (<tag/>) as well as with an opening and a closing tag (<tag></tag>). Character entities like &quot; have the same meaning as the character itself ("). And there are more examples beyond these two. So comparing XML documents means more than just comparing the text contents - the XML standard must be kept in mind, and XMLDIFF does exactly this. In the next example we'll compare two documents which are different; also from the "XML point of view".
select xmldiff(
 xmltype('
  <document xmlns="my-namespace">
   <tag>Text 1</tag>
   <tag>Text 3</tag>
  </document>'
 ), xmltype('
  <ns:document xmlns:ns="my-namespace">
   <ns:tag>Text 1</ns:tag>
   <ns:tag>Text 2</ns:tag>
   <ns:tag option="a">Text 3</ns:tag>
  </ns:document>'
 )
) as xml_diff from dual
/ 

XML_DIFF
------------------------------------------------------------------------------------------
<xd:xdiff xsi:schemaLocation="http://xmlns.oracle.com/xdb/xdiff.xsd http://xmlns.oracle.co
m/xdb/xdiff.xsd" xmlns:xd="http://xmlns.oracle.com/xdb/xdiff.xsd" xmlns:ns="my-namespace"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <?oracle-xmldiff operations-in-docorder="true" output-model="snapshot" diff-algorithm="g
lobal"?>
  <xd:update-node xd:node-type="text" xd:xpath="/ns:document[1]/ns:tag[2]/text()[1]">
    <xd:content>Text 2</xd:content>
  </xd:update-node>
  <xd:append-node xd:node-type="element" xd:parent-xpath="/ns:document[1]">
    <xd:content>
      <ns:tag option="a">Text 3</ns:tag>
    </xd:content>
  </xd:append-node>
</xd:xdiff>
We can see, that XMLDIFF returns more than just the information that these two documents are different. As it's UNIX pendant diff it returns a "delta" - instructions how to modify the first document in order to get the second. This output can be consumed by XMLPATCH; as we'll see in the next example: We'll apply the "patch instruction" to another XML document ...
with diff as (
  -- Obiges SELECT XMLDIFF hier ...
)
select xmlpatch(
 xmltype('
  <document xmlns="my-namespace">
   <tag>Text a</tag>
   <tag>Text b</tag>
  </document>'
 ),
 xml_diff
) from diff
/ 
The "patching" instructions ...
  • Modify the text contents of the second tag tag within document to Text 2
  • Append another tag tag containing the text Text 3 and the attrbute option="a" within document.
... are now being applied to another XML document which is (in this case) even different from the very first one (which we used to compare initially). The output of the XMLPATCH operation is as follows ...
XMLPATCH
------------------------------------------------------------------------------------------
<document xmlns="my-namespace">
  <tag>Text a</tag>
  <tag>Text 2</tag>
  <tag xmlns:ns="my-namespace" option="a">Text 3</tag>
</document>
XMLDIFF and XMLPATCH are used internally by Oracle for the XML DB functionality of In Place XML Schema Evolution. But one can (as we have seen) use them also for own purposes - the functions are interesting when it's about comparing XML documents and computing deltas between them. einem XML-Dokument zu erkennen und diese ggfs. auf andere Dokumente zu übertragen ...

9. Januar 2012

Login als User "A" - mit dem Password von "B": Proxy Authentication!

Log in as user "A" - with the password of "B": Proxy Authentication
Es ist in der Tat möglich: Ich kann mich an der Oracle-Datenbank als User "A" mit dem Passwort des Benutzers "B" anmelden - und das ist keine Sicherheitslücke. Ich möchte dieses Blog-Posting dem Thema Proxy Authentication widmen. Das kann man ganz besonders in einer dreischichtigen Webarchitektur gebrauchen. Denn dort werden Datenbankverbindungen nahezu immer statisch im Application Server konfiguriert ( data-sources.xml). Wenn man in einer solchem Umgebung mit den Nutzerkonten der Datenbank arbeiten möchte, hat man ein Problem: Man kann sie nicht alle in der data-sources.xml einrichten; erstmal sind es oft zu viele und zweitens kann man die data-sources.xml nicht so einfach im laufenden Betrieb ändern. Die Lösung ist Proxy Authentication: Dabei wird die Datenbankverbidnung im Application Server fest mit einem technischen User und einem Passwort eingerichtet; der tatsächliche Datenbankuser, nach dem sich auch die Privilegien richten, wird zur Laufzeit festgelegt. Und das ganze funktioniert wie folgt: Zuerst brauchen wir einen technischen User TECHUSER (mit Passwort TECHUSER) und zwei "echte" User (REALUSER1 und REALUSER2).
create user techuser identified by techuser
/

grant connect to techuser
/

reate user realuser1 identified by realuser1
/

grant connect, resource to realuser1
/

reate user realuser2 identified by realuser2
/

grant connect, resource to realuser2
/
Und damit die User in einer Anwendung unterscheidbar werden, bekommen Sie nun eine Kopie der EMP-Tabelle, aber mit unterschiedlichen Inhalten ...
craete table realuser1.emp as select * from scott.emp where deptno = 10
/

craete table realuser2.emp as select * from scott.emp where deptno = 20
/
Und jetzt geht es los: Zuerst muss man der Datenbank sagen, dass TECHUSER die Erlaubnis bekommt, sich mit seinem eigenen Passwort als REALUSER1 oder REALUSER2 "auszugeben". Man kann auch sagen: TECHUSER wird der Proxy User für die Clients REALUSER1 und REALUSER2.
alter user realuser1 grant connect through techuser
/

alter user realuser2 grant connect through techuser
/
Der Setup lässt sich in der Dictionary View PROXY_USERS auch überprüfen:
SQL> select * from proxy_users;

PROXY           CLIENT          AUT FLAGS
--------------- --------------- --- -----------------------------------
TECHUSER        REALUSER1       NO  PROXY MAY ACTIVATE ALL CLIENT ROLES
TECHUSER        REALUSER2       NO  PROXY MAY ACTIVATE ALL CLIENT ROLES
Mit SQL*Plus kann man das jetzt schon ausprobieren:
D:\>sqlplus.exe techuser[realuser1]/techuser

SQL*Plus: Release 11.1.0.6.0 - Production on Do Dez 22 16:17:30 2011

Copyright (c) 1982, 2007, Oracle.  All rights reserved.


Verbunden mit:
Oracle Database 11g Enterprise Edition Release 11.2.0.2.0 - Production
With the Partitioning, Oracle Label Security, OLAP, Data Mining
and Real Application Testing options

SQL> select user from dual;

USER
------------------------------
REALUSER1
Man sieht sehr schön, dass Username und Passwort des TECHUSER zum Login verwendet werden; in eckigen Klammern wird aber festgelegt, für wen die Datenbankverbindung eigentlich aufgemacht werden soll.
So weit, so gut, so einfach. Aber für eine dreischichtige Applikation nutzt das bis jetzt noch gar nichts. Schließlich kann man nicht einfach den "echten" User in eckige Klammern in die Definition der Datenbankverbindung eintragen, denn diese ist ja statisch. Der User, für den der Connect gelten soll, soll sich aber beliebig ändern können - also dynamisch sein. Also müssen wir in der Lage sein, den Usernamen, den man bei SQL*Plus in eckige Klammern setzt, per Java-Code zu setzen ... Schauen wir uns zunächst ein kleines Java-Testprogramm an: Es gibt zuerst den Namen des angemeldeten Users aus ( select user from dual) und dann die Inhalte der EMP Tabelle. Achtet darauf, dass Username und Passwort des TECHUSER hart kodiert sind - und das wird so bleiben!
import java.sql.*;
import oracle.jdbc.*;
import java.io.*;
import java.util.*;

public class proxyConnect {
  public static void main(String args[]) throws Exception {
    DriverManager.registerDriver(new oracle.jdbc.OracleDriver());
    Connection con = DriverManager.getConnection("jdbc:oracle:thin:@sccloud030:1521/orcl","techuser","techuser");
    
/*
 * Code zum Setzen des Client-Usernamens HIER !!!
 */

    Statement stmt = null;
    ResultSet rs1 = null;
    stmt = con.createStatement();
    rs1 = stmt.executeQuery("select user from dual");
    while (rs1.next()) {
      System.out.println("Database userid: " + rs1.getString(1));
    }  
    rs1.close();
    stmt.close();

    stmt = con.createStatement();
    rs1 = stmt.executeQuery("select * from emp");
    while (rs1.next()) {
      System.out.println("EMPNO: " + rs1.getString(1) + " ["+rs1.getString(2)+"] - DEPTNO: "+rs1.getString("DEPTNO"));
    }  
    rs1.close();
    stmt.close();
    con.close();
  }
} 
Beim ersten Test sagt uns das Programm nur, dass wir als TECHUSER verbunden sind und dass es keine EMP-Tabelle gibt ...
D:\> java.exe proxyConnect realuser1
Database userid: TECHUSER
Exception in thread "main" java.sql.SQLSyntaxErrorException: 
ORA-00942: Tabelle oder View nicht vorhanden
        at oracle.jdbc.driver.SQLStateMapping.newSQLException(SQLStateMapping.java:91)
        at oracle.jdbc.driver.DatabaseError.newSQLException(DatabaseError.java:112)
        at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:173)
        :
Dann bauen wir jetzt den Code ein, der den "richtigen" User setzt. Tauscht die Zeilen ...
/*
 * Code zum Setzen des Client-Usernamens HIER !!!
 */
... durch diese hier aus: Als Usernamen nehmen wir den ersten Parameter der Kommandozeile.
    Properties props = new Properties();
    props.put("PROXY_USER_NAME", args[0]);
    ((OracleConnection)con).openProxySession(OracleConnection.PROXYTYPE_USER_NAME, props);  
Neu kompilieren und wieder ausführen. Und .. voilá:
D:\> java proxyConnect realuser1
Database userid: REALUSER1
EMPNO: 7782 [CLARK] - DEPTNO: 10
EMPNO: 7839 [KING] - DEPTNO: 10
EMPNO: 7934 [MILLER] - DEPTNO: 10

D:\> java proxyConnect realuser2
Database userid: REALUSER2
EMPNO: 7369 [SMITH] - DEPTNO: 20
EMPNO: 7566 [JONES] - DEPTNO: 20
EMPNO: 7788 [SCOTT] - DEPTNO: 20
EMPNO: 7876 [ADAMS] - DEPTNO: 20
EMPNO: 7902 [FORD] - DEPTNO: 20
Nur durch die Angabe des Namens wird nun festgelegt, als welcher User die Datenbanksession laufen soll. Damit kann ein Java-Programm (bspw. in einem Application Server) mit einer statischen Datasource-Definition dennoch dynamisch den Datenbankuser wechseln. Denn eine bestehende Proxy-Verbindung kann geschlossen werden, um danach auf der gleichen "physikalischen" Datenbankverbindung eine neue Proxy-Verbindung für einen anderen User zu öffnen. Im folgenden Code habe ich das mal illustriert - zur besseren Übersicht habe ich die Datenbankaktionen entfernt und gegen den Pseudocall auf erledigeDatenbankaktionen() ausgetauscht.
  public static void main(String args[]) throws Exception {
    // Physikalische Verbindung öffnen als TECHUSER
    DriverManager.registerDriver(new oracle.jdbc.OracleDriver());
    Connection con = DriverManager.getConnection("jdbc:oracle:thin:@sccloud030:1521/orcl","techuser","techuser");
    
    // Proxy-Verbindung für User REALUSER1 öffnen, etwas tun und schließen
    Properties props = new Properties();
    props.put("PROXY_USER_NAME", "realuser1");
    ((OracleConnection)con).openProxySession(OracleConnection.PROXYTYPE_USER_NAME, props);  
    erledigeDatenbankaktionen(con);
    ((OracleConnection)con).close(OracleConnection.PROXY_SESSION); 

    // Die physikalische Verbindung ist immer noch offen ...
    // Proxy-Verbindung für User REALUSER2 öffnen, etwas tun und schließen
    props.put("PROXY_USER_NAME", "realuser2");
    ((OracleConnection)con).openProxySession(OracleConnection.PROXYTYPE_USER_NAME, props);  
    erledigeDatenbankaktionen(con);
    ((OracleConnection)con).close(OracleConnection.PROXY_SESSION); 

    // Physikalische Verbindung schließen
    con.close();
  }
Beim Umstieg von Client/Server- zu Webanwendungen ist ein solches Vorgehen hochinteressant.
Wichtig ist, dass REALUSER1 bzw. REALUSER2 immer noch das CREATE SESSION-Privileg brauchen; auch darf der Account nicht gelockt sein. Es ist aber durchaus möglich, direkte Connects zu unterbinden - dazu muss man "nur" das Passwort auf einen nicht ermittelbaren Wert setzen ...
SQL> alter user realuser1 identified by values 'Hallo'
Nun wird der Text "Hallo" als Hashwert in die Oracle-Passworttabelle geschrieben - ein Connect als REALUSER1 ist nun theoretisch möglich: wenn man das Passwort herausbekommt, dessen Hashwert "Hallo" ergibt. Defacto jedoch kann man sich nur noch über den TECHUSER als REALUSER1 anmelden. Aber auch ein Parallelbetrieb mit direkten und Proxy-Verbindungen ist natürlich problemlos machbar.
Mehr zur Proxy Authentication in der Dokumentation: Dazu sind zwei Links wichtig:
Yes - it is possible: You can connect to the Oracle database as say: User "A" with the password of User "B" - and that is not a security vulnerability. In this blog posting I'd like to elaborate a bit on proxy authentication - this feature is particular useful in three-tier-applications. Database connections are typically part of the static application server configuration ("data-sources.xml"). And when such a java environments want to use the user accounts in the database there is a problem: We cannot add each individual user to the datasource configuration. The first arguments is, that there might be just too many users - the second one is that changes to data-sources.xml are often not possible without downtime. So we need a connection using one single technical user's credentials and the ability to set the desired username at runtime. And that is all what proxy authentication is about. We'll start with creating the technical user (TECHUSER) and two "real" user accounts (REALUSER1 and REALUSER2).
create user techuser identified by techuser
/

grant connect to techuser
/

reate user realuser1 identified by realuser1
/

grant connect, resource to realuser1
/

reate user realuser2 identified by realuser2
/

grant connect, resource to realuser2
/
REALUSER1 and REALUSER2 now get a copy of the EMP table with different contents: we want to differentiate between the two later on.
craete table realuser1.emp as select * from scott.emp where deptno = 10
/

craete table realuser2.emp as select * from scott.emp where deptno = 20
/
Now we declare TECHUSER as the proxy user for REALUSER1 and REALUSER2. This is kind of a GRANT statement. The privilege to act as (the clients) REALUSER1 or REALUSER2 is granted to TECHUSER.
alter user realuser1 grant connect through techuser
/

alter user realuser2 grant connect through techuser
/
You might review this in the dictionary view PROXY_USERS:
SQL> select * from proxy_users;

PROXY           CLIENT          AUT FLAGS
--------------- --------------- --- -----------------------------------
TECHUSER        REALUSER1       NO  PROXY MAY ACTIVATE ALL CLIENT ROLES
TECHUSER        REALUSER2       NO  PROXY MAY ACTIVATE ALL CLIENT ROLES
A first test with SQL*Plus:
D:\>sqlplus.exe techuser[realuser1]/techuser

SQL*Plus: Release 11.1.0.6.0 - Production on Do Dez 22 16:17:30 2011

Copyright (c) 1982, 2007, Oracle.  All rights reserved.


Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.2.0 - Production
With the Partitioning, Oracle Label Security, OLAP, Data Mining
and Real Application Testing options

SQL> select user from dual;

USER
------------------------------
REALUSER1
TECHUSER used his own credentials to log in. But the user as which the connection should be established was provided within the brackets. In SQL*Plus it is that easy: We logged in as TECHUSER/techuser and we now are REALUSER1.
So far - so good. But for real world environments this is virtually useless - no end user connects with SQL*Plus. And we cannot add the brackets to the static datasource definition in the application server: we need to set the client user dynamically with some code - and the following example will show how to do this. First I have a little testing program in java language. It first connects to the database with hardcoded TECHUSER credentials, then it looks up "as who" it is connected and finally it shows the contents of the EMP table. Again: The username and password arguments in the call to DriverManager.getConnection are hard coded and this will not change!
import java.sql.*;
import oracle.jdbc.*;
import java.io.*;
import java.util.*;

public class proxyConnect {
  public static void main(String args[]) throws Exception {
    DriverManager.registerDriver(new oracle.jdbc.OracleDriver());
    Connection con = DriverManager.getConnection("jdbc:oracle:thin:@sccloud030:1521/orcl","techuser","techuser");
    
/*
 * The java code to set the client username goes here !!!
 */

    Statement stmt = null;
    ResultSet rs1 = null;
    stmt = con.createStatement();
    rs1 = stmt.executeQuery("select user from dual");
    while (rs1.next()) {
      System.out.println("Database userid: " + rs1.getString(1));
    }  
    rs1.close();
    stmt.close();

    stmt = con.createStatement();
    rs1 = stmt.executeQuery("select * from emp");
    while (rs1.next()) {
      System.out.println("EMPNO: " + rs1.getString(1) + " ["+rs1.getString(2)+"] - DEPTNO: "+rs1.getString("DEPTNO"));
    }  
    rs1.close();
    stmt.close();
    con.close();
  }
} 
This program so far does not know anything about proxy connections - the results are therefore straightforward: We are connected as TECHUSER and there is no EMP table.
D:\> java.exe proxyConnect realuser1
Database userid: TECHUSER
Exception in thread "main" java.sql.SQLSyntaxErrorException: 
ORA-00942: table or view does not exist.
        at oracle.jdbc.driver.SQLStateMapping.newSQLException(SQLStateMapping.java:91)
        at oracle.jdbc.driver.DatabaseError.newSQLException(DatabaseError.java:112)
        at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:173)
        :
Now we'll add the "magic" lines of code to set the client username. Exchange those three lines ...
/*
 * The java code to set the client username goes here !!!
 */
... with these - we'll take the first command line argument as the username.
    Properties props = new Properties();
    props.put("PROXY_USER_NAME", args[0]);
    ((OracleConnection)con).openProxySession(OracleConnection.PROXYTYPE_USER_NAME, props);  
Recompile and test again - and ... it works.
D:\> java proxyConnect realuser1
Database userid: REALUSER1
EMPNO: 7782 [CLARK] - DEPTNO: 10
EMPNO: 7839 [KING] - DEPTNO: 10
EMPNO: 7934 [MILLER] - DEPTNO: 10

D:\> java proxyConnect realuser2
Database userid: REALUSER2
EMPNO: 7369 [SMITH] - DEPTNO: 20
EMPNO: 7566 [JONES] - DEPTNO: 20
EMPNO: 7788 [SCOTT] - DEPTNO: 20
EMPNO: 7876 [ADAMS] - DEPTNO: 20
EMPNO: 7902 [FORD] - DEPTNO: 20
Only by giving the username on the command line we connected as REALUSER1 or REALUSER2 - the password always was TECHUSER's one. With this approach a java program can obtain a database connection from its application server, set the client username and connect to the database as another "real" user. And this proxy connection can be changed while the main "physical" connection remains open. The following code shows this - for clarity I have removed the actual database actions and replaced them with the pseudo method doDatabaseActions().
  public static void main(String args[]) throws Exception {
    // open the physical connection as TECHUSER
    DriverManager.registerDriver(new oracle.jdbc.OracleDriver());
    Connection con = DriverManager.getConnection("jdbc:oracle:thin:@sccloud030:1521/orcl","techuser","techuser");
    
    // open the proxy connection for REALUSER1, do something and close
    Properties props = new Properties();
    props.put("PROXY_USER_NAME", "realuser1");
    ((OracleConnection)con).openProxySession(OracleConnection.PROXYTYPE_USER_NAME, props);  
    doDatabaseActions(con);
    ((OracleConnection)con).close(OracleConnection.PROXY_SESSION); 

    // the physical connection is still open ...
    // now open the proxy connection for REALUSER2, do something and close
    props.put("PROXY_USER_NAME", "realuser2");
    ((OracleConnection)con).openProxySession(OracleConnection.PROXYTYPE_USER_NAME, props);  
    doDatabaseActions(con);
    ((OracleConnection)con).close(OracleConnection.PROXY_SESSION); 

    // finally close the physical connection
    con.close();
  }
When client/server applications are moving to a web environment, this technology gets highly interesting - because the application can keep its existing user, role and security model and move on to the web technology where all database connections are done with one technical user.
REALUSER1 or REALUSER2 still need their CREATE SESSION privilege - the accounts also must exist in the database and must not be locked. But (if needed) you actually can "forbid" direct connections by setting the password to an "impossible" value ...
SQL> alter user realuser1 identified by values 'Hello'
The REALUSER1 account is still open and connects are possible in theory. But since the IDENTIFIED BY VALUES command directly wrote "Hello" into the password table, one would have to find out one of the password - which evaluate to "Hello" during the hashing process - I'll say it that way: at least very difficult. Connections as REALUSER1 now are only possible as connections through TECHUSER. But it is also possible to have both direct and proxy connections in parallel.
More about proxy authentication is in the documentation:

Beliebte Postings