jeecgboot/JeecgBoot · error · JeecgBootException
500
500
Error message
请查看应用key和应用秘钥是否正确,组织ID是否匹配
What it means
Thrown by ThirdAppDingtalkServiceImpl.oauth2Login when JdtOauth2API.getUserAccessToken(clientId, clientSecret, authCode) returns null. The clientId/clientSecret come from SysThirdAppConfig (queried per tenant by configMapper.getThirdConfigByThirdType), and the authCode is the DingTalk免登授权码. A null token means the OAuth2 token exchange at DingTalk failed — almost always a credential or authCode problem.
Source
Thrown at jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/service/impl/ThirdAppDingtalkServiceImpl.java:1104
actionCardMessage.setUserid_list(dtUserIds);
return JdtMessageAPI.sendActionCardMessage(actionCardMessage, accessToken);
}
}
return null;
}
/**
* OAuth2登录,成功返回登录的SysUser,失败返回null
*/
public SysUser oauth2Login(String authCode,Integer tenantId) {
this.tenantIzExist(tenantId);
// 代码逻辑说明: [QQYUN-3440]新建企业微信和钉钉配置表,通过租户模式隔离------------
SysThirdAppConfig dtConfig = configMapper.getThirdConfigByThirdType(tenantId, MessageTypeEnum.DD.getType());
// 1. 根据免登授权码获取用户 AccessToken
String userAccessToken = JdtOauth2API.getUserAccessToken(dtConfig.getClientId(), dtConfig.getClientSecret(), authCode);
if (userAccessToken == null) {
log.error("oauth2Login userAccessToken is null");
throw new JeecgBootException("请查看应用key和应用秘钥是否正确,组织ID是否匹配");
}
// 2. 根据用户 AccessToken 获取当前用户的基本信息(不包括userId)
ContactUser contactUser = JdtOauth2API.getContactUsers("me", userAccessToken);
if (contactUser == null) {
log.error("oauth2Login contactUser is null");
throw new JeecgBootException("获取钉钉用户信息失败");
}
String unionId = contactUser.getUnionId();
// 3. 根据获取到的 unionId 换取用户 userId
String accessToken = this.getTenantAccessToken(dtConfig);
if (accessToken == null) {
log.error("oauth2Login accessToken is null");
throw new JeecgBootException("请查看应用key和应用秘钥是否正确,组织ID是否匹配");
}
Response<String> getUserIdRes = JdtUserAPI.getUseridByUnionid(unionId, accessToken);
if (!getUserIdRes.isSuccess()) {
log.error("oauth2Login getUseridByUnionid failed: " + JSON.toJSONString(getUserIdRes));
throw new JeecgBootException("获取钉钉用户信息失败");View on GitHub (pinned to 96fb33f5ec)
Solutions
- Verify the clientId/clientSecret in sys_third_app_config for the given tenantId and thirdType='dd' against the DingTalk open-platform app page.
- Ensure the authCode is fresh and single-use — do not cache/replay it; initiate the OAuth flow again.
- Confirm the corp/org ID in the config matches the corp that issued the authCode (cross-corp authCodes fail).
- Check the DingTalk app is published/enabled and has the required scopes (contact, auth).
- Enable DingTalk SDK debug logging to see the underlying error response from the token endpoint.
Example fix
// before: token-exchange failure surfaces as a vague message
String userAccessToken = JdtOauth2API.getUserAccessToken(dtConfig.getClientId(), dtConfig.getClientSecret(), authCode);
if (userAccessToken == null) {
log.error("oauth2Login userAccessToken is null");
throw new JeecgBootException("请查看应用key和应用秘钥是否正确,组织ID是否匹配");
}
// after: validate config presence before the call and log the SDK error
if (dtConfig == null || oConvertUtils.isEmpty(dtConfig.getClientId()) || oConvertUtils.isEmpty(dtConfig.getClientSecret())) {
throw new JeecgBootException("钉钉应用配置缺失,请检查租户 " + tenantId + " 的 sys_third_app_config");
} Defensive patterns
Strategy: validation
Validate before calling
// Validate the DingTalk config exists for this tenant before OAuth
SysThirdAppConfig cfg = configMapper.getThirdConfigByThirdType(tenantId, MessageTypeEnum.DD.getType());
if (cfg == null || oConvertUtils.isEmpty(cfg.getClientId()) || oConvertUtils.isEmpty(cfg.getClientSecret())) {
throw new JeecgBootException("钉钉应用配置缺失 (tenantId=" + tenantId + ")");
}
// also ensure authCode is fresh and single-use before passing it in Prevention
- Keep clientId/clientSecret in sys_third_app_config aligned with the DingTalk app page per tenant.
- Never reuse an authCode — it is single-use; initiate the OAuth flow fresh each login.
- Verify the org ID matches the corp issuing the authCode.
When it happens
Trigger: Wrong app key/secret for the corp in SysThirdAppConfig; authCode already used (DingTalk authCodes are single-use) or expired; org ID in the config doesn't match the corp that issued the authCode; app disabled in DingTalk admin console.
Common situations: Copied config from another environment without updating key/secret; clock skew causing authCode rejection; the user switched corps but the config still points at the old one; tenantId resolves to a config row with stale credentials.
Related errors
- 数据源URL配置格式不正确!
- 动态数据源连接失败,dbKey:{dbKey}
- DaoFormat 是 minidao 保留关键字,不允许使用 ,请更改参数定义!
- 上传业务路径深度超出限制!
- 类 {ruleClass} 未实现 IFillRuleHandler 接口
AI-assisted analysis of jeecgboot/JeecgBoot@96fb33f5ec (2026-08-14).
Data as JSON: /api/errors/f0f3b645f74f6234.
Report an issue: GitHub.