Základní implementace podepisování PDF dokumentů. Podpis implementován pouze pro sestavu schválení služební cesty (natvrdo v kódu). Spouští se univerzální podepisovací aplikace, která čte certifikát ze souboru.

closes #224
This commit is contained in:
2015-06-24 12:54:31 +02:00
parent 312b62af7b
commit d6cff454fd
16 changed files with 960 additions and 4 deletions
@@ -168,4 +168,7 @@ public class Constants {
public final static int LEN_TEXT = 255;
public final static int LEN_DESCRIPTION = 8192;
public final static int LEN_RESULT_MESSAGE = 8192;
public final static String KEY_SIGN_DATA = "SIGN_DATA";
public final static String KEY_SIGN_GUID = "SIGN_GUID";
}
@@ -5,6 +5,7 @@ import info.bukova.isspst.data.User;
import info.bukova.isspst.data.Workgroup;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -13,16 +14,18 @@ public class SessionData implements Serializable {
/**
*
*/
private static final long serialVersionUID = -764426911263559758L;
private static final long serialVersionUID = -764426911263559759L;
private List<Workgroup> userCentres;
private List<Workgroup> userWorkgroups;
private User currentUser;
private Map<Integer, List<Role>> workgroupRoles;
private boolean loginFailed;
private Map<String, Object> properties;
public SessionData() {
loginFailed = false;
properties = new HashMap<String, Object>();
}
public List<Workgroup> getUserCentres() {
@@ -65,4 +68,11 @@ public class SessionData implements Serializable {
this.loginFailed = loginFailed;
}
public void setProperty(String key, Object value) {
properties.put(key, value);
}
public Object getProperty(String key) {
return properties.get(key);
}
}
@@ -0,0 +1,55 @@
package info.bukova.isspst.reporting;
import com.lowagie.text.Rectangle;
import com.lowagie.text.pdf.PdfAnnotation;
import com.lowagie.text.pdf.PdfFormField;
import com.lowagie.text.pdf.PdfName;
import net.sf.jasperreports.engine.JRGenericPrintElement;
import net.sf.jasperreports.engine.JRPropertiesMap;
import net.sf.jasperreports.engine.export.GenericElementHandler;
import net.sf.jasperreports.engine.export.GenericElementHandlerBundle;
import net.sf.jasperreports.engine.export.GenericElementPdfHandler;
import net.sf.jasperreports.engine.export.JRPdfExporter;
import net.sf.jasperreports.engine.export.JRPdfExporterContext;
import net.sf.jasperreports.extensions.ExtensionsRegistry;
import net.sf.jasperreports.extensions.ExtensionsRegistryFactory;
import net.sf.jasperreports.extensions.SingletonExtensionRegistry;
/**
* @author Pepa Rokos
*/
public class SignaturePdfHandler implements GenericElementPdfHandler, GenericElementHandlerBundle, ExtensionsRegistryFactory {
@Override
public ExtensionsRegistry createRegistry(String registryId, JRPropertiesMap properties) {
return new SingletonExtensionRegistry<GenericElementHandlerBundle>(GenericElementHandlerBundle.class, this);
}
@Override
public void exportElement(JRPdfExporterContext exporterContext, JRGenericPrintElement element) {
PdfFormField field = PdfFormField.createSignature(exporterContext.getPdfWriter());
field.setFieldName("signature");
field.setFieldFlags(PdfAnnotation.FLAGS_PRINT | PdfAnnotation.FLAGS_LOCKED);
field.setWidget(new Rectangle(element.getX(), element.getY(), element.getX() + element.getWidth(), element.getY() + element.getHeight()), PdfName.HIGHLIGHT);
exporterContext.getPdfWriter().addAnnotation(field);
}
@Override
public boolean toExport(JRGenericPrintElement element) {
return true;
}
@Override
public String getNamespace() {
return "urn:sig:sig";
}
@Override
public GenericElementHandler getHandler(String elementName, String exporterKey) {
if (elementName.equals("signature") && exporterKey.equals(JRPdfExporter.PDF_EXPORTER_KEY)) {
return this;
}
return null;
}
}
@@ -0,0 +1,10 @@
package info.bukova.isspst.signapi;
/**
* @author Pepa Rokos
*/
public interface JnlpGenerator {
public byte[] generate();
}
@@ -0,0 +1,109 @@
package info.bukova.isspst.signapi;
import org.springframework.beans.factory.annotation.Autowired;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import javax.servlet.http.HttpServletRequest;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.ByteArrayOutputStream;
/**
* @author Pepa Rokos
*/
public class JnlpGeneratorImpl implements JnlpGenerator {
@Autowired
private HttpServletRequest request;
@Override
public byte[] generate() {
String sessionId;
if (request.getParameter("id") != null && !request.getParameter("id").isEmpty()) {
sessionId = request.getParameter("id");
} else {
sessionId = request.getSession().getId();
}
try {
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
Document document = docBuilder.newDocument();
Element root = document.createElement("jnlp");
root.setAttribute("spec", "1.0+");
String signerUrl = request.getRequestURL().toString();
signerUrl = signerUrl.substring(0, signerUrl.indexOf(request.getServletPath())) + "/api/sign";
root.setAttribute("codebase", signerUrl);
root.setAttribute("href", "signer.jnlp" + "?id=" + sessionId);
document.appendChild(root);
Element info = document.createElement("information");
Element title = document.createElement("title");
title.appendChild(document.createTextNode("Signer component"));
info.appendChild(title);
Element vendor = document.createElement("vendor");
vendor.appendChild(document.createTextNode("bukova.info"));
info.appendChild(vendor);
root.appendChild(info);
Element security = document.createElement("security");
security.appendChild(document.createElement("all-permissions"));
root.appendChild(security);
Element resources = document.createElement("resources");
Element j2se = document.createElement("j2se");
j2se.setAttribute("version", "1.8+");
Element jar = document.createElement("jar");
jar.setAttribute("href", "pdfsigner.jar");
resources.appendChild(j2se);
resources.appendChild(jar);
root.appendChild(resources);
Element appDesc = document.createElement("application-desc");
appDesc.setAttribute("main-class", "info.bukova.pdfsigner.Main");
Element argUrl = document.createElement("argument");
argUrl.appendChild(document.createTextNode(signerUrl + "/data"));
appDesc.appendChild(argUrl);
Element argSession = document.createElement("argument");
argSession.appendChild(document.createTextNode(sessionId));
appDesc.appendChild(argSession);
root.appendChild(appDesc);
System.out.println(signerUrl);
System.out.println(request.getSession().getId());
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource source = new DOMSource(document);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
StreamResult result = new StreamResult(bos);
transformer.transform(source, result);
return bos.toByteArray();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (TransformerConfigurationException e) {
e.printStackTrace();
} catch (TransformerException e) {
e.printStackTrace();
}
return null;
}
}
@@ -0,0 +1,92 @@
package info.bukova.isspst.signapi;
import java.io.Serializable;
import java.util.Date;
/**
* @author Pepa Rokos
*/
public class SignData implements Serializable {
private byte[] pdfData;
private byte[] signImg;
private int areaId;
private Date signDate;
private String description;
private String numser;
private boolean signed;
private String signGuid;
private boolean signSuccess;
public byte[] getPdfData() {
return pdfData;
}
public void setPdfData(byte[] pdfData) {
this.pdfData = pdfData;
}
public Date getSignDate() {
return signDate;
}
public void setSignDate(Date signDate) {
this.signDate = signDate;
}
public String getNumser() {
return numser;
}
public void setNumser(String numser) {
this.numser = numser;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public boolean isSigned() {
return signed;
}
public void setSigned(boolean signed) {
this.signed = signed;
}
public String getSignGuid() {
return signGuid;
}
public void setSignGuid(String signGuid) {
this.signGuid = signGuid;
}
public boolean isSignSuccess() {
return signSuccess;
}
public void setSignSuccess(boolean signSuccess) {
this.signSuccess = signSuccess;
}
public byte[] getSignImg() {
return signImg;
}
public void setSignImg(byte[] signImg) {
this.signImg = signImg;
}
public int getAreaId() {
return areaId;
}
public void setAreaId(int areaId) {
this.areaId = areaId;
}
}
@@ -0,0 +1,28 @@
package info.bukova.isspst.signapi;
import java.io.Serializable;
/**
* @author Pepa Rokos
*/
public class SignUploadResponse implements Serializable {
private boolean ok;
private String errorMessage;
public boolean isOk() {
return ok;
}
public void setOk(boolean ok) {
this.ok = ok;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
}
@@ -0,0 +1,133 @@
package info.bukova.isspst.signapi;
import info.bukova.isspst.Constants;
import info.bukova.isspst.SessionData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.ServletContext;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/**
* @author Pepa Rokos
*/
@Controller
public class SigningController {
@Autowired
private SessionData sessionData;
@Autowired
private JnlpGenerator jnlpGenerator;
@Autowired
private ServletContext context;
@RequestMapping(value="/sign/data", method= RequestMethod.GET)
public void dataForSign(HttpServletResponse response, HttpServletRequest request) {
SignData data = (SignData) sessionData.getProperty(Constants.KEY_SIGN_DATA);
ObjectOutputStream os = null;
try {
os = new ObjectOutputStream(response.getOutputStream());
response.setContentType("application/x-java-serialized-object");
os.writeObject(data);
os.flush();
os.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@RequestMapping(value="/sign/data", method= RequestMethod.POST)
public void signedData(HttpServletRequest request, HttpServletResponse response) {
ObjectInputStream ois = null;
ObjectOutputStream os = null;
String error = null;
try {
ois = new ObjectInputStream(request.getInputStream());
SignData data = (SignData) ois.readObject();
sessionData.setProperty(Constants.KEY_SIGN_DATA, data);
} catch (IOException e) {
e.printStackTrace();
error = "IO error";
} catch (ClassNotFoundException e) {
e.printStackTrace();
error = "Loading error";
}
try {
SignUploadResponse resp = new SignUploadResponse();
if (error == null) {
resp.setOk(true);
} else {
resp.setOk(false);
resp.setErrorMessage(error);
}
os = new ObjectOutputStream(response.getOutputStream());
response.setContentType("application/x-java-serialized-object");
os.writeObject(resp);
os.flush();
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@RequestMapping(value="/sign/signer.jnlp", method= RequestMethod.GET)
public void jnlp(HttpServletResponse response) {
byte[] data = jnlpGenerator.generate();
sendByte(response, data, "application/x-java-jnlp-file");
}
@RequestMapping(value="/sign/pdfsigner.jar", method= RequestMethod.GET)
public void pdfsigner(HttpServletResponse response) {
File inputJar = new File(context.getRealPath("/WEB-INF/signer/PDFSigner.jar"));
try {
byte[] data = new byte[(int) inputJar.length()];
FileInputStream is = new FileInputStream(inputJar);
is.read(data);
sendByte(response, data, "application/java-archive");
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private void sendByte(HttpServletResponse response, byte[] data, String contentType) {
ServletOutputStream os = null;
try {
os = response.getOutputStream();
response.setContentType(contentType);
response.setContentLength(data.length);
os.write(data);
os.flush();
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@@ -1,19 +1,32 @@
package info.bukova.isspst.ui.main;
import info.bukova.isspst.Constants;
import info.bukova.isspst.SessionData;
import info.bukova.isspst.StringUtils;
import info.bukova.isspst.data.RequirementBase;
import info.bukova.isspst.reporting.Generator;
import info.bukova.isspst.reporting.GeneratorFactory;
import info.bukova.isspst.reporting.ParamFiller;
import info.bukova.isspst.reporting.Report;
import info.bukova.isspst.reporting.ReportDefinition;
import info.bukova.isspst.services.Service;
import info.bukova.isspst.services.requirement.ApproveException;
import info.bukova.isspst.services.requirement.RequirementBaseService;
import info.bukova.isspst.signapi.SignData;
import info.bukova.isspst.ui.ListViewModel;
import org.zkoss.bind.BindUtils;
import org.zkoss.bind.annotation.BindingParam;
import org.zkoss.bind.annotation.Command;
import org.zkoss.bind.annotation.ExecutionArgParam;
import org.zkoss.bind.annotation.Init;
import org.zkoss.bind.annotation.NotifyChange;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.select.annotation.WireVariable;
import org.zkoss.zul.Messagebox;
import org.zkoss.zul.Window;
import java.util.Date;
import java.util.UUID;
/**
* @author Pepa Rokos
@@ -24,6 +37,16 @@ public class ApproveDialogVM {
private RequirementBase requirement;
private ListViewModel grid;
private Date approveDate;
@WireVariable
private SessionData sessionData;
@WireVariable
private ReportDefinition reportDefinition;
@WireVariable
private GeneratorFactory genFactory;
@WireVariable
private ParamFiller paramFiller;
private boolean signed;
private boolean timer;
public Date getApproveDate() {
return approveDate;
@@ -41,6 +64,9 @@ public class ApproveDialogVM {
this.requirement = requirement;
this.grid = grid;
this.approveDate = new Date();
this.signed = false;
this.timer = false;
}
@Command
@@ -57,4 +83,51 @@ public class ApproveDialogVM {
}
}
@Command
@NotifyChange("timer")
public void signPdf() {
SignData data = new SignData();
sessionData.setProperty(Constants.KEY_SIGN_DATA, data);
data.setSignGuid(UUID.randomUUID().toString());
sessionData.setProperty(Constants.KEY_SIGN_GUID, data.getSignGuid());
// ToDo: Generovat report
reportDefinition.clear();
reportDefinition.setSingleObject(requirement);
reportDefinition.setReport(new Report(0, true, "", "tripRequirementApp", false, true, true));
reportDefinition.setService((Service) service);
paramFiller.fill();
Generator gen = genFactory.createGenerator(reportDefinition);
data.setPdfData(gen.generate());
data.setDescription(requirement.getDescription());
data.setNumser(requirement.getNumser());
timer = true;
Executions.getCurrent().sendRedirect("/api/sign/signer.jnlp", "signer");
}
@Command
@NotifyChange({"signed", "timer"})
public void onTimer() {
String guid = (String) sessionData.getProperty(Constants.KEY_SIGN_GUID);
SignData data = (SignData) sessionData.getProperty(Constants.KEY_SIGN_DATA);
if (guid.equals(data.getSignGuid()) && data.isSignSuccess()) {
signed = true;
timer = false;
} else {
signed = false;
}
}
public boolean isSigned() {
return signed;
}
public boolean isTimer() {
return timer;
}
}