jeecgboot/JeecgBoot · error · IllegalArgumentException
非法的任务类名:${classname},仅允许 org.jeecg 包下的Job类
Error message
非法的任务类名:${classname},仅允许 org.jeecg 包下的Job类 What it means
A security guard in QuartzJobServiceImpl.getClass() that blocks arbitrary class instantiation (the classic Quartz RCE vector). Before any reflection, the classname is rejected unless it starts with the literal prefix "org.jeecg.". This is the first line of defense against scheduling a deserialization/gadget class.
Source
Thrown at jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/quartz/service/impl/QuartzJobServiceImpl.java:183
*/
private void schedulerDelete(String id) {
try {
scheduler.pauseTrigger(TriggerKey.triggerKey(id));
scheduler.unscheduleJob(TriggerKey.triggerKey(id));
scheduler.deleteJob(JobKey.jobKey(id));
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new JeecgBootException("删除定时任务失败");
}
}
/**
* 安全加载Job类:仅允许 org.jeecg. 包下的类,且必须实现 org.quartz.Job 接口
*/
private static Job getClass(String classname) throws Exception {
// 包名白名单校验,防止任意类实例化导致RCE
if (classname == null || !classname.startsWith("org.jeecg.")) {
throw new IllegalArgumentException("非法的任务类名:" + classname + ",仅允许 org.jeecg 包下的Job类");
}
//update-begin---author:scott ---date:20260416 for:【PR#9538】Class.forName使用上下文类加载器,增强部署兼容性-----------
Class<?> clazz = Class.forName(classname, true, Thread.currentThread().getContextClassLoader());
//update-end---author:scott ---date:20260416 for:【PR#9538】Class.forName使用上下文类加载器,增强部署兼容性-----------
// 校验是否实现了 org.quartz.Job 接口
if (!Job.class.isAssignableFrom(clazz)) {
throw new IllegalArgumentException("非法的任务类:" + classname + ",必须实现 org.quartz.Job 接口");
}
return (Job) clazz.getDeclaredConstructor().newInstance();
}
}
View on GitHub (pinned to 96fb33f5ec)
Solutions
- Move or wrap the job class under an org.jeecg.* package so it passes the whitelist.
- If you legitimately require another package, extend the whitelist check in getClass() and review the RCE exposure first.
- Ensure jobClassName is non-null and correctly prefixed before persisting it.
Example fix
// before: third-party job
package com.acme.jobs;
public class AcmeJob implements Job { ... }
// after: place under the whitelisted package (or delegate)
package org.jeecg.modules.acme.job;
public class AcmeJob implements Job { ... } Defensive patterns
Strategy: validation
Validate before calling
// Reject non-whitelisted class names before they reach the scheduler.
private static final String JOB_PREFIX = "org.jeecg.";
if (jobClassName == null || !jobClassName.startsWith(JOB_PREFIX)) {
throw new IllegalArgumentException("非法的任务类名: " + jobClassName);
} Type guard
public static boolean isWhitelistedJobClassName(String name) {
return name != null && name.startsWith("org.jeecg.");
} Try / catch
try {
quartzJobService.schedulerAdd(job);
} catch (JeecgBootException e) {
if (e.getMessage() != null && e.getMessage().contains("仅允许 org.jeecg")) {
return Result.error("任务类必须在 org.jeecg 包下");
}
throw e;
} Prevention
- Enforce the org.jeecg.* package convention for all job classes via code review.
- Do not relax the whitelist without a threat assessment — it is an RCE guard.
- Validate jobClassName format on the controller before persisting.
When it happens
Trigger: Submitting a jobClassName that does not start with "org.jeecg." — e.g. an ysoserial gadget like "org.apache.commons.collections.functors...", a third-party class "com.example.MyJob", or a null value (the null check also routes here).
Common situations: Legitimate job classes placed outside the jeecg package; copy-paste from a tutorial using a different package; adversarial/admin input attempting to trigger a gadget chain; null jobClassName from a malformed request.
Related errors
- 后台找不到该类名:${jobClassName}
- 非法的任务类:${classname},必须实现 org.quartz.Job 接口
- 请注意,将要排序的列字段不存在:${column}
- 未获取到用户
- Illegal access to path outside of base directory.
AI-assisted analysis of jeecgboot/JeecgBoot@96fb33f5ec (2026-08-14).
Data as JSON: /api/errors/1ca5bc63c7797f49.
Report an issue: GitHub.