Passwortgeschützte Dokumente laden mit dem jadice web toolkit
Dieser Artikel beschreibt, wie passwortgeschützte PDF-Dokumente mit dem jadice web toolkit geladen werden können
Die vorgestellte Lösung ist im Showcase des jadice web toolkit unter http://webtoolkit.jadice.com/showcase/index.html#!EncryptedDocumentExample abrufbar. Das Passwort für das dort verwendete Dokument lautet J4d1c3
Anforderung
Ein PDF-Dokument, zu dessen Anzeige ein Passwort benötigt wird, soll im jadice web toolkit dargestellt werden. Dazu wird beim Öffnen des Dokuments ein Passwort in einem Benutzerdialog abgefragt. Anschließend wird versucht, das Dokument mit diesen Passwort zu öffnen.
Grundlegendes Prinzip
In diesem Abschnitt werden die grundlegenden Prinzipien behandelt, um passwortgeschützte PDF-Dokumente im jadice web toolkit öffnen zu können.
Lesen eines passwortgeschützten Dokuments mit dem DocumentDataProvider
Um ein passwortgeschütztes Dokument mit Mitteln der jadice documentplatform öffnen zu können, muss in den PDFStandardSecurityHandlerSettings des Readers ein CryptoMaterialProvider gesetzt werden. Das vom CryptoMaterialProviders bereitgestellte PasswordMaterial enthält dann einen String, mit dem versucht wird, das Dokument zu öffnen.
Das Setzen dieser Einstellung kann beispielsweise in der der read()- beziehungsweise der recovery()Methode des verwendeten DocumentDataProvider erfolgen. Es ist allerdings auch möglich, in diese Methoden eine Reader-Instanz zu übergeben, für die die PDFStandardSecurityHandlerSettings bereits zuvor gesetzt wurden.
Das folgende Code-Beispiel enthält einen Auszug der Implementierung eines DocumentDataProvider, bei der stets versucht wird, das DEFAULT_PASSWORD zum Öffnen des Dokuments zu verwenden. In der Praxis wird man das Passwort eher über einen Benutzerdialog abfragen und in Source beziehungsweise Handle hinterlegen.
DocumentDataProvider mit Standard-Passwort
@Override
public void read(Reader reader, final S source) throws JadiceException, IOException {
// Abfragen der PDFStandardSecurityHandlerSettings beim Reader.
PDFStandardSecurityHandlerSettings pdfStandardSecurityHandlerSettings = reader.getSettings(
PDFStandardSecurityHandlerSettings.class);
// Definition des CryptoMaterialProviders
CryptoMaterialProvider<PasswordMaterial> cryptoMaterialProvider = new CryptoMaterialProvider<PasswordMaterial>() {
@Override
// Der CryptoMaterialProvider wiederum stellt einem CryptoMaterialReceiver das
// PasswordMaterial zur Verfügung.
public void provide(CryptoMaterialReceiver<PasswordMaterial> receiver) {
// Bereistellung des PasswordMaterial mit dem fixen Passwort DEFAULT_PASSWORD
receiver.receive(new PasswordMaterial(DEFAULT_PASSWORD));
}
};
// setzen des oben definierten cryptoMaterialProvider in den pdfStandardSecurityHandlerSettings
// des Readers
pdfStandardSecurityHandlerSettings.setCryptoMaterialProvider(cryptoMaterialProvider);
// eigentlicher Lesevorgang im Reader
Provider<InputStream, IOException> stream = getStream(source);
reader.read(stream);
}
@Override
public void recover(Reader reader, final SH handle) throws RecoverFailedException, JadiceException {
// Abfragen der PDFStandardSecurityHandlerSettings beim Reader.
PDFStandardSecurityHandlerSettings pdfStandardSecurityHandlerSettings = reader.getSettings(PDFStandardSecurityHandlerSettings.class);
// Definition des CryptoMaterialProviders
CryptoMaterialProvider<PasswordMaterial> cryptoMaterialProvider = new CryptoMaterialProvider<PasswordMaterial>() {
// Der CryptoMaterialProvider wiederum stellt einem CryptoMaterialReceiver das
// PasswordMaterial zur Verfügung.
@Override
public void provide(CryptoMaterialReceiver<PasswordMaterial> receiver) {
// Bereistellung des PasswordMaterial mit dem fixen Passwort DEFAULT_PASSWORD
receiver.receive(new PasswordMaterial(DEFAULT_PASSWORD));
}
};
// setzen des oben definierten cryptoMaterialProvider in den pdfStandardSecurityHandlerSettings
// des Readers
pdfStandardSecurityHandlerSettings.setCryptoMaterialProvider(cryptoMaterialProvider);
try {
Provider<InputStream, IOException> stream = getRecoveryStream(handle);
reader.read(stream);
} catch (IOException e) {
throw new RecoverFailedException("Can't recover " + handle, e);
}
}Weitere Implementierungsbeispiele eines CryptoMaterialProvider finden sich in den weiterführenden Links am Ende dieses Artikels. In der dort verlinkten Dokumentation wird auch erklärt, wie ein CryptoMaterialProvider mit mehreren Standard-Passwörtern implementiert werden kann.
Der Vergleich des Passworts aus dem PasswordMaterial mit dem im Dokument gesetzten Wert erfolgt auf Basis eine byte-Array, nicht als String-Vergleich! Deswegen kann es bei Verwendung unterschiedlicher Encodings zu Fehlern kommen, auch wenn offenbar der korrekte String im PasswordMaterial gesetzt wurde. Ein Workaround für dieses Problem ist ebenfalls am Ende dieses Artikels dargestellt.
Anfordern des Passworts beim Benutzer
Ist beim Laden eines PDF-Dokuments das benötigte Passwort serverseitig nicht bekannt wird dort zunächst eine PDFSecurityDocumentCreationException geworfen.
Diese kann anschließend in Form eines MimicryThrowable an den Client propagiert werden. Dort kann dann durch einen Benutzerdialog das Passwort abgefragt und an den Server übertragen werden.
Ist das Passwort korrekt, wird das Dokument geladen und angezeigt. Kann das Dokument mit dem eingegebenen Passwort nicht geöffnet werden, wird erneut eine PDFSecurityDocumentCreationException geworfen und als MimicryThrowable an den Client gesendet.
Der serverseitige Ladevorgang kann durch den Client abgebrochen werden, beispielsweise wenn das Passwort dem Benutzer nicht bekannt ist.
Da das Passwort bei jedem Ladevorgang des Dokuments auf dem Server vorhanden sein muss, sollte sichergestellt sein dass das Passwort dort lange genug vorgehalten wird. Damit wird sichergestellt. dass das Dokument im Recovery-Fall nach der Entfernung aus dem Cache ohne Nutzerinteraktion neu geladen werden kann.
Beispielhafte Implementierung im Showcase
Eine beispielhafte Implementierung zur Anzeige passwortgeschützter PDF-Dateien wird im Showcase unter https://webtoolkit.jadice.com/showcase/index.html#!EncryptedDocumentExample vorgestellt.
Dort findet in Example.java der Ladevorgang des passwortgeschützten Dokuments encr.pdf statt. Dabei wird dem Reader im Aufruf von Reader#complete(...) ein AsyncCallback-Objekt übergeben, dessen onFailure-Methode bei Fehlern beim Dokumentenladevorgang aufgerufen wird.
Innerhalb der onFailure-Methode wird geprüft, ob der Fehler durch ein falsches oder fehlendes Passwort verursacht wurde. Ist dies der Fall wird ein PasswortDialog angezeigt.
Der PasswortDialog hinterlegt das Passwort bei Bestätigung der Eingabe in einem Source-Handle, das anschließend an den Reader übergeben wird.
Weitere Information zum Lesen von Dokumenten mit Source-Handles finden sich in Kapitel zwei des Tutorials "Getting Started" unter 2 - Laden eines Dokuments
- |
Passwort-Dialog aus |
Erläuterungen zur Implementierung
In diesem Abschnitt soll anhand von Code-Auszügen die Implementierung des Showcase erläutert werden.