news 2026/10/4 6:41:10

Java连接OPC Server报Access is denied?DCOM权限配置与排查指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java连接OPC Server报Access is denied?DCOM权限配置与排查指南

搞Java的人第一次去连OPC Server,十有八九会撞上这个异常:

org.jinterop.dcom.common.JIException: Access is denied

它出现的时机通常都在创建DCOM会话、调用CoCreateInstanceEx 那一步,也就是程序刚尝试连接远程OPC Server,或者还在网络协商阶段就抛出来了。明明代码是照着教程抄的,用户名、密码也都填了,可它就是要拒绝你。尤其当你在Windows防火墙已经关闭、网络能ping通的情况下还看到这个错误,那种挫败感我太熟悉了。

这篇文章我从问题根因说起,把DCOM这套老旧的访问控制机制拆开讲清楚,再给出dcomcnfg的详细配置步骤、Java侧连接代码的注意点、以及一套能直接落地的排查顺序。适合正在用Utgard、j-Interop这类库对接OPC DA的程序员,也适合第一次接触OPC集成、被Windows权限折腾到怀疑人生的新手。如果你是做OPC UA的,可以关掉了,这个异常基本只在OPC DA + DCOM这条老链路上出现。

1. Access is denied 到底是谁在拒绝你

1.1 整条技术链:Java 是怎么够到 OPC 的

Java本身跑在JVM里,而OPC DA(Data Access)是构建在Windows COM/DCOM之上的老协议。两端语言都不一样,中间必须有一座桥。实际项目中最常见的链路是:

Java应用 → OpenSCADA Utgard → j-Interop → DCOM网络协议 → 远程Windows机器上的OPC Server

Utgard是Java生态里比较老牌的开源OPC库,它封装了OPC DA 2.0/3.0的客户端逻辑;底层依赖j-Interop(也就是你报错信息里那个org.jinterop包),用纯Java重新实现了DCOM协议,所以客户端机器上不需要安装任何Windows COM组件或DLL。j-Interop的任务,就是在Java和远程Windows之间模拟出一个DCOM客户端来。

整个流程大致是这样:你的Java程序先通过j-Interop建立一个JISession会话,然后向远程机器的RPC Endpoint Mapper(也就是135端口)发起请求,申请创建一个OPC Server组件的实例。创建成功后,再通过DCOM调用OPC Server暴露出来的接口去读写点位数据。

注意“创建实例”这步很关键。一个OPC Server在Windows里是一个COM组件,你想远程使用它,不光要网络通、账户对,还要Windows的DCOM授权机制点头。任何一个环节不放行,结果就是一个错误码:

0x80070005 E_ACCESSDENIED

j-Interop收到这个错误,就包装成你看到的JIException: Access is denied。

1.2 它不是登录失败,而是“被门禁拦住了”

很多人把Access is denied理解为用户名或密码错误,这个理解方向不完全对。它更像是一个门禁系统:你到了楼下,有门禁卡(用户名密码),但系统没有给这张卡开放“进入这栋楼”的权限,于是门就是不打开。

DCOM的权限体系里至少有三道门:

  • 访问权限(Access Permissions):决定谁能远程调用这个COM组件的方法。
  • 启动和激活权限(Launch and Activation Permissions):决定谁能远程创建、激活这个COM组件的实例。
  • 身份验证级别和模拟级别:决定身份是以什么安全级别在会话中传递,以及能否模拟客户端身份去访问其他资源。

你代码里填的用户名密码只是让DCOM知道“你是谁”,但实际问题往往是“你是谁”这个答案本身没有出现在允许列表里。所以光靠修Java代码解决不了,要去Windows那一侧给这个用户授权。这也是为什么网上搜这个错误,几乎所有人最后都指向同一个操作:打开dcomcnfg 改权限。

2. DCOM 权限模型里,最容易踩的坑在哪

2.1 “编辑默认值”和“编辑限制”是两回事

打开组件服务之后,在“我的电脑”属性里点进COM安全,你会看到两组设置:访问权限、启动和激活权限,每组下面又有“编辑默认值”和“编辑限制”两个按钮。

这两者的区别很多人搞不清。默认值是对没有单独配置安全属性的组件生效的统一授权;限制则是一个强制底线,优先级高于任何组件自己的配置。换句话说,哪怕你在某个具体组件的属性里给了用户“允许”,如果“编辑限制”里没有给,最终结果还是拒绝。

很多人改完默认值发现没用,就是因为没动限制:

  • 访问权限的两处:默认值和限制,都要添加用户,并且勾选“远程访问”。
  • 启动和激活权限的两处:默认值和限制,都要添加用户,并且勾选“远程启动”和“远程激活”。

我第一次配置时也踩过这个坑,只改了默认值,重启服务后连接还是Access is denied,后来把限制也改了,问题消失。所以配置时要记住:两侧同步,缺一不可。

2.2 用户选谁、加哪些账户,是有讲究的

在“编辑默认值”和“编辑限制”里需要添加哪些账户,取决于你的实际部署环境。常规做法是添加三类:

  • Everyone:这一项能保证授权覆盖范围最大,适合测试环境快速验证。
  • ANONYMOUS LOGON:用于匿名远程激活,如果局域网里没有域环境、工作组模式下非常常见。
  • 实际运行j-Interop的用户账户:比如你代码里填的ci.user,或者目标机器上的本地管理员账户。

要注意的是,某些Windows安全策略默认禁止匿名枚举、禁止匿名访问某些对象。如果你选择了添加ANONYMOUS LOGON,还需要确认本地安全策略里“网络安全:允许对SAM账户和共享的匿名枚举”这类选项没有被强行锁死。否则即使给了匿名登录权限,系统也可能不认。

在一个没有域控的工作组环境里,我建议直接用目标机上已有的管理员账户,把“Everyone”和“ANONYMOUS LOGON”都先加上,把环境跑通后再按最小权限原则收紧。生产环境千万别一上来就追求“最小权限”,否则你会分不清到底是权限不够还是配置没生效,排查周期会拉得很长。

2.3 网络访问模型和“拒绝从网络访问”的坑

Windows本地安全策略里有一个位置非常隐蔽,但它能让你所有权限配置都白费。在“本地策略→用户权限分配”下面,有两项:

  • 从网络访问此计算机
  • 拒绝从网络访问此计算机

默认情况下,普通管理员账号是允许从网络访问的。但在某些精简版系统、或者被安全加固过的服务器上,“拒绝从网络访问这台计算机”里可能会带着Guest或者其它账户组。如果你Java连接代码用的用户正好被放在了这个拒绝列表里,那DCOM请求过来时,系统会直接拒绝,日志里也像Access is denied。

另一个坑是“网络访问:本地账户的共享和安全模型”。在Windows 10/Server 2016之后,这个策略有两个选项:

  • 仅来宾:所有网络用户都映射为Guest账户,这种模式下即使你填了管理员密码,远程创建DCOM实例也极大概率失败。
  • 经典:本地用户以自身身份验证,这才是DCOM远程访问通常需要的模式。

如果你的机器是刚装好的Windows系统,先确认这两处策略没有拖后腿,再继续往下配置。

3. dcomcnfg 完整配置步骤,照着做就行

3.1 先确认 OPC Server 已经以 DCOM 方式注册

权限配置的前提是这个OPC Server组件已经被正确安装并注册到Windows的COM注册表里。如果你用的是KepServerEX、Matrikon OPC Simulation,或者西门子、罗克韦尔等厂家的OPC Server,安装完成后通常会自动注册。

可以用两种方式确认:

  1. 打开dcomcnfg → 组件服务 → 计算机 → 我的电脑 → DCOM配置,在列表里找有没有对应的OPC Server项。名字一般类似KEPware.KEPServerEX.V6、Matrikon.OPC.Simulation.1这类,或者带有OPC关键字。
  2. 在注册表里搜ProgId字符串:开始菜单搜索regedit,在HKEY_CLASSES_ROOT下搜索你OPC Server的ProgId名称,能找到对应的CLSID。

如果DCOM配置列表里看不到OPC Server,常见原因是OPC Core Components没装,或者某些绿色精简版OPC Server没有注册过COM。装一次OPC Core Components Redistributable 3.00.107,问题往往就解决了。

3.2 逐项配置 COM 安全,这里是最核心的一步

我以Windows Server 2019 / Windows 10为例,完整步骤是这样:

  1. 按Win + R,输入dcomcnfg,回车,打开组件服务。
  2. 左侧导航:组件服务 → 计算机 → 我的电脑。
  3. 右键“我的电脑”,选择“属性”。
  4. 在“默认属性”页签里,确认:
    • “在此计算机上启用分布式COM”是勾选状态。
    • 默认身份验证级别选择“连接”。
    • 默认模拟级别选择“标识”。
  5. 在“默认协议”页签里,确认有“面向连接的TCP/IP”,如果没有就添加。
  6. 切到“COM安全”页签,开始正式配置。

访问权限的配置:

  • 点击“访问权限”区域里的“编辑默认值”,添加以下账户,并勾选“远程访问”:
    • Everyone
    • ANONYMOUS LOGON
    • 你实际连接用的Windows账户
  • 再点击“编辑限制”,做同样的添加和“远程访问”勾选。
  • 确定保存。

启动和激活权限的配置:

  • 点击“启动和激活权限”区域里的“编辑默认值”,添加同样几个账户,并勾选“远程启动”和“远程激活”。
  • 再点击“编辑限制”,重复同样的操作。
  • 确定保存。

配置完“我的电脑”这层,再去DCOM配置列表里找到你的OPC Server项:

  1. 右键组件,选择“属性”。
  2. 在“身份”页签里,选择“交互式用户”。有些服务型OPC Server会建议“指定用户”并填入高权限账户,这个可以按OPC Server的产品文档来。
  3. 在“安全”页签里,如果你希望针对这个组件单独设置权限,可以选择“自定义”,然后继续添加账户授权。实际上只要“我的电脑”层的权限都放开了,这里通常保持默认即可。

全部改完后,最好重启一次系统,或者至少重启OPC Server服务。DCOM权限的生效依赖系统服务重新加载,完全不用重启的情况也不是没有,但重启后确认最省心。

3.3 防火墙也要管:不只是放行 135 端口

DCOM远程调用的第一步是访问TCP 135端口。但这个端口只负责“牵线”,真正传输调用数据的端口是从系统动态端口范围里随机分配的,通常是高速TCP端口。所以只放行135端口是不够的,否则会出现“一开始好像连上了,随后又超时或拒绝”的诡异现象。

我通常用两条防火墙规则解决:

# 放行 RPC Endpoint Mapper netsh advfirewall firewall add rule name="DCOM TCP 135" dir=in action=allow protocol=TCP localport=135 # 放行动态 RPC 端口范围(根据系统版本动态端口可能不同,一般是 49152-65535) netsh advfirewall firewall add rule name="DCOM TCP Dynamic" dir=in action=allow protocol=TCP localport=49152-65535

在测试环境里,我会先直接关闭Windows防火墙来确认问题是否出在防火墙侧:

# 临时关闭防火墙(仅测试环境) netsh advfirewall set allprofiles state off

如果关了防火墙就通了,那基本可以确定是端口放行不完整。

顺带一提,某些网络环境里UDP 135、UDP 137-138、TCP/UDP 445也会被DCOM相关的发现、浏览机制用到。严格来说,只要OPC Server地址和CLSID是明确的,走TCP 135 + 动态端口就够了,但如果你用主机名做host,NetBIOS和DNS解析就可能牵涉445端口。为了快速跑通,测试环境把防火墙全部关闭验证一次是最高效的办法。

4. Java 连接代码与 j-Interop 侧的注意事项

4.1 一个能跑的连接示例

依赖方面,如果你用Maven,核心依赖如下:

<dependency> <groupId>org.openscada.utgard</groupId> <artifactId>org.openscada.utgard</artifactId> <version>1.5.0</version> </dependency> <dependency> <groupId>org.jinterop</groupId> <artifactId>org.jinterop</artifactId> <version>2.4.7</version> </dependency>

如果你的公司私服拉不到这些旧依赖,去Maven Central直接搜索utgard和org.jinterop也能找到对应坐标。正因为这些库维护得不活跃,我强烈建议锁定版本,不要盲目升到最新,否则API可能有变化。

连接代码的骨架大致是这样:

import org.openscada.opc.lib.da.ConnectionInformation; import org.openscada.opc.lib.da.OPCServer; public class OpcConnectionTest { public static void main(String[] args) { // 有些容器环境需要显式设置上下文ClassLoader,否则j-Interop反射时会出问题 Thread.currentThread().setContextClassLoader( OpcConnectionTest.class.getClassLoader() ); ConnectionInformation ci = new ConnectionInformation(); ci.setHost("192.168.1.100"); // OPC Server机器IP ci.setDomain("WORKGROUP"); // 域;工作组就填WORKGROUP或机器名 ci.setUser("administrator"); // 有DCOM远程权限的账户 ci.setPassword("your_password"); ci.setClsid("F8582CF2-88FB-11D0-B850-00A0C922A001"); // OPC Server组件的CLSID // 或者用ProgId,二选一: // ci.setProgId("KEPware.KEPServerEX.V6"); OPCServer server = new OPCServer(); try { server.connect(ci); System.out.println("连接成功,当前OPC Server: " + server.getServerState()); // 后续就是获取group、添加item、读取值 // ... } catch (Exception e) { e.printStackTrace(); } finally { try { server.disconnect(); } catch (Exception e) { // 忽略断开异常 } } } }

我建议测试时先用clsid连接,而不是用progId,因为progId解析额外依赖OPCEnum服务,如果OPC Core Components没装好,解析会失败。CLSID需要从OPC Server的产品文档或注册表里查。KepServer的CLSID不同版本不一样,不要直接抄网上的。

4.2 两个经常被忽略的 Java 侧问题

第一个是ClassLoader。j-Interop内部某些类加载操作走的是线程上下文ClassLoader。如果你把连接代码写在Tomcat、Spring Boot或者其他类加载器隔离比较严格的容器里,偶尔会报奇怪的ClassNotFoundException,甚至表现为连接建立到一半就失败。提前加一行Thread.currentThread().setContextClassLoader(...),能省很多排查时间。

第二个是会话生命周期。每次连接都应该使用独立的JISession,用完必须销毁。如果不销毁,Windows端会积累大量来自同一客户机的DCOM会话,一段时间后新连接会被Windows拒绝,表现仍是Access is denied。很多人查了半天权限,结果发现是之前的测试会话没清理干净。

在Windows端看到会话堆积的办法:目标机器打开“计算机管理 → 系统工具 → 共享文件夹 → 会话”,如果看到大量来自你本机的网络连接,基本就是会话没释放。

另外,JDK版本要特别注意。j-Interop是很多年前的库,在JDK 8上运行最稳定。我遇到过在JDK 11及以上版本调用时出现模块化相关的IllegalAccessError,这不是业务代码问题,而是JDK强封装导致的反射受限。如果你必须用JDK 11+,可以尝试加上:

--add-opens java.base/java.lang=ALL-UNNAMED

但坦白讲,这只能缓解一部分问题,最稳妥的还是JDK 8。

5. 一套实用的排障路线,五分钟定位问题

5.1 排查顺序:先剥开 Java,再处理 Windows

遇到Access is denied,我建议不要急着改代码,先按下面的顺序排查:

  1. 用现成的OPC客户端工具验证。在目标机器或同网络的另一台Windows机器上装一个Matrikon OPC Explorer,尝试远程连接同一个OPC Server。如果OPC Explorer也连不上,那问题百分百不在Java侧,而是DCOM、防火墙或OPC Server本身的问题。这个步骤能直接帮你把方向定下来。
  2. 在目标Windows机器上打开事件查看器,看“应用程序”日志里有没有DCOM相关报错。最重要的两个事件ID是10005和10010,它们会明确告诉你:哪个用户尝试激活哪个CLSID的组件、因为什么权限失败。看到CLSID后,再去注册表确认是不是你的OPC Server。
  3. 检查防火墙端口。先看135端口通不通,再确认动态端口范围。实在不行测试环境临时关防火墙验证。
  4. 最后再看Java代码:用户、域、CLSID是否和Windows那边一致,ClassLoader是否设置,会话是否正常销毁。

这个顺序能避免你在Java代码里反复折腾,最后发现是Windows权限没给。

5.2 常见错误与解决方法速查

错误信息可能原因处理方式
JIException: Access is deniedDCOM访问权限或启动激活权限不足dcomcnfg添加用户并勾选远程访问、远程启动、远程激活
JIException: Access is denied且固定发生在运行一段时间后会话没释放,Windows端DCOM会话堆积确保每次连接后disconnect,必要时重启OPC Server服务
0x800706ba: The RPC server is unavailable135端口不通、防火墙拦截、目标机器远程服务不可用检查135端口和动态RPC端口放行
0x80040154: Class not registeredCLSID错误、OPC Core Components缺失、OPC Server未注册核对CLSID,安装OPC Core Components,重新注册OPC Server
0x80070002: The system cannot find the file specifiedProgId解析失败或注册信息损坏改用CLSID连接,或修复OPC Server安装
ClassNotFoundException依赖缺失或上下文ClassLoader不对补全依赖,设置Thread.currentThread().setContextClassLoader

这张表我是在项目里整理出来的。以前接手过一台很老的工控机,对方说Java连不上OPC,我远程一看,和代码一点关系没有,纯粹是OPC Server那个Windows账户被人从“从网络访问此计算机”里移掉了,加上事件日志里10010一直刷。把用户加回允许列表,立刻就好。

6. 最后再分享一点实际操作中的体会

DCOM这套东西设计于上世纪九十年代,安全模型复杂,调试手段又少,和现代化的API风格完全不是一个时代的产品。我的体会是,遇到Access is denied,心态上要接受“大概率是Windows权限问题”,而不是一直在Java代码里钻牛角尖。

有几个小技巧对排查很有帮助:第一次配置时可以把Everyone和ANONYMOUS LOGON都加上,跑通后再按最小权限原则收缩到指定用户;配置完dcomcnfg后不要舍不得重启,重启一次能排除配置未生效的干扰;连接代码里把日志级别调到DEBUG,如果j-Interop能输出更详细的DCOM握手日志,很多报错原因会更早暴露出来。

另外,如果你的项目是从零开始、OPC可是可选的,我的建议很直接:上OPC UA。OPC UA摆脱了DCOM依赖,Java端有Eclipse Milo这种维护活跃、文档齐全的库,连接配置比OPC DA简单太多,也不用跟Windows权限纠缠。老一辈的OPC DA设备没法升级时,才需要和DCOM硬碰硬。这篇配置方案也是给这些“不得不兼容老设备”的场景兜底用的,希望它能让你少熬几个夜。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/4 6:40:22

Claude Code 实战指南:从安装到本地模型接入的完整玩法

说实话&#xff0c;我对 Claude Code 一开始是持保留态度的。作为一款 AI 编程助手&#xff0c;它连个正经图形界面都没有&#xff0c;就是跑在终端里的命令行工具。但用了两周之后我承认&#xff0c;它是目前把“自然语言变成真实代码改动”这件事做得最透彻的工具之一。它不是…

作者头像 李华
网站建设 2026/10/4 6:36:12

OpenShell使用指南:定制经典开始菜单与提升操作效率

1. 先说清楚&#xff1a;OpenShell 是干什么的Windows 10/11 的用户&#xff0c;尤其是从 Win7 时代一路迁移过来的老用户&#xff0c;大概率都经历过同一个崩溃瞬间&#xff1a;点开开始菜单&#xff0c;迎面是满屏的动态磁贴或者推荐软件列表&#xff0c;想找一个本地安装的程…

作者头像 李华
网站建设 2026/10/4 6:35:57

Obsidian + WorkBuddy + Gitee:构建可对话、可追溯的个人知识库

1. 为什么我要把 Obsidian、WorkBuddy 和 Gitee 拼在一起用先说结论&#xff1a;这套组合解决的核心问题只有一个——让个人知识库从“静态笔记堆”变成“能对话、能追溯、能回滚的活系统”。我用了三年 Obsidian&#xff0c;笔记攒了四千多条&#xff0c;但真正回头翻的不到百…

作者头像 李华
网站建设 2026/10/4 6:34:17

10款降AI率工具实测,专科论文怎么选才靠谱

专科论文写作进入2026年&#xff0c;降AI率成了绕不开的一关。不少学校对毕业论文的AI生成内容检测越来越严&#xff0c;重复率过了、AI检测却亮红灯的情况比比皆是。市面上号称能降AI率的工具五花八门&#xff0c;实际效果参差不齐。这篇盘点从专科生实际写作场景出发&#xf…

作者头像 李华
网站建设 2026/10/4 6:32:49

MR25H40CDF与PIC32组合:工业存储的MRAM高速读写方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华