使用Open62541连接OPC UA服务器失败:无合适UserTokenPolicy
解决Open62541连接无安全策略OPC UA服务器的用户令牌策略错误
问题分析
你遇到的No suitable UserTokenPolicy found for the possible endpoints错误,核心原因是Open62541默认的用户名密码连接逻辑,在无安全模式(UA_MESSAGESECURITYMODE_NONE)下无法自动匹配服务器支持的用户令牌策略。UaExpert能自动完成适配,但代码中需要手动指定匹配规则。
解决方案
通过手动获取并筛选服务器端点、配置匹配的用户令牌凭证,替代默认的连接逻辑即可解决问题。修改后的代码如下:
#include "open62541.h" #include "ros/ros.h" #include <iostream> int main() { UA_Client *client = UA_Client_new(); UA_ClientConfig *cc = UA_Client_getConfig(client); UA_ClientConfig_setDefault(cc); // 明确设置无安全模式与空安全策略 cc->securityMode = UA_MESSAGESECURITYMODE_NONE; cc->securityPolicyUri = UA_STRING_NULL; const char* serverurl = "opc.tcp://server_ip"; const char* username = "username"; const char* password = "123"; // 1. 获取服务器所有端点列表 UA_EndpointDescription* endpoints = NULL; size_t endpointCount = 0; UA_StatusCode retval = UA_Client_getEndpoints(client, serverurl, &endpoints, &endpointCount); if(retval != UA_STATUSCODE_GOOD || endpointCount == 0) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Failed to get server endpoints"); UA_Client_delete(client); return EXIT_FAILURE; } // 2. 筛选符合要求的端点:无安全模式 + 支持用户名密码令牌 UA_EndpointDescription selectedEndpoint = {}; bool foundValidEndpoint = false; for(size_t i = 0; i < endpointCount; i++) { if(endpoints[i].securityMode == UA_MESSAGESECURITYMODE_NONE) { for(size_t j = 0; j < endpoints[i].userTokenPolicySize; j++) { if(endpoints[i].userTokenPolicy[j].tokenType == UA_USERTOKENTYPE_USERNAME) { selectedEndpoint = endpoints[i]; foundValidEndpoint = true; break; } } if(foundValidEndpoint) break; } } if(!foundValidEndpoint) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "No matching endpoint found"); UA_Array_delete(endpoints, endpointCount, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]); UA_Client_delete(client); return EXIT_FAILURE; } // 3. 配置用户名密码身份凭证 UA_UsernamePasswordLogin login; UA_String_copy(UA_String_fromChars(username), &login.username); UA_String_copy(UA_String_fromChars(password), &login.password); cc->identityManager.setUsernamePassword(cc->identityManager.context, &login); // 4. 使用筛选后的端点建立连接 retval = UA_Client_connect(client, &selectedEndpoint); // 清理临时资源 UA_Array_delete(endpoints, endpointCount, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]); UA_String_delete(&login.username); UA_String_delete(&login.password); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Connection failed: %s", UA_StatusCode_name(retval)); UA_Client_delete(client); return EXIT_FAILURE; } UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Connected successfully!"); UA_Client_disconnect(client); UA_Client_delete(client); return EXIT_SUCCESS; }
关键修改说明
- 端点筛选:主动获取服务器端点并筛选出无安全模式且支持用户名密码令牌的端点,避免默认逻辑匹配失败
- 凭证手动配置:通过
identityManager.setUsernamePassword明确设置身份信息,替代UA_Client_connectUsername的默认适配逻辑 - 安全策略清空:在无安全模式下将
securityPolicyUri设为UA_STRING_NULL,消除默认策略不匹配的风险
内容的提问来源于stack exchange,提问作者DaNilsoN
相关产品推荐
相关产品推荐

