实现指导原则
请参阅以下筛选器实现,以自定义 Windchill 系统中的内容下载:
* 
以下实现仅供参考,可协助创建筛选器实现。所有自定义均应遵循推荐的编码最佳实践以及其他客户特定注意事项。
package wt.content.filter;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* Sample content encryption filter that can be configured to encrypt content upon download.
* If the HTTP request is for content download, the original content file is held in GenericResponseWrapper's data field.
* Original file is encrypted and written into actual ServletResponse.
*/
public class ContentEncryptionFilter implements Filter {

private static final Logger logger = LogManager.getLogger(ContentEncryptionFilter.class.getName());
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
GenericResponseWrapper wrapper = new
GenericResponseWrapper((HttpServletResponse) servletResponse);
filterChain.doFilter(servletRequest, wrapper);
if(logger.isDebugEnabled()) {
logger.debug("ContentEncryptionFilter.doFilter: isDownloadRequest = "+wrapper.isDownloadRequest());
}
if(!wrapper.isDownloadRequest()) {
return;
}
OutputStream os = servletResponse.getOutputStream();
InputStream inputStream = wrapper.getData();
try {
byte[] iv = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
IvParameterSpec ivspec = new IvParameterSpec(iv);
String SECRET_KEY = "my_super_secret_key_ho_ho_ho";
String SALT = "ssshhhhhhhhhhh!!!!";
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(SECRET_KEY.toCharArray(), SALT.getBytes(), 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKeySpec secretKey = new SecretKeySpec(tmp.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivspec);
byte[] inputBytes = new byte[8192];
for (int n = inputStream.read(inputBytes); n > 0; n = inputStream.read(inputBytes)) {
byte[] outputBytes = cipher.update(inputBytes, 0, n);
os.write(outputBytes);
}
byte[] outputBytes = cipher.doFinal();
os.write(outputBytes);
os.flush();
os.close();
} catch (Exception e) {
e.printStackTrace();
}
finally {
inputStream.close();
File tempfile = wrapper.getTempFile();
if(tempfile != null) {
tempfile.delete();
}
}
}
@Override
public void init(FilterConfig arg0) throws ServletException {
}
}
package wt.content.filter;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* Wrapper class for HttpServletResponseWrapper.
* The class determines if the response is for content file download.
*/
public class GenericResponseWrapper extends HttpServletResponseWrapper {
private int contentLength;
private String contentType;
private boolean downloadRequest = false;
private static final Logger logger = LogManager.getLogger(GenericResponseWrapper.class.getName());
private FilterServletOutputStream fsos = null;

public GenericResponseWrapper(HttpServletResponse response) {
super(response);
}
/**
* returns original content file.
* @return
*/
public InputStream getData() {
return fsos.getInputStream();
}

public File getTempFile() {
return fsos.getTempFile();
}
public ServletOutputStream getOutputStream() throws IOException {
if(downloadRequest) {
if(fsos == null) {
fsos = new FilterServletOutputStream();
}
return fsos;
}
return super.getOutputStream();
}

public PrintWriter getWriter() throws IOException {
if(downloadRequest) {
return new PrintWriter(getOutputStream(),true);
}
return super.getWriter();
}
public void setContentLength(int length) {
this.contentLength = length;
super.setContentLength(length);
}
public int getContentLength() {
return contentLength;
}
public void setContentType(String type) {
this.contentType = type;
super.setContentType(type);
}
public String getContentType() {
return contentType;
}
/**
* This method determines if the HTTP request is for content file download.
* Customize it as per requirements. For example you can look for specific content-types like
* application/msword, application/vnd.ms-excel, application/vnd.ms-powerpoint etc.
*/
public void setHeader(String name, String value) {
//if content-disposition header set, its a content file download request
if(name.equalsIgnoreCase("content-disposition")) {
if(contentType != null &&
(contentType.equalsIgnoreCase("application/json")
|| contentType.equalsIgnoreCase("application/json; charset=utf-8")
|| contentType.equalsIgnoreCase("application/ld+json")
|| contentType.equalsIgnoreCase("application/wcdti")
|| contentType.equalsIgnoreCase("text/html"))) {
downloadRequest = false;
logger.debug("GenericResponseWrapper.setHeader: content-disposition, contentType="+contentType+". downloadRequest = "+downloadRequest);
}
else {
downloadRequest = true;
logger.debug("GenericResponseWrapper.setHeader: content-disposition. downloadRequest = "+downloadRequest);
}
}
else if(name.equalsIgnoreCase("content-type")) {
value = value.trim();
contentType = value;
//if content-type value is one of OOTB configured mime types, its a content file download request
if(value.equalsIgnoreCase("application/cals-1840")
|| value.equalsIgnoreCase("application/ed")
|| value.equalsIgnoreCase("application/edz")
|| value.equalsIgnoreCase("application/msaccess")
|| value.equalsIgnoreCase("application/msonenote")
|| value.equalsIgnoreCase("application/msoutlook")
|| value.equalsIgnoreCase("application/msproject")
|| value.equalsIgnoreCase("application/msword")
|| value.equalsIgnoreCase("application/octet-stream")
|| value.equalsIgnoreCase("application/pdf")
|| value.equalsIgnoreCase("application/postscript")
|| value.equalsIgnoreCase("application/pvz")
|| value.equalsIgnoreCase("application/rtf")
|| value.equalsIgnoreCase("application/visio")
|| value.equalsIgnoreCase("application/vnd.groove-injector")
|| value.equalsIgnoreCase("application/vnd.ms-excel")
|| value.equalsIgnoreCase("application/vnd.ms-excel.addin.macroEnabled.12")
|| value.equalsIgnoreCase("application/vnd.ms-excel.sheet.binary.macroEnabled.12")
|| value.equalsIgnoreCase("application/vnd.ms-excel.sheet.macroEnabled.12")
|| value.equalsIgnoreCase("application/vnd.ms-excel.template.macroEnabled.12")
|| value.equalsIgnoreCase("application/vnd.ms-officetheme")
|| value.equalsIgnoreCase("application/vnd.ms-powerpoint")
|| value.equalsIgnoreCase("application/vnd.ms-powerpoint.addin.macroEnabled.12")
|| value.equalsIgnoreCase("application/vnd.ms-powerpoint.presentation.macroEnabled.12")
|| value.equalsIgnoreCase("application/vnd.ms-powerpoint.slide.macroEnabled.12")
|| value.equalsIgnoreCase("application/vnd.ms-powerpoint.slideshow.macroEnabled.12")
|| value.equalsIgnoreCase("application/vnd.ms-powerpoint.template.macroEnabled.12")
|| value.equalsIgnoreCase("application/vnd.ms-project")
|| value.equalsIgnoreCase("application/vnd.ms-publisher")
|| value.equalsIgnoreCase("application/vnd.ms-word.document.macroEnabled.12")
|| value.equalsIgnoreCase("application/vnd.ms-word.template.macroEnabled.12")
|| value.equalsIgnoreCase("application/vnd.openxmlformats-officedocument.presentationml.presentation")
|| value.equalsIgnoreCase("application/vnd.openxmlformats-officedocument.presentationml.slide")
|| value.equalsIgnoreCase("application/vnd.openxmlformats-officedocument.presentationml.slideshow")
|| value.equalsIgnoreCase("application/vnd.openxmlformats-officedocument.presentationml.template")
|| value.equalsIgnoreCase("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|| value.equalsIgnoreCase("application/vnd.openxmlformats-officedocument.spreadsheetml.template")
|| value.equalsIgnoreCase("application/vnd.openxmlformats-officedocument.wordprocessingml.document")
|| value.equalsIgnoreCase("application/vnd.openxmlformats-officedocument.wordprocessingml.template")
|| value.equalsIgnoreCase("application/x-camcad")
|| value.equalsIgnoreCase("application/x-chm")
|| value.equalsIgnoreCase("application/x-gzip")
|| value.equalsIgnoreCase("application/zip")
|| value.equalsIgnoreCase("audio/basic")
|| value.equalsIgnoreCase("audio/wav")
|| value.equalsIgnoreCase("image/bmp")
|| value.equalsIgnoreCase("image/cgm")
|| value.equalsIgnoreCase("image/gif")
|| value.equalsIgnoreCase("image/jpeg")
|| value.equalsIgnoreCase("image/png")
|| value.equalsIgnoreCase("image/slp")
|| value.equalsIgnoreCase("image/stl")
|| value.equalsIgnoreCase("image/tiff")
|| value.equalsIgnoreCase("image/vnd.dwg")
|| value.equalsIgnoreCase("image/vnd.dxf")
|| value.equalsIgnoreCase("model/vrml")
|| value.equalsIgnoreCase("text/plain")
|| value.equalsIgnoreCase("text/sgml")
|| value.equalsIgnoreCase("text/xml")
|| value.equalsIgnoreCase("video/mpeg")
|| value.equalsIgnoreCase("x-ptc/x-asm")
|| value.equalsIgnoreCase("x-ptc/x-dgm")
|| value.equalsIgnoreCase("x-ptc/x-drw")
|| value.equalsIgnoreCase("x-ptc/x-frm")
|| value.equalsIgnoreCase("x-ptc/x-lay")
|| value.equalsIgnoreCase("x-ptc/x-package")
|| value.equalsIgnoreCase("x-ptc/x-part")
|| value.equalsIgnoreCase("x-ptc/x-pic")
|| value.equalsIgnoreCase("x-ptc/x-rep")
|| value.equalsIgnoreCase("x-ptc/x-vpf")
|| value.equalsIgnoreCase("x-unknown/x-unknown")
|| value.equalsIgnoreCase("x-world/x-gaf")
|| value.equalsIgnoreCase("x-world/x-gbf")){
downloadRequest = true;
logger.debug("GenericResponseWrapper.setHeader: content-type="+value+". downloadRequest = "+downloadRequest);
}
else {
logger.debug("GenericResponseWrapper.setHeader: content-type="+value+" not programmed in custom Servlet Filter. downloadRequest = "+downloadRequest);
}
}
super.setHeader(name, value);
}
public boolean isDownloadRequest() {
return downloadRequest;
}
}
package wt.content.filter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Random;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import wt.util.WTProperties;
/**
* Custom OutputStream which holds the data either in memory or in temporary file.
* The data can be later retrieved using getInputStream API.
*/
public class FilterServletOutputStream extends ServletOutputStream {
private OutputStream stream;
private ByteArrayOutputStream output;
private File tempfile;
private long length = 0;
//fine tune property to avoid memory issues.
private static long MAX_FILE_SIZE = 10 * 1024 * 1024;//10MB
private static final Logger logger = LogManager.getLogger(FilterServletOutputStream.class.getName());
public FilterServletOutputStream() {
output = new ByteArrayOutputStream();
stream = new DataOutputStream(output);
}
public void write(int b) throws IOException {
stream.write(b);
length++;
switchToFile();
}
public void write(byte[] b) throws IOException {
stream.write(b);
length += b.length;
switchToFile();
}
public void write(byte[] b, int off, int len) throws IOException {
stream.write(b,off,len);
length += len;
switchToFile();
}
private void switchToFile() {
if(length > MAX_FILE_SIZE) {
if(tempfile == null) {
logger.debug("switching to temp file..");
WTProperties props;
try {
props = WTProperties.getServerProperties();
String WT_TEMP = props.getProperty("wt.temp");
Random random = new Random();
tempfile = new File(WT_TEMP + "/" + random.nextInt() + "_" + System.currentTimeMillis()) ;
FileOutputStream fos = new FileOutputStream(tempfile);
fos.write(output.toByteArray());
stream = fos;
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

@Override
public boolean isReady() {
return false;
}
@Override
public void setWriteListener(WriteListener arg0) {
}
public InputStream getInputStream() {
if(length > MAX_FILE_SIZE) {
try {
stream.flush();
stream.close();
FileInputStream fis = new FileInputStream(tempfile);
return fis;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
else {
ByteArrayInputStream inputStream = new ByteArrayInputStream(output.toByteArray());
return inputStream;
}
}
public File getTempFile() {
return tempfile;
}
}
* 
此配置不适用于像 Creo 及其他 CAD 应用程序这类的客户端 (这些客户端使用客户端连接器缓存来存储内容)。
如果将 servlet 筛选器配置为加密特定的 MIME 类型,则浏览器中显示的下载内容也将被加密,并且可能无法按预期工作。
在 Office 365 应用程序中打开内容文件时,系统不会对其进行加密。
这对您有帮助吗?