Archive

Archive for the ‘Development’ Category

Excel VBA Entwicklung (Teil 1): Makros

February 4th, 2012 No comments

Alle kennen Excel, DAS Tabellenkalkulationsprogramm aus dem Hause Microsoft – aktuell in der Version 2010 erhältlich.

Excel bietet eine Umfangreiche Bibliothek mit Funktionen unter anderem aus den Bereichen Mathematik, Finanz- und Ingenieurswesen. Es bietet Pivottabellen für Auswertungen und vielfältige Möglichkeiten der grafischen Aufbereitung mittels Diagrammen. Zudem liefert Excel mit der Skriptsprache Visual Basic for Applications, kurz VBA, die Möglichkeit Makros aufzuzeichnen, um komplexe, sich wiederholende Aufgaben zu automatisieren. Steigt man tiefer in die Programmierung mit VBA ein, kann man eigenen Funktionen und Funktionsbibliotheken entwickeln, bis hin zu einer völlig eigenständigen (aberauf Excel basierenden) Anwendung. Die zur Programmierung nötige Entwicklungsumgebung, der Visual Basic Editor (VBE), liefert Excel gleich mit.

Ich will hier einen kurze Einstieg in die Entwicklung mit Excel VBA bieten.

Makros

Die für die meisten einfachste und schnellste Art ein VBA Programm zu schreiben dürfe sein es nicht selbst zu schreiben, sondern die Arbeit dem Makrorekorder zu überlassen.

Vor die Aufzeichnung des ersten Makros hat Excel allerdings die Optionen gesetzt. Die Makrofunktionen befinden sich im Ribbon im Register “Entwicklertools”. Dieses Register ist standardmäßig ausgeblendet und wird folgendermaßen sichtbar gemacht:

  1. Im Ribbon auf Register “Datei” gehen.
  2. Den vorletzten Punkt “Optionen” aufrufen.
  3. Im jetzt aufgegangenen Dialog auf der linken Seite “Menüband anpassen” wählen
  4. und anschließend in der Liste “Hauptregisterkarten” einen Haken bei “Entwicklertools” setzen:

Nun können wir auf das Register “Entwicklertools” gehen und finden dort im Abschnitts “Code” die Makrofunktionen:

 

  • “Makros”
    Hier erscheint ein Dialog mit dem man seine vorhandenen Makros verwalten und ausführen kann.
  • “Makro aufzeichnen” / “Aufzeichnung beenden”
    Mit dieser Funktion wird die Aufzeichnung eines Makros gestartet. Dabei werden alle ausgeführten Befehle in ihrer Reihenfolge zur späteren Wiedergabe aufgezeichnet. Auf diese Weise lassen sich komplexe und wiederkehrende Aufgaben automatisieren.
  • “Relative Aufzeichnung”
    Diese Option legt fest, ob beim Aufzeichnen der Makros die Zellpositionen absolut, oder relativ gespeichert werden sollen. Soll etwa die rechts von A1 liegende Zelle als B1 gespeichert werden, oder als “Rechts-von-der-aktuellen-Zelle”.
  • “Makrosicherheit”
    Diese Funktion ruft das Excel-Sicherheitscenter auf, wo man die Bedingungen zur Ausführung von Makros einstellen kann.

Mit diesem Wissen kann jetzt jeder selbst Makros aufzeichnen:

  1. “Makro aufzeichnen”.
  2. Name des Makros einstellen.
  3. Arbeitsschritte in Excel abarbeiten.
  4. “Aufzeichnung beenden”

Über den Dialog “Makros” lässt sich das soeben aufgezeichnete Makro nun jederzeit starten und die aufgezeichneten Schnitte werden automatisch nacheinander ausgeführt.

Categories: Development Tags: , ,

Zeitgesteuerte Jobs in Java / JEE (EJB-Timer und Quartz-Scheduler)

February 3rd, 2012 No comments

Problemstellung:

Da wir für eines unserer Projekte zeitgesteuerte Prozesse benötigen und ich gerade am Architekturkonzept schreibe, habe ich verschiedene Technologien evaluiert. Dabei bin ich auf zwei verschiedene Frameworks gestoßen:

  • EJB-Timer
  • Quartz-Scheduler

Die Anforderung ist:

Zuverlässig in einem bestimmten Intervall (z.B. täglich um 23.30 Uhr oder an jedem Jahresende) eine Reihe von Prozessen auszuführen. Die Lösung muss mit Java 7 auf dem JBoss 7.0.2 deployed werden.

 

EJB-Timer:

Vorteil

Um den EJB-Timer nutzen zu können müssen keine externen JAR-Dateien gesucht werden, da alles im javax.ejb-Packet enthalten ist. Die erforderliche JAR-Datei ist im JBoss integriert und im Eclipse-Projekt muss lediglich die JBoss 7.0 Runtime Bibliothek hinzugefügt werden.

Nachteil

Dafür musste ich leider feststellen, dass der „jboss-as-7.0.2.Final“ den <Timer-Service> nicht aktiviert hat. Um dies zu tun muss man die „Standalone-preview.xml“ laden. Dafür muss die standalone.bat (unter Windows) mit folgendem Parameter gestartet werden:

  • standalone.bat -server-config=standalone-preview.xml

Es darf dabei nicht vergessen werden, jegliche Änderung, die in der standalone.xml vorgenommen wurde, jetzt auch nochmal in der standalone-preview.xml einzutragen.

Java-Code

Um den Timer-Service zu nutzen, erstellt man eine EJB. Für die Problemstellung bietet sich ein @Singleton an, der per @Startup ausgeführt wird. Mit @Resource bekommt man den SessionContext, der wiederum über die Methode getTimerService() den gewünschten Service anspricht.

Um einen Timer zu erstellen, kennt der Service die Methode createTimer(Date startZeitpunkt, Long intervallInMillisekunden, Serializable nameDesTimers), mit der ein Timer initialisiert wird.

Nach jedem Intervall läuft der Timer in ein Timeout, für das die EJB eine Methode mit der Annotation @Timeout benötigt. Sie muss public void sein und als Übergabeparameter ein Timer-Objekt besitzen. Diese Methode wird bei jedem Timeout ausgeführt.

Problem

Beim ersten Ausprobieren war ich voller Euphorie als ich sah, dass in meinem eingestellten Intervall eine Nachricht auf der Konsole erschien. Als ich jedoch ein weiteres Mal deployed habe musste ich feststellen, dass der erste Timer immer noch lief, obwohl ich die War-Datei gelöscht, den JBoss beendet und wieder neu hochgefahren habe. Nach weiterer Recherche habe ich vom Timer-Service über die Methode getTimers() jeden aktiven Timer mit cancel() beendet. Trotzdem liefen mittlerweile sehr viele Timer weiter.

Scheinbar speichert der JBoss die seralisierten Timer-Objekte in irgendeinen Cache. Jedenfalls habe ich auch nach längerer Recherche nicht herausgefunden wie und wo die abgespeichert werden, bzw. wie ich sie wieder löschen kann. Falls jemand darüber Bescheid weiß, wäre es nett, wenn er mich kontaktieren würde.

 

Quartz-Scheduler

Vorteil

Für den Quartz-Scheduler werden keine Änderungen in der JBoss-Konfiguration benötigt. Lediglich drei JAR-Dateien müssen dem Projekt beigefügt werden.

  • Quartz-all-2.1.3.jar
  • Slf4j-api-1.6.1.jar
  • Slf4j-log4j12-1.6.1.jar

Diese findet man auf der offiziellen Seite quartz-scheduler.org.

Nachteil

Um die JAR-Dateien herunter zu laden, muss man sich auf der Seite registrieren.

Java-Code

Um den Quartz-Scheduler zu nutzen, benötigt man drei Objekte. Einen Job der ausgeführt werden soll, einen Trigger, der sagt wann und in welchem Intervall der Job ausgeführt werden soll und einen Scheduler, in dem der Job einem Trigger zugewiesen und gestartet wird. Für die Implementierung bietet sich eine @Singleton @Startup EJB an, die beim initialisieren (@PostConstruct) die benötigten Objekte erstellt und den Scheduler startet.

Job

Man benötigt eine JavaKlasse, die das Interface org.quartz.Job implementiert und folgende Methode überschreibt:

  • public void execute(JobExecutionContext context) throws JobExecutionException

In dieser Methode kann beliebiger Java-Code stehen, der zeitgesteuert ausgeführt werden soll.

Dieser Job muss mit einem JobDetail-Objekt verbunden werden:

JobDetailImpl jobHolidayTaken = new JobDetailImpl();
jobHolidayTaken.setName("HolidayTakenJob");
jobHolidayTaken.setJobClass(HolidayTakenJob.class);

Der wird benötigt, da dem Scheduler ein Trigger zusammen mit einem JobDetail-Objekt übergeben wird.

Trigger

Der Trigger dient dazu, ein Intervall und einen Startzeitpunkt zu definieren, in dem ein Job ausgeführt werden soll. Es gibt verschiedene Arten von Triggern. Ich habe den SimpleTrigger und den CronTrigger versucht, wobei dem SimpleTrigger ein Startzeitpunkt und ein Intervall in Millisekunden übergeben werden. Der CronTrigger ist für unsere Problemstellung interessanter, da er über seine CronExpression-language mit dem Kalender klar kommt. Wenn man z.B. an jedem Monatsende einen bestimmten Prozess ausführen will, kommt man mit einem Millisekunden-intervall ohne zusätzliche Logik nicht aus. Die Syntax der CronExpression-language macht das zu einem Kinderspiel:

http://www.quartz-scheduler.org/documentation/quartz-1.x/tutorials/crontrigger

Beispiel:

Calendar startHolidayTaken = new GregorianCalendar();
startHolidayTaken.set(2012, 1, 2, 16, 45, 0);
CronTriggerImpl crontriggerHolidayTaken = new CronTriggerImpl();
crontriggerHolidayTaken.setName("cronHolidayTaken");
crontriggerHolidayTaken.setStartTime(startHolidayTaken.getTime());
crontriggerHolidayTaken.setCronExpression("0 30 23 * * * *");

Dieser Trigger wird täglich um 23:30 Uhr in ein Timeout laufen und den Job ausführen, den er im Scheduler zugewiesen bekommt.

Scheduler

Der Scheduler ist so etwas wie der Container, in dem sich die Trigger und die Jobs befinden. Der Scheduler verbindet einen Job mit einem Trigger und sorgt dafür, dass beim Timeout der Job ausgeführt wird.

Beispiel:

this.scheduler = new StdSchedulerFactory().getScheduler();
this.scheduler.start();
this.scheduler.scheduleJob(jobHolidayTaken, crontriggerHolidayTaken);

Fazit

Ich habe die Evaluierung mit dem EJB-Timer angefangen, da wir sowieso einen EJB-Container in unserem JBoss haben und ich kein weiteres Framework einbinden wollte.

Da ich mich allerdings einen ganzen Tag mit dem EJB-Timer beschäftigt habe und zu keinem zufriedenstellenden Ergebnis gekommen bin (wie schon erwähnt: Ich würde mich über andere Erfahrungen darüber sehr freuen), habe ich mich für den Quartz-Scheduler entschieden, den ich innerhalb kurzer Einarbeitungszeit so zum Laufen bekommen habe, wie ich es brauchte.

Categories: Java Tags:

jQuery – noConflict() und die Fallstricke

January 26th, 2012 No comments

jQuery hat sich in den vergangenen Jahren zu einer weit verbreiten JavaScript – Bibliothek entwickelt. Der Slogan dieser Bibliothek “Write less, do more” ist keine leere Versprechung. Ich habe nun in verschiedenen Projekten jQuery erfolgreich eingesetzt. In einem Projekt bin ich jedoch auf etwas gestossen, was unbekannt war für mich: Die Verwendung von unterschiedlichen Versionen von jQuery auf einer geladenen Seite.

Wie erreicht man das Zusammenspiel? Hierfür bietet jQuery die Funktion “noConflict()” an.

Ein paar Hintergrundinformationen: Hat man die jQuery – Bibliothek importiert auf seine Seite, sind die Funktionen von jQuery mittels “$” oder “jQuery” verfügbar; z.b. ‘jQuery(“div p”).hide();’ oder ‘$(“div p”).hide();’

Verwenden nun andere Skript das ‘$’ – Zeichen um die Funktionen verfügbar zu machen, so kann führt es dazu dass die Funktionen von anderen Bibliotheken nicht verfügbar sind, da jQuery bereits das ‘$’ – Zeichen verwendet.

Was kann also getan werden?

1. jQuery.noConflict();
2. jQuery(“div p”).hide();
3. $(“content”).style.display = ‘none’;

Was wird in diesem Abschnitt getan?

1. Hier sagen wir: Gib dem anderen Skript den Alias ‘$’ zurück; so dass ‘$’ wieder für die andere Bibliothek zur Verfügung steht.
2. Arbeite hier mit “jQuery” um jQuery – spezifische Funktion aufzurufen.
3. Da wir in Zeile 1 “.noConflict()” aufgerufen haben, können wir hier mittels ‘$’ die Funktionen einer anderen Bibliothek aufrufen.

Mittels “.noConflict()” kann auch die Verwendung von 2 unterschiedlichen jQuery – Versionen realisiert werden:

1. <script src=”/domain/lib/js/jQuery1.3.2.js” type=”text/javascript”></script>
2. var jq = jQuery.noConflict();
3. <script src=”/domain/lib/js/jQuery1.5.1.js” type=”text/javascript”></script>
4. var player = jQuery.noConflict();

Was wird hier gemacht?

1. Importieren von einer jQuery – Bibliothek der Version 1.3.2.
2. Setzen eines Aliasses “jq”, so dass alle Funktionalitäten von Version 1.3.2. unter dem Alias “jq” verfügbar sind.
3. Importieren von einer jQuery – Bibliothek der Version 1.5.1.
4. Setzen eines Aliasses “player”, so dass alle Funktionalitäten von Version 1.5.1. unter dem Alias “player” verfügbar sind.

So weit so gut. Doch welche Fallstrickt ergeben sich hieraus?

Es muss im Code darauf geachtet werden, das die beiden Aliasses konsequent verwendet werden!

Folgendes Beispiel demonstiert diesen Fallstrick:

function initEvents(id)
{
var _this = jQuery(this);
var container = _this(id);

var viewSwitch = function(event, state){

}

viewContainer.bind(“viewSwitch”, viewSwitch);
}

function init(id)
{
initEvents(id);
}

In HTML – Seite steht folgendes:

jq(“body”).init(“list”));

Auf den ersten Blick sieht dieser Code fehlerfrei aus: Es wird eine Funktion aufgeruf, welche wiederum Events registriert (“bind”). Dies kann beim Starten der Zeit geschehen.

Das Problem liegt allerdings in der Zeile (“var _this = jQuery(this);”). Hier wird die aktive jQuery Bibliothek verwendet (unter dem Alias “jQuery” verfügbar).
Dieses Event ist nun also unter “jQuery” registriert. Wird nun später eine andere jQuery – Version verwendet z.B. 1.5.1, so ist das vorher registrierte Event nicht mehr bekannt, da es unter jQuery 1.3.2. registriert wurde, nicht jedoch unter jQuery 1.5.1. Merke: jQuery zeigt auf die zuletzt importierte jQuery Bibliothek.

Um dies zu verdeutlichen:

1. Import von Version 1.3.2
2. Setzen von Alias “jq” mittels “.noConflict()”.
3. “jQuery” und Alias “jq” zeigen auf Version 1.3.2.
4. Import von Version 1.5.2. <- Hier entsteht das Problem.
5. Setzen von Alias “jPlayer”
6. “jQuery” und Alias “jPlayer” zeigen auf Version 1.5.1.

Die Lösung des Problems: In der Funktion “initEvents” muss statt “jQuery” das Alias “jq” verwendet werden.

Categories: Development, General Tags: , ,

Wo kommst das Event her?

January 26th, 2012 No comments

Jeder kennt die Situation: Man entwickelt in Javascript, registriert u.a. viele Events etc. oder man arbeitet an einem bestehenden Projekt und muss das bestehenhe Javascript anpassen. Wie behält man in beiden Fällen den Überblick über die Events auf einer Seite?

Ein Tool das ich im Rahmen einer Arbeit bei einem Kunden kennengelernt habe ist “visual event”. Dieses Tool ist ein sogenanntes Bookmarklet.

Wie funktioniert es und was macht es? Man fügt per Drag’n'Drop von der Seite http://www.sprymedia.co.uk/article/Visual+Event den Button “Visual Event” in die Bookmarkleiste des Browsers.

Danach geht man auf eine beliebige Seite deren Events man untersuchen möchte. Ist die Seite geöffnet, klick man auf das zuvor hinzugefügte Bookmark. Nach kurzer Zeit wird die Seite abgedunkelt und es erscheinen kleine, farbige Kästchen die über die Art des Events (Maus: Klick, Doppelklick, Mouseover; HTML – Events: select, blur, focus sowie UI Events: Key down, Key up etc.).

Fährt man mit der Maus über die Kästchen, erhält man Informationen wo im Skript diese Event behandelt wird.

Sehr hilfreich, wenn man den Überblick verliert, wer was wann wieso wo macht!

 

 

 

Enterprise Architect – XML-Schema generieren

December 23rd, 2011 No comments

Der Enterprise Architect (EA) bietet die Möglichkeit, aus einem bestehenden Klassenmodell ein XML-Schema zu erzeugen.
Zu finden ist diese Funktion im Menü unter Project –> XML Schema –> Generate XML Schema

Enterprise Architect - Generate XML Schema

Es erscheint ein Eingabedialog, in dem man Einstellung vornehmen kann, wie das XML-Schema generiert werden soll (z. B. Encoding) und unter welchem Pfad die XSD-Datei abgelegt werden soll.
Der Zielpfad für die XML-Schema-Datei lässt sich mit einem Doppelklick auf den Package Namen in der Tabelle Package-Filename ändern.

Enterprise Architect - Generate XML Schema Detail View

Create Java Classes from XML-Schema

December 23rd, 2011 No comments

JAXB (Java Architecture for XML Binding) is a framework to marshal Java objects to XML and vice-versa.
It is part of the JDK and comes with a command-line compiler (xjc) that creates Java classes from an existing XML-Schema.

Location
The xjc it located at the /bin folder of the JDK:
../jdk1.6.0_18/bin/xjc.exe

Usage
xjc -d [target folder] -p [package name] [xml-schema file name]

Example
The classes will be written to the folder “src” (relative to the current location) in the package “de.myproject.somepackage”.
The schema file “my-schema.xsd” will be compiled.
xjc -d src -p de.myproject.somepackage my-schema.xsd

For more information see
xjc -help

CacheIgnoreHeaders Set-Cookie bei Apache mod_cache

November 3rd, 2011 Comments off

“Kleine Ursache, große Wirkung” passt bei dieser Konfigurationseinstellung ziemlich genau. Ist sie nicht gesetzt, werden bei allen gecachten Seiten die eventuell vorhandenen “set-cookie” Parameter in den Headern mit in den Cache abgelegt. Das kann fatale Folgen haben, wenn z.B. eine JSESSIONID als Cookie gesetzt wird. Wird dann eine Seite mit dem “set-cookie” aus dem Cache geladen, überschreibt diese JSESSIONID die aktuell vorhandene. Da die gecachte JSESSIONID aber einem ganz anderen Benutzer gehören könnte, ist es nun möglich, dessen Session zu übernehmen. Natürlich nur, falls dessen Session noch aktiv ist. Wenn nicht, dann gibt es zumindest einen ungewollten SessionTimedOut Fehler. Die Symptome treten allerdings auch nicht immer und nicht immer bei allen gecachten Inhalten auf, so dass der Fehler schwer reproduzierbar und auffindbar ist.

Apache mod_cache verursacht damit also ungewolltes “Session Hijacking”.

Abhilfe schafft das Setzen von “CacheIgnoreHeaders Set-Cookie”. Jeder, der mod_cache im Einsatz hat, sollte seine Konfiguration dahingehend prüfen wie diese Einstellung gesetzt ist und ob das so ausreichend ist.

Monitoring Apache HTTP Server

October 21st, 2011 Comments off

Für das Monitoring des Apache HTTP Servers stellt dieser eine Statusseite zur Verfügung: http://servername/server-status
Voraussetzung ist, dass am Webserver das Modul mod_status aktiviert ist.

Die Statusseite bietet folgende Informationen:

  • Anzahl der arbeitenden Prozesse (requests currently being processed)
  • Anzahl der Prozesse im Leerlauf (idle workers)
  • Status jedes Prozesses (Zahl der abgearbeiteten Anfragen; Gesamtzahl der Bytes)
  • Gesamtzahl der Zugriffe und Bytes
  • Startzeit und Laufzeit des Servers
  • Durchschnittliche Anzahl der Anfragen je Sekunde, Bytes je Sekunde und durchschnittliche Zahl von Bytes je Anfrage
  • Aktueller CPU-Ressourcenverbrauch je Prozess und Apache insgesamt
  • Aktuell bearbeitete Hostnamen und Anfragen

Sicherheit in Java-EE6-Webapplikationen

October 18th, 2011 Comments off

Java-EE-Webapplikation absichern

 

Authentifizierung und Autorisierung mit Servlet 3.0 und JDBCRealm

 

In diesem Weblog möchte ich in aller Kürze ;-) das Thema “Anwendungssicherheit” in
Java EE 6 erläutern. Als Applikationsserver habe ich Glassfish verwendet,
Infos zum JBoss sind weiter unten verlinkt.

Anhand eines Beispiels gehen wir die einzelnen Schritte durch, um den Server
abzusichern und um unsere Web-Anwendung vor unberechtigtem Zugriff zu schützen.
Die Web-Anwendung wird drei voneinander abgegrenzte Bereiche (admin, employee,
manager) haben, die nur ein Nutzer mit der richtigen Rolle betreten darf.

 

Ganz konkret werden wir an einer bestehenden Webapplikation folgende Dinge tun:

1. Wir sichern das Umfeld des Web-Servers ab. [Link]

2. Wir erzeugen eine Datenbanktabelle, die User, Passwort und Rollen enthält. [Link]

3. JSF Login: Wir bauen eine JSF-Login-Seite, die EJB nutzt, um Nutzer einzulassen oder
auszusperren. Die Zugriffsberechtigungen liegen in einer SQL-Datenbank. [Link]

4. Deployment Deskriptor web.xml: Wir setzen deklarativ SecurityConstraints in der
web.xml, um Seiten vor unberechtigtem Zugriff zu schützen. [Link]

5. EJBs und Servlets: Per Annotation setzen wir erlaubte und unerlaubte Rollen
und können in EJBs sogar auf Methodenebene Nutzer ausschließen. [Link]

 

Unsere Web-Anwendung besteht aus folgenden Schichten:
JSF 2.0 und Servlets 3.0 (z.B. Glassfish 3, JBoss 6+, Tomcat 7)
EJB 3.1 (mind. JavaEE6)
MySQL-Datenbank

 

1. Server und Betriebssystem absichern

Bevor wir uns daranmachen, unsere Web-Anwendung zu sichern, müssen wir erstmal
daran, den Server und das Umfeld zu sichern. Wir legen hier die Basis für die Sicherheit
unserer Web-Anwendung. Was hilft es, wenn unsere Web-Anwendung perfekt geschützt
ist, aber der Server und das Betriebssystem angreifbar sind?

In diesem Artikel möchte ich nur einige Stichpunkte geben, da das Thema zu weitreichend
ist, um es hier auch nur annähernd vollständig behandeln zu können.

1.1 Absichern des Betriebssystems:

  • User mit eingeschränkten Rechten einrichten, nicht als Administrator auf dem BS arbeiten
  • Firewall, Virenscanner und gesunden Menschenverstand einschalten
  • “Sichere”, d.h. aktuelle und gepatchte Software verwenden
  • und vieles mehr

1.2 Absichern des (Java Application) Servers:

  • Nur Ports öffnen, die vom Server genutzt werden (s. %serverdir%/config/server.xml auf
    den meisten Java-Applikationsservern und Servlet-Containern)
  • Die Admin-Konsole per Passwort absichern. Hier einige Beispiele:
    Tomcat (ungetestet): Die Datei %tomcat%/conf/tomcat-users.xml muss angepasst werden
    Glassfish: %glassfish%/bin/asadmin change-master-password bzw.
    %glassfish%/bin/asadmin change-admin-password auf der Kommandozeile ausführen
    JBoss 7 (ungetestet)%jboss7%/standalone/configuration/mgmt-users.properties editieren
  • und vieles mehr

2. Authentifizierung mit einer Datenbanktabelle

Für die Login-Seite müssen wir ein Realm anlegen.

Was ist ein Realm? Lt. Definition im offiziellen Tutorial von Sun ist ein Realm eine Sicherheits-
domäne, die für einen Webserver definiert wird. Den Satz verstehe ich selber nur zur Hälfte,
deshalb habe ich ein Bild gemalt, das diesen Sachverhalt klarer macht:

In Worten: Wir haben in der Datenbank einen User ‘holger’, der in der Gruppe ‘manager’ ist,
einen User ‘bernhard’, der in der Gruppe ‘employee’ ist usw. Unsere Web-Anwendung definiert
Rollen, die zufällig genauso heißen – aber auch anders heißen könnten. Nun müssen wir noch
die in der Datenbank definierten Gruppen unserer Web-Anwendung bekannt machen: das
geht über eine Mapping-Tabelle, die besagt, dass bspw. der Datenbankeintrag ‘manager’ auf
die Rolle ‘manager’ unserer Web-Anwendung passt.

Ziel: Der “Manager” darf nur in den “Manager”-Bereich unserer Web-Anwendung und nur mit
“manager” bezeichnete Geschäftslogik aufrufen, das Gleiche gilt für den Nutzer mit der Rolle
“employee” und Nutzer der Rolle “admin”.

 

So, jetzt müssen wir die User anlegen, die unsere Web-Anwendung im Login erkennen soll.
Hier will ich die Erzeugung von Usern auf zwei Wegen behandeln – per FileRealm und per
JDBCRealm. FileRealm ist einfacher, da wir dafür keine Datenbanktabelle und kein
Mapping brauchen, aber wir können damit nicht dynamisch User anlegen, wie wir es mit
dem datenbankbasierten JDBCRealm können, sondern müssen händisch User anlegen.

  • FileRealm (optional): wir legen die User mitsamt Gruppe per Hand auf dem Server (nicht per DB)
    an. Wenn nur der JDBC-Realm interessant ist, der überspringt diesen Abschnitt und
    klickt hier.
    Der Server legt dafür eine Datei auf dem Server an, die Username, gehashtes Passwort
    und Rollen enthält.
    Glassfish: In der Web-Oberfläche gehen wir auf den Punkt
    Konfigurationen -> server-config ->Sicherheit -> Bereiche -> file (s. Screenshot).


    Hier im Beispiel habe ich die User ‘fileBernhard’ mit Gruppenzugehörigkeit ‘employee’
    angelegt, den ‘fileAdmin’ mit Gruppe ‘admin’ und ‘fileHolger’ mit Gruppe ‘manager’.
    Wer das sofort mal testen will, kann den kommenden Abschnitt ‘JDBCRealm’
    überspringen und geht gleich zum Login unserer Web-Applikation [Link].
    JBoss
    : http://docs.jboss.org/jbossweb/3.0.x/realm-howto.html (ungetestet)
  • JDBCRealm: die Definition der User liegt in einer DB-Tabelle. Legen wir also eine
    DB-Tabelle an:

    CREATE TABLE `myuser` (
      `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
      `username` VARCHAR( 30 ) NOT NULL ,
      `password` VARCHAR( 30 ) NOT NULL ,
      `group` VARCHAR( 30 ) NOT NULL
    )

    und legen ein paar Datensätze an:

    INSERT INTO `myuser` ( `username` , `password` , `group`)
    VALUES
      ('admin', 'adminpw', 'admin'),
      ('bernhard', 'bernhardpw', 'employee'),
      ('holger', 'holgerpw', 'manager'),
      ('karlheinz', 'karlheinzpw', 'employee')

    Ok, wir haben jetzt 4 Datensätze. Die müssen wir jetzt unserer Web-Anwendung bekannt
    machen (s. Abb. “Realm”).

    Nächster Schritt: Im Glassfish müssen wir die Datei WEB-INF/glassfish-web.xml anlegen, die
    unsere “Gruppen” in unserer Datenbank-Tabelle auf die “Rollen” auf dem Server überträgt.

    glassfish-web.xml

      <security-role-mapping>
        <role-name>admin</role-name>
        <group-name>admin</group-name>
      </security-role-mapping>
      <security-role-mapping>
        <role-name>employee</role-name>
        <group-name>employee</group-name>
      </security-role-mapping>
      <security-role-mapping>
        <role-name>manager</role-name>
        <group-name>manager</group-name>
    </security-role-mapping>

    Im JBoss gibt es ebenfalls die Security Roles mit leicht unterschiedlicher Notation:
    JBoss-Docs zum Mapping

    Wir müssen die Datenbank dem Server über JNDI bekannt machen: wie das geht, hat
    Kollege Joachim beschrieben: [Link zu JNDI]

    Jetzt legen wir das JDBCRealm an:
    bei Glassfish geht das über die Weboberfläche,
    bei JBoss über die Konfigurationsdatei $CATALINA_HOME/conf/server.xml [Link]
    JBoss-Docs zu JDBCRealm

 

3. JSF Login-Seite

Wir nähern uns langsam dem ersten Erfolg. Erstellen wir also die Login-Seite als JSF-Facelet:
login.xhtml

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
	  xmlns:h="http://java.sun.com/jsf/html">
	<head>
		<title>Login</title>
	</head>
	<h:body>
		<h:form id="j_security_check">
			<center>
				<h:messages errorClass="errorMessage" infoClass="infoMessage"
							warnClass="warnMessage"></h:messages>
				<h:panelGrid columns="2">
					<h:outputText value="Username : "/>
					<h:inputText id="j_username" value="#{loginBean.username}"/>

					<h:outputText value="Password : "/>
					<h:inputSecret id="j_password" value="#{loginBean.password}"/>

					<h:commandButton value="Submit" action="#{loginBean.login}"
									 type="submit"/>
				</h:panelGrid>
			</center>
		</h:form>
	</h:body>
</html>

Und die logout.xhtml:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
	  xmlns:h="http://java.sun.com/jsf/html">

	<head>
		<title></title>
	</head>
	<h:body styleClass="body">
		<h:messages></h:messages>
		<h:form>
			<h:commandLink value="Logout" action="#{loginBean.logout}"></h:commandLink>
		</h:form>
	</h:body>
</html>

Wir man sieht, greift das Facelet auf eine loginBean zu.
Diese wollen wir jetzt definieren:
LoginBean.java

package de.triona.ejb;
 
import java.io.Serializable;
import java.security.Principal;
import java.util.logging.Logger;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.context.FacesContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
 
@ManagedBean
public class LoginBean implements Serializable {
 
	private String username;
	private String password;
 
	public String login() {
		FacesContext fc = FacesContext.getCurrentInstance();
		HttpServletRequest request = (HttpServletRequest) fc.getExternalContext().getRequest();
		try {
			//Login per Servlet 3.0
			request.login(username, password);
 
			// Der Principal entspricht dem Usernamen
			Principal principal = request.getUserPrincipal();
 
			// Wir können hier nur abfragen, ob der User eine Rolle hat (isUserInRole('whatever')),
			// aber wir können NICHT die Rolle aktiv erfragen (z.B. mit getUserRole(...))
			if (request.isUserInRole("admin")) {
				String msg = "User: " + principal.getName() + ", Role: admin";
				fc.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, msg, null));
				return "admin/startrek";
			} else if (request.isUserInRole("manager")) {
				String msg = "User: " + principal.getName() + ", Role: manager";
				fc.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, msg, null));
				return "manager/timesheet";
			} else if (request.isUserInRole("employee")) {
				String msg = "User: " + principal.getName() + ", Role: employee";
				fc.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, msg, null));
				return "employee/work";
			}
			return "du_musst_die_rollen_noch_definieren";	// hier sollte etwas sinnvolles passieren ;-)
		} catch (ServletException e) {
			fc.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "An Error Occured: Login failed", null));
			e.printStackTrace();
		}
		return "loginFailed";
	}
 
	public void logout() {
		FacesContext fc = FacesContext.getCurrentInstance();
		HttpSession session = (HttpSession) fc.getExternalContext().getSession(false);
		if (session != null) {
			session.invalidate();
		}
		fc.getApplication().getNavigationHandler().handleNavigation(fc, null, "/login.xhtml");
	}
 
	public String getUsername() {
		return username;
	}
 
	public void setUsername(String username) {
		this.username = username;
	}
 
	public String getPassword() {
		return password;
	}
 
	public void setPassword(String password) {
		this.password = password;
	}
}

Die Besonderheit an diesem Code ist die Methode HttpServletRequest.login(username,password)
aus der Servlet-Spezifikation 3.0 und HttpServletRequest.isUserInRole(String role).
Mit login(username,password)  wird username und password des Realms (hier: JDBCRealm oder
FileRealm) validiert. Die Methode HttpServletRequest.isUserInRole(String role) ist selbst-
erklärend: hier wird die Rolle abgefragt, wir können aber aus Sicherheitsgründen nicht – und das ist
Absicht der Java EE 6 Spezifikation – aktiv die Rolle abfragen.

 

Zwischenstand: 

  • wir haben eine DB-Tabelle myusers (mit Name, Passwort und Gruppe) angelegt und befüllt
  • wir haben einen FileRealm auf dem Server angelegt (dieser Schritt ist optional)
  • wir haben einen JDBCRealm angelegt
  • wir haben eine JSF-Seite login.xhtml angelegt, die auf die EJB-Klasse LoginBean zugreift
  • wir haben auf dem Glassfish die XML-Datei glassfish-web.xml bzw. auf dem JBoss die SecurityRoles
    angelegt, die das Datenbankfeld myuser.group auf die Rollen in der Web-Applikation abbildet

Was fehlt noch?

 

4. web.xml bearbeiten

Wir müssen unserem Deployment-Deskriptor (WEB-INF/)web.xml mitteilen, das er eine Authentifizierung
anhand unseres Formulars login.xhtml durchführt und wir müssen unsere Rollen definieren.
In der Netbeans-Oberfläche können wir die web.xml grafisch bearbeiten.

Wer direkt die web.xml bearbeiten will, kann das auch tun:
web.xml

    <login-config>
        <auth-method>FORM</auth-method>
        <realm-name>loginRealm</realm-name>
        <form-login-config>
            <form-login-page>/login.xhtml</form-login-page>
            <form-error-page>/loginfailed.xhtml</form-error-page>
        </form-login-config>
    </login-config>
    <security-role>
        <description/>
        <role-name>manager</role-name>
    </security-role>
    <security-role>
        <description/>
        <role-name>admin</role-name>
    </security-role>
    <security-role>
        <description/>
        <role-name>employee</role-name>
    </security-role>

Probieren wir das Ganze aus: wir starten die login.xhtml im Browser.
Loggen wir uns erfolgreich als admin ein, dann sollten wir zur Seite /admin/startrek.xhtml
weitergeleitet werden, der manager wird auf /manager/timesheet.xhtml und der employee auf
/employee/work.xhtml weitergeleitet.

So weit, so gut: wir können uns einloggen und unsere Rolle ist dem Server bekannt.
Ein Problem haben wir noch: wer zufällig den Link für den Admin-Bereich kennt, kann
diese Seiten aufrufen, dito für den Employee- und den Manager-Bereich.

Das können wir so ebenfalls in der web.xml fixen, indem wir URLs explizit für eine Rolle
erlauben. Der Admin darf nur URLs mit dem Pattern /admin/* aufrufen, dito für employee
und Manager:
Hier via Netbeans:

Und hier der XML-Codeausschnitt aus der web.xml:

    <security-constraint>
        <display-name>AdminConstraint</display-name>
        <web-resource-collection>
            <web-resource-name>AdminArea</web-resource-name>
            <description/>
            <url-pattern>/admin/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <description/>
            <role-name>admin</role-name>
        </auth-constraint>
        <user-data-constraint>
            <description/>
            <transport-guarantee>CONFIDENTIAL</transport-guarantee>
        </user-data-constraint>
    </security-constraint>
    <security-constraint>
        <display-name>EmployeeConstraint</display-name>
        <web-resource-collection>
            <web-resource-name>EmployeeArea</web-resource-name>
            <description/>
            <url-pattern>/employee/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <description/>
            <role-name>employee</role-name>
        </auth-constraint>
    </security-constraint>
    <security-constraint>
        <display-name>ManagerConstraint</display-name>
        <web-resource-collection>
            <web-resource-name>ManagerArea</web-resource-name>
            <description/>
            <url-pattern>/manager/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <description/>
            <role-name>manager</role-name>
        </auth-constraint>
    </security-constraint>

In diesem Beispiel habe ich den Admin-Bereich via SSL-geschützt

<user-data-constraint><transport-guarantee>CONFIDENTIAL</transport-guarantee></user-data-constraint>

Der Browser leitet direkt von http auf https um.

Langsam geht es auf das Finale zu:

 

5. Annotationsbasierter Schutz von Servlets und EJBs

Wir haben das Schlimmste hinter uns. Jetzt folgt die Kür und die wunderbare Welt
der Annotationen.

Fangen wir mit dem Servlet an. Wir wollen ein Servlet schreiben, das nur User mit den Rollen
manager und admin besuchen können. Nichts leichter als das:

ManagerAndAdminServlet.java

@ServletSecurity(@HttpConstraint(rolesAllowed={"manager", "admin"}))
public class ManagerAndAdminServlet extends HttpServlet {
	private static final long serialVersionUID = 1046L;
 
	protected void processRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
//manager und admin dürfen rein, der employee bleibt draußen mit einer SecurityException
        }
}

Mit der Annotation

@ServletSecurity(@HttpConstraint(rolesAllowed={"manager", "admin"}))

ist schon alles fertig.

 

Genauso einfach ist eine EJB vor unautorisiertem Zugriff geschützt:

WorkBean.java

@DeclareRoles({"manager", "employee", "admin"})
@Stateless
@LocalBean
public class WorkBean {
 
	/** Nur für Nutzer mit Rolle 'employee' */
	@RolesAllowed("employee")
	public void doWork(String employee) {
		System.out.println("work");
	}
 
	/** Nur für Nutzer mit Rolle 'manager' */
	@RolesAllowed("manager")
	public void delegateWork() {
		System.out.println("delegateWork");
	}
 
	/** Nur für den 'admin' */
	@RolesAllowed("admin")
	public void administrateWhatever() {
		System.out.println("administrateWhatever");
	}
 
	/** Alle Nutzer ('manager', 'employee' und 'admin') dürfen diese Methode aufrufen */
	public void lookOutOfTheWindow() {
		System.out.println("it rains");
	}
}

 

Unser Web-Anwendung könnte bspw. jetzt so aussehen:


Fazit: Wir haben unsere Web-Anwendung deklarativ und programmativ schützen können.
Natürlich haben wir zum Thema Anwendungssicherheit nur die Spitze des Eisbergs gesehen.
Dies ist nur ein erster Einblick.

Liebe Leser, vielen Dank für eure Aufmerksamkeit.

JBoss7: Oracle 10g und das Leid mit Oracles JDBC-Treiber

September 29th, 2011 Comments off

Heute wollte ich den neuen superschnellen JBoss 7 mit unserer
Oracle-DB 10g (10.2.0.1) verheiraten. Der Server soll eine JNDI-Verbindung
zur Oracle-DB aufbauen und unsere Web-Anwendung greift per JNDI
darauf zu. Ich hole mir vorher noch auf der Oracle-Seite den passenden
JDBC-Treiber für die Version 10.2.0.1.

Nichts leichter als das, denke ich mir, google ein wenig und finde einen
schön kurzen, knackigen Artikel, wie das denn funktioniert:

http://blog.foos-bar.com/2011/08/jboss-as-7-and-oracle-datasource.html

Alles haarklein ausgeführt (Jar auf Server kopieren, XMLs anpassen), 3 mal
gegengecheckt, und es kommt, wie es kommen soll: klappt nicht! Aaaarrgh!
Hin und her überlegt, was der Fehler ist, aber es hilft nichts.
Der Kollege probiert den obigen Blog-Eintrag mit JBoss 7 auch aus und bei
ihm klappt’s tadellos. Na super!

Wo war der Fehler?

Der JDBC-Treiber für Oracle 10.x funktioniert im JBoss 7 nicht mit Oracle 10.x.

Lösung: Wir nehmen den JDBC-Treiber mit der Version 11 !

 

Was war das Problem?
Ich mutmaße: Der Treiber ist nicht JDBC4-konform. Bei älteren Treibern (hier:
JDBC 3) empfiehlt die JBoss-Doku die jdbc.jar händisch um einen Eintrag in der
META-INF zu erweitern, aber diese Maßnahme greift nicht.
Aber es kann auch einfach sein, dass der Jboss 7 schlichtweg mit dem 6 Jahre
alten Treiber ein “internes” Problem hat. Wie dem auch sei, Oracle 10g freut sich
über den JDBC-Treiber v11.