E9附件给第三方下载,两种免登方案一次讲清
- 2026-10-31 18:38:42
“第三方系统要下载 OA 里的文件,有什么接口可以调用么?”
这个问题我被问过不止一次。
答复通常就两句话:有,而且不止一个。E9 给了两条现成的路:ddcode 免登下载,和 ssoToken 认证下载。路都是通的,只是坑藏得深。
这篇把两种方案的代码、参数和踩过的坑一次讲清,你可以直接对着抄。
一、先看问题在哪
E9 的标准下载地址长这样:
标准下载地址
OA地址/weaver/weaver.file.FileDownload?fileid=附件id&download=1
这个地址要求用户已登录,而且对附件对应的文档有下载权限。
第三方系统没有 OA 的 session,直接 GET 这个地址,返回的要么是 403,要么是一个登录页。
所以要免登。E9 里能走的路有两条,先说官方内置的 ddcode。
二、方案一,ddcode 免登下载
1. 它是什么
ddcode 是服务端生成的有时效的加密令牌,本质是把用户信息和文件信息用 DES 加密后的字符串。
在下载地址后面追加这个参数,就能绕过登录态校验:
免登下载地址
OA地址/weaver/weaver.file.FileDownload?fileid=附件id&download=1&ddcode=xxxxxx
它有三个特征:同一个文件不同时间生成的值不同;跟用户身份绑定;默认 5 分钟后失效。
2. 怎么生成
写法一,用 DesUtils 直接加密,简单直接:
JAVA · 简单写法
weaver.docs.docs.util.DesUtils des = new weaver.docs.docs.util.DesUtils();
String ddcode = "1" + "_" + 附件id; // "1" 为用户ID,替换成实际有权限的用户
ddcode = des.encrypt(ddcode);
写法二,用官方工具方法 SystemDocUtil.takeddcode,推荐这个,能显式控制时效和权限:
JAVA · 推荐写法
import com.api.doc.mobile.systemDoc.util.SystemDocUtil;
User user = new User(1); // 传哪个用户,就按谁的权限校验
Map<String, String> params = new HashMap<>();
params.put("timelimit", "0"); // 取消时效限制,非必要别用
params.put("adminright", "1"); // 管理员权限
String ddcode = SystemDocUtil.takeddcode(user, fileid + "", params);
doc_writelog_configovertime 项,单位分钟 | |
timelimit=0 | |
adminright=1 | |
3. 权限怎么校验
规则就两条:当前请求有 session,按 session 用户的权限来;没有 session,按 ddcode 里携带的用户权限来。
同时不管哪种,都要求这个用户对附件对应的文档有下载权限。文档权限在「文档详情页 → 文档共享 TAB」里能查。
4. 第三方怎么接入
关键约束:生成 ddcode 的代码只能在 OA 内部调用,加密密钥拿不出来,第三方自己签不了。
所以标准姿势是:
1. 在 OA 侧开发一个接口,入参是附件 ID 或 requestid;2. OA 侧生成 ddcode,拼好完整的免登下载地址;3. 接口把这个 URL 返回给第三方,第三方直接 GET 拿文件流。
URL 模板里有个小坑,requestid 参数必须保留为空字符串,不写的话系统会去校验流程权限,反而下载失败:
完整 URL 模板
http://[OA地址]/weaver/weaver.file.FileDownload?fileid={imagefileid}&download=1&requestid=&ddcode={加密串}
三、方案二,ssoToken 认证下载
1. 思路
第三方先调 /ssologin/getToken 接口,用 appid 和 loginid 换一个 token,再把 token 拼到下载地址后面:
TOKEN 下载地址
OA地址/weaver/weaver.file.FileDownload?fileid=附件id&download=1&ssoToken=xxx
下面这段是我实际在用的工具方法,传多个 docid 会自动分割,一次返回所有下载链接:
JAVA · GETFILEURL.JAVA
/**
* 根据附件的docid生成可下载的链接地址,用于oa传给第三方附件
* @param appid getToken 用的应用id
* @param address OA 的 ip 地址
* @param loginid 用于 getToken 的登录名
* @param docid 附件的id,就是流程附件字段值,可能多个,直接传,方法里会分割
*/
public static List getFileUrl(String appid, String address, String loginid, String docid) {
List fileList = new ArrayList<>();
RecordSet rs = new RecordSet();
String[] items = docid.split(",");
for (int i = 0; i < items.length; i++) {
// 获取Token
String token = HttpRequest.get(address + "/ssologin/getToken?appid=" + appid + "&loginid=" + loginid + "").execute().body();
// 根据docid获取imagefileid
String getImageFileId = "select imagefileid from docimagefile where docid=?";
rs.executeQuery(getImageFileId, items[i]);
rs.next();
// 拼接下载链接
String downloadUrl = address + "/weaver/weaver.file.FileDownload?fileid=" +
Util.null2String(rs.getString("imagefileid")) + "&download=1&ssoToken=" + token;
fileList.add(downloadUrl);
}
return fileList;
}
代码在 E9 的 Java 环境里跑,RecordSet、Util、HttpRequest 都是平台自带的,不用额外引包。
2. 那个必踩的坑
地址拼好,一访问,还是提示没有权限。
我在这上面排查了半天,最后发现原因很朴实:ecology/WEB-INF/web.xml 里的单点过滤器 WeaverLoginFilter,只拦截了 *.jsp 和 *.html,压根没拦 /weaver/weaver.file.FileDownload 这个地址。
请求没进过滤器,单点自然不生效,token 等于白拼。
解决办法是在 filter-mapping 里增加一行拦截配置:
XML · WEB.XML
<filter>
<filter-name>WeaverLoginFilter</filter-name>
<filter-class>weaver.weaversso.WeaverLoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>WeaverLoginFilter</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>WeaverLoginFilter</filter-name>
<url-pattern>*.html</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>WeaverLoginFilter</filter-name>
<url-pattern>/weaver/weaver.file.FileDownload</url-pattern>
</filter-mapping>
加完重启,token 方案才算真正通了。
注意:集群环境下,各个节点的 web.xml 都要加,漏一个节点,负载均衡分过去的那部分请求照样 403。
四、两种方案怎么选
我的选型习惯:点对点给个链接让人点,用 ddcode,省事;系统对系统批量搬文件,用 ssoToken,一次认证反复用。
五、踩坑清单
1. fileid 是 imagefileid,不是 docid。中间隔着一张 docimagefile 映射表,直接拿 docid 去下载必失败。流程附件可以从 workflow_requestdoc 按 requestid 查 docid,再转一次。
2. ddcode 方案的 URL 里 requestid 必须保留为空。写上值反而触发流程权限校验,下载失败。
3. ddcode 默认只有 5 分钟。链接即取即用,别缓存复用,真要加长就改 doc_writelog_config 的 overtime。
4. token 方案报没有权限,先查 web.xml。九成是 WeaverLoginFilter 没拦 FileDownload,加一行 url-pattern 就好。
5. 集群环境 web.xml 每个节点都要改。改了一个节点测试通了,不代表上线没问题。
6. 别拿 User(1) 签发 ddcode。等于把管理员下载权限外放,链接一旦泄露风险很大,用只对目标附件有权限的专用账号。
六、安全上的两句提醒
ddcode 和 token 本质都是把「某个身份的下载权限」放进了一个 URL。URL 会转发、会进日志、会被人截图。
所以按最小权限来:专用账号签发、短时效、OA 侧收敛接口,只暴露「按附件 ID 返回下载地址」这一个能力,加密逻辑和密钥永远不出 OA。
另外泛微的下载接口历史上出过未授权访问漏洞,上线前把补丁版本核一遍,不亏。
小结
两种方案说到底就一件事:把登录态变成一个可控的、有时效的、绑权限的参数。
偶发外传、门户列表,ddcode 够用;系统级批量同步,ssoToken 更顺手;哪条路都别用管理员身份裸奔。
你在对接第三方下载时踩过什么坑?留言区聊一句。
关注『FW落地手记』,每周分享泛微 E9 / E10 的二开与低代码实战技巧。