💡 Esempio completo funzionante disponibile su GitHub: qr-sign-password-protected-pdf-python
Introduzione
Esiste un modello a tre passaggi che la maggior parte dei team utilizza quando un documento che deve essere firmato risulta criptato: decrittarlo, firmare il testo in chiaro, ricriptare il risultato. Funziona. Significa anche che per qualche centinaio di millisecondi una copia leggibile di un documento deliberatamente protetto esiste in una directory temporanea, e in una pipeline auditata quella finestra è la scoperta piuttosto che la firma.
Firmare un PDF protetto è una funzionalità di GroupDocs.Signature per Python via .NET che elimina completamente quei tre passaggi: la password apre la sorgente in loco, la firma viene applicata e l’output viene riscritto protetto. Questo articolo confronta i quattro percorsi di password – due che funzionano e due che falliscono di proposito – e descrive il contratto di errore specifico di questo binding.
Perché è importante
La gestione delle password è il punto in cui le pipeline di documenti perdono. Non attraverso la libreria di firma, di solito, ma tramite lo scaffolding intorno: il file temporaneo che doveva essere eliminato, il gestore di eccezioni che ha inghiottito un errore di password errata e ha ritentato all’infinito, la copia firmata consegnata con una password di cui il destinatario non è mai stato informato.
Tutti e tre hanno la stessa causa radice, cioè la password è trattata come qualcosa da rimuovere piuttosto che come parte dell’operazione. LoadOptions e SaveOptions la reinseriscono nell’operazione.
Prerequisiti
Python 3 e groupdocs-signature-net==26.1, più un PDF con password utente. Senza licenza la libreria gira in modalità di valutazione, che firma comunque ma aggiunge il proprio testo alla pagina.
Installazione
pip install groupdocs-signature-net==26.1
Metodo 1 - Conserva la password originale
Il valore predefinito, e quello che richiede meno codice. La password viene fornita tramite LoadOptions, e non viene passato alcun SaveOptions:
load_options = LoadOptions()
load_options.password = password
options = _build_qr_options(qr_text)
with signature.Signature(source_path, load_options) as sign:
result = sign.sign(output_path, options)
return len(result.succeeded)
L’assenza di SaveOptions è ciò che compie il lavoro reale qui. use_original_password è impostato di default su True, quindi GroupDocs riapplica la password di origine all’output firmato. Non c’è alcun momento in cui esiste una versione non protetta, su disco o altrove, e len(result.succeeded) indica quante firme sono state scritte.
Metodo 2 - Cambia la password della copia firmata
Quando il documento firmato viene consegnato a una parte diversa, la mossa sensata è dare alla copia una propria credenziale e lasciare intatta la sorgente:
save_options = SaveOptions()
save_options.password = new_password
save_options.use_original_password = False
with signature.Signature(source_path, load_options) as sign:
result = sign.sign(output_path, options, save_options)
return len(result.succeeded)
Entrambe le righe di SaveOptions sono necessarie, e questo è il dettaglio da ricordare: impostare password lasciando use_original_password al valore predefinito non produce alcun effetto osservabile. Il flag prevale, l’output mantiene la vecchia password e lo scopri quando il destinatario segnala che la password inviata non funziona.
Metodo 3 e 4 - I due fallimenti
Un documento criptato risponde in modo diverso a una password mancante e a una errata, e la differenza vale la gestione.
Senza alcun LoadOptions, l’apertura fallisce e non viene scritto nulla:
try:
with signature.Signature(source_path) as sign:
sign.sign(output_path, options)
return ""
except RuntimeError as error:
return proxy_error_name(error)
Questo restituisce PasswordRequiredException. Fornire invece una password errata fa sì che lo stesso codice restituisca IncorrectPasswordException. Una indica di chiedere all’utente una credenziale; l’altra indica che la credenziale in tuo possesso è obsoleta. Un gestore che non riesce a distinguerle finisce per ritentare una password che non funzionerà mai.
Il contratto di errore, e perché il codice ovvio fallisce
Ecco la parte che costa un pomeriggio se nessuno ti avverte. Il binding espone PasswordRequiredException, IncorrectPasswordException e GroupDocsSignatureException come nomi grezzi che non ereditano da BaseException. Scrivi il gestore intuitivo:
except IncorrectPasswordException:
...
e Python solleva TypeError: catching classes that do not inherit from BaseException is not allowed. L’errore originale scompare, sostituito da uno che punta alla tua riga except anziché alla password. Ho scritto esattamente quel gestore la prima volta, e i venti minuti spesi a leggere il TypeError sono il motivo per cui questa sezione esiste.
Ciò che arriva realmente è un RuntimeError il cui messaggio inizia con Proxy error(<Name>): . Analizzare quel prefisso recupera la causa:
message = str(error)
marker = "Proxy error("
if not message.startswith(marker):
return ""
start = len(marker)
end = message.find(")", start)
if end < 0:
return ""
return message[start:end]
Fai il branching sul nome restituito anziché sul testo del messaggio, che contiene percorsi di file e varia tra esecuzioni.
Ispezionare prima di firmare
C’è un quinto percorso da conoscere, e non scrive nulla. Aprire il documento con LoadOptions e chiamare get_document_info restituisce il formato, il conteggio delle pagine e la dimensione mentre il file rimane criptato su disco:
with signature.Signature(source_path, load_options) as sign:
info = sign.get_document_info()
return info.file_type.file_format, info.page_count, info.size
Due usi per questo. Quando la password proviene da un modulo utente, questo valida la credenziale con una chiamata leggera anziché a metà di un batch di duecento documenti. E quando una pipeline non è autorizzata a memorizzare testo in chiaro, permette comunque alla pipeline di segnalare ciò che sta contenendo – conteggi di pagine per un registro di audit, dimensioni per una quota – senza decrittare nulla.
Confronto dei Metodi: Quando Usare Ognuno
| Metodo | Ideale per | Vantaggi principali | Limitazioni |
|---|---|---|---|
| Keep original password | pipeline che firmano in loco | nessun SaveOptions, nulla scritto in chiaro | il destinatario ha bisogno della password di origine |
| Re-key on save | consegna a un’altra parte | la sorgente mantiene la sua credenziale, la copia ne ottiene una nuova | due righe di SaveOptions, facile impostare solo una |
| No password (fails) | dimostrare il contratto nei test | fallisce all’apertura, non scrive nulla | non è un percorso di firma |
| Wrong password (fails) | distinguere una credenziale obsoleta | nome dell’eccezione distinto | non è un percorso di firma |
Vale la pena la lettura di ritorno con la chiamata extra?
Sì, per due ragioni. Riaprire il file firmato con QrCodeVerifyOptions dimostra che la firma è sopravvissuta al salvataggio, e poiché la riapertura deve fornire la password, dimostra anche che l’output è ancora criptato. Un conteggio zero è quasi sempre un problema di licenza piuttosto che un fallimento di firma – la chiamata sign solleva un’eccezione quando fallisce realmente, quindi silenzio più zero corrispondenze indica una build non licenziata.
Cosa costa il passaggio
Nulla di strutturale. Se il tuo codice già decripta in un file temporaneo, la modifica consiste nell’eliminare quel passaggio, spostare la password in LoadOptions e rimuovere la chiamata di ricriptazione alla fine – tipicamente una perdita netta di righe. La chiamata di firma stessa non cambia forma, e l’output è byte per byte un PDF firmato con la stessa protezione con cui è stato fornito.
L’unico punto da controllare attentamente è il codice di pulizia. Una pipeline costruita attorno a decrypt‑sign‑reencrypt di solito ha un blocco finally che elimina il file temporaneo, e una volta che il file temporaneo è sparito quel blocco sta eliminando un percorso che non esiste più.
Buone pratiche
- Lascia
use_original_passwordcosì com’è, a meno che tu non stia ruotando deliberatamente; il valore predefinito è quello sicuro. - Analizza il nome proxy una sola volta, in un helper, e fai il branching su di esso ovunque altrove.
- Convalida una password fornita dall’utente con
get_document_infoprima di avviare un batch, così una credenziale errata costa una chiamata leggera anziché un’esecuzione a metà. - Non scrivere mai l’output firmato sul percorso della sorgente, così un errore lascia recuperabile l’originale.
Conclusione
La password non è un ostacolo da aggirare prima della firma – è un argomento dell’operazione. Apri con LoadOptions, decidi la protezione dell’output con SaveOptions, analizza il nome proxy quando qualcosa fallisce e verifica tramite la password successivamente. Il campione esegue tutti e quattro i percorsi in un unico run, così la differenza tra di essi richiede un solo comando per essere vista anziché un paragrafo da credere.