# 附件集成
在附件上传和下载时提供拦截机制.
杀毒:附件上传时结合杀毒软件厂商提供的接口,对文件进行查毒和杀毒。 加密:附件上传下载时使用特定的加密算法对附件进行加解密。 舆情监控:扫描上传附件的内容,禁止含特定关键字的附件进入系统。 审计日志:对附件的上传和下载行为进行记录。
# 接口调用方式
需要在OA Java主程序中调用本接口,依赖OA中相关实例。
# 1.1附件上传
通过监听FileUploadEvent事件可以拦截附件上传进行处理。
适用于对附件进行查毒杀毒或加密等场景。
@ListenEvent(event = FileUploadEvent.class)
public void onFileUpload(FileUploadEvent evt) throws Exception {
// 取得上传的文件
FileItem fileItem = evt.getFileItem();
// doSth
}
如果要终止上传,可以抛异常:throw new BusinessException,此时,上传被取消,前端提示错误,错误信息为异常的message。
如果要更改上传文件的内容,取得Event中的FileItem对象,调用setInputStream设置输入流,此时,保存到磁盘的文件被更改,上传成功。
如果想不终止上传,但给上传完毕要给客户端用户提示时,使用FileItem的appendMessage设置提示。
# 1.2附件下载
通过监听FileDownEvent事件可以拦截附件下载进行处理。
适用于对附件进行加密和解密等场景。
@ListenEvent(event = FileDownloadEvent.class)
public void onFileDownload(FileDownloadEvent evt) throws Exception {
// 取得下载的文件
V3XFile file = evt.getFile();
// doSth
}
# 1.3附件解密
系统中存储的附件都按设置的加密算法进行了加密,如果要手动对存储的附件进行解密,可以调用CoderFactory.getInstance().download方法(依赖seeyon\WEB-INF\lib\seeyon_ctp_core.jar),如下所示。
import com.seeyon.ctp.common.encrypt.CoderFactory;
/**
* 解密附件。
* @param input 附件文件对象
* @param output 解密后的附件
* @throws Exception
*/
public void decrypt(File input, File output) throws Exception {
FileOutputStream fop = null;
FileInputStream fin = null;
try {
fop = new FileOutputStream(output);
fin = new FileInputStream(input);
CoderFactory.getInstance().download(fin, fop);
fop.flush();
fop.close();
} catch (Exception e) {
throw e;
} finally {
try {
if (fop != null) {
fop.close();
}
if (fin != null) {
fin.close();
}
} catch (IOException e) {
}
}
}
# 1.4附件加密
第三方系统可以调用CoderFactory.getInstance().upload方法(依赖seeyon\WEB-INF\lib\seeyon_ctp_core.jar)对文件进行加密。
import com.seeyon.ctp.common.encrypt.CoderFactory;
import com.seeyon.ctp.common.encrypt.ICoder;
/**
* 加密文件。
*
* @param input
* 要加密的文件输入流
* @param output
* 加密后的文件输出流
* @param encryptVersion
* 可选值为轻度加密:ICoder.VERSON01;深度加密:ICoder.VERSON02;不加密:no
* @throws Exception
*/
public void encrypt(FileInputStream in, FileOutputStream out
,String encryptVersion) throws Exception {
CoderFactory.getInstance().upload(in, out, encryptVersion);
}
# 1.5示例
package com.seeyon.apps.test.listener;
import java.io.ByteArrayInputStream;
import javax.servlet.http.HttpServletResponse;
import com.seeyon.ctp.common.exceptions.BusinessException;
import com.seeyon.ctp.common.filemanager.event.FileDownloadEvent;
import com.seeyon.ctp.common.filemanager.event.FileItem;
import com.seeyon.ctp.common.filemanager.event.FileUploadEvent;
import com.seeyon.ctp.common.po.filemanager.V3XFile;
import com.seeyon.ctp.util.annotation.ListenEvent;
//
public class FileEventListener {
// 附件上传监听示例:杀毒
@ListenEvent(event = FileUploadEvent.class)
public void onFileUpload(FileUploadEvent evt) throws Exception {
FileItem fileItem = evt.getFileItem();
String originalFilename = fileItem.getOriginalFilename();
if (originalFilename.contains("有毒无法杀")) {
// 文件有病毒,但杀毒失败,此时抛出异常,提示用户
throw new BusinessException(originalFilename + "有病毒,无法清除");
} else if (originalFilename.contains("有毒已杀")) {
// 文件有病毒,杀毒成功,保存清除病毒后的文件,然后提示用户
fileItem.setInputStream(new ByteArrayInputStream("virus clean"
.getBytes()));
fileItem.appendMessage(originalFilename + "病毒已清除");
}
}
// 附件下载监听示例:加密
@ListenEvent(event = FileDownloadEvent.class)
public void onFileDownload(FileDownloadEvent evt) throws Exception {
V3XFile file = evt.getFile();
if(file==null){
return;
}
// 筛选要处理的文件
if("application/vnd.ms-excel".equals(file.getMimeType())){
HttpServletResponse response = evt.getResponse();
response.setContentType("application/vnd.ms-excel; charset=UTF-8");
response.setHeader("Content-disposition", "");
// 压缩或加密文件
evt.setInputStream(new ByteArrayInputStream("my file"
.getBytes()));
}
}
}
# 查找返回V3XFile对象
Since:V7.0
查找返回V3XFile对象。
接口请求说明
http请求方式:GET
http://ip:port/seeyon/rest/doc/getctpfile/{ctpFileId}
例如:
http://127.0.0.1:8080/seeyon/rest/doc/getctpfile/-7885545461126487774
DEMO:
CTPRestClient client = RestResource.getInstance().resouresClent();
String token = RestResource.getInstance().getToken(client);
client.get("doc/getctpfile/-7885545461126487774",String.class);
返回说明
获取成功,会返回{"code":"0","data":"V3XFile的JSON的Object对象"}
# Rest附件相关接口变动
Since:V9.1 0710 由于安全问题,对于如下接口做了安全整改
接口 | 接口说明 | 处理策略 |
---|---|---|
/rest/attachment/digestv | 获取文件的v | 删除,接口不可用 |
/rest/attachment/removeFile/{fileId} | 删除附件 | 删除,接口不可用 |
/rest/attachment/renameFile/{fileId}/{newName} | 附件重命名 | 删除,接口不可用 |
/rest/attachment/{attachmentId} | 获取附件信息 | 去掉v属性返回 |
/rest/attachment/file/{fileId} | 下载附件 | 增加校验 |
/rest/attachment/file/batch/{fileIds} | 批量下载附件 | 增加校验 |
/rest/attachment/fileforuc/{fileId} | 致信附件下载 | 增加校验 |
/rest/attachment/file/batchDownload/{fileIds} | 批量下载附件 | 增加校验 |
对于“增加校验”的接口。附件下载接口增加了如下校验过程
- a.首先通过查询参数是否有v,如果带有,则通过根据digest(fileId, seed)计算是否与v参数一致
- b.如果参数没有v,则查看是否当前session是否绑定fileId,如果绑定了该fileId,则校验通过
- c.如果a/b两步都没通过,则通过对应fileId的category找到业务校验接口,业务校验接口实现该文件校验,目前只有致信实现了该接口,用于对M3上致信聊天记录中的附件进行下载。
上述a)b)c)三步校验来确保附件下载越权检测
如果使用a策略的校验,需要前端页面请求增加v的查询参数,例如对于rest/attachment/file的下载
例如:
http://127.0.0.1:8080/seeyon/rest/attachment/file/-7885545461126487774?v=32454315235r51
后端业务调用点,如果是通过FileManager获取的com.seeyon.ctp.common.po.filemanager.V3XFile对象,
package com.seeyon.ctp.common.po.filemanager;
import java.io.Serializable;
/**
*
* @author <a href="mailto:tanmf@seeyon.com">Tanmf</a>
* @version 1.0 2006-11-15
*
* @hibernate.class table="v3x_file_mapping"
*/
public class V3XFile extends BasePO implements Serializable {
private String v;
public String getV() {
if (v == null) {
//OA-131736:移动端上传的文件type可能为null
if(id!=null){
v = SecurityHelper.digest(id);
}
}
return v;
}
或者通过AttachmentManager获取到的com.seeyon.ctp.common.po.filemanager.Attachment对象,
package com.seeyon.ctp.common.po.filemanager;
/**
*
*
* @author <a href="mailto:tanmf@seeyon.com">Tanmf</a>
* @version 1.0 2006-11-15
*/
public class Attachment extends BasePO implements Serializable {
public String getV() {
if (v == null) {
if (type == null) {
//OA-131736:移动端上传的文件type可能为null
if (fileUrl != null) {
//OA-132254:PC端处理协同时,插入附件后,点击【提交】协同仍在待办中(修改问题引起的新问题)
v = SecurityHelper.digest(fileUrl);
}
} else {
switch (type) {
case 0:
case 1:
case 3:
case 5:
//附件情况下的安全v串生成规则
if (fileUrl != null) {
v = SecurityHelper.digest(fileUrl);
}
break;
case 2:
case 4:
//关联文档情况下的安全v串生成规则
if (description != null && reference != null) {
v = SecurityHelper.digest(description, reference);
}
break;
}
}
}
return v;
}
这两个对象本来是提供了getV方法,所以业务只需要在业务VO对象里返回附件对象里的v即可。如果业务后端没有调用过FileManager或者AttachmentManager,而是直接通过读取自己业务存储里获取到的fileId,则可以直接在返回VO对象里,通过自己通过com.seeyon.ctp.common.security.SecurityHelper#digest来计算v参数属性,如下
public String getV() {
if (v == null) {
//OA-131736:移动端上传的文件type可能为null
if(id!=null){
v = SecurityHelper.digest(id);
}
}
return v;
}
如果使用b策略的校验,只需要改动后端代码,但是需要梳理清楚,业务前端在什么地方通过后端什么接口获取到的附件列表。 在该后端接口处,使用com.seeyon.ctp.rest.util.SessionFileIdsUtil#add,将fileId绑定到session中。代码样例如下:
