{"record":{"id":"26ad1404df5d41a9","repo":"jeecgboot/JeecgBoot","slug":"classname-org-quartz-job","errorCode":null,"errorMessage":"非法的任务类：${classname}，必须实现 org.quartz.Job 接口","messagePattern":"非法的任务类：(.+?)，必须实现 org\\.quartz\\.Job 接口","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/quartz/service/impl/QuartzJobServiceImpl.java","lineNumber":190,"sourceCode":"\t\t\tlog.error(e.getMessage(), e);\n\t\t\tthrow new JeecgBootException(\"删除定时任务失败\");\n\t\t}\n\t}\n\n\t/**\n\t * 安全加载Job类：仅允许 org.jeecg. 包下的类，且必须实现 org.quartz.Job 接口\n\t */\n\tprivate static Job getClass(String classname) throws Exception {\n\t\t// 包名白名单校验，防止任意类实例化导致RCE\n\t\tif (classname == null || !classname.startsWith(\"org.jeecg.\")) {\n\t\t\tthrow new IllegalArgumentException(\"非法的任务类名：\" + classname + \"，仅允许 org.jeecg 包下的Job类\");\n\t\t}\n\t\t//update-begin---author:scott ---date:20260416  for：【PR#9538】Class.forName使用上下文类加载器，增强部署兼容性-----------\n\t\tClass<?> clazz = Class.forName(classname, true, Thread.currentThread().getContextClassLoader());\n\t\t//update-end---author:scott ---date:20260416  for：【PR#9538】Class.forName使用上下文类加载器，增强部署兼容性-----------\n\t\t// 校验是否实现了 org.quartz.Job 接口\n\t\tif (!Job.class.isAssignableFrom(clazz)) {\n\t\t\tthrow new IllegalArgumentException(\"非法的任务类：\" + classname + \"，必须实现 org.quartz.Job 接口\");\n\t\t}\n\t\treturn (Job) clazz.getDeclaredConstructor().newInstance();\n\t}\n\n}\n","sourceCodeStart":172,"sourceCodeEnd":196,"githubUrl":"https://github.com/jeecgboot/JeecgBoot/blob/96fb33f5ec68516da0b0147da06b2eb0419e063a/jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/quartz/service/impl/QuartzJobServiceImpl.java#L172-L196","documentation":"The second guard in getClass(): after the whitelist passes and Class.forName succeeds, the loaded class must be assignable to org.quartz.Job. This prevents scheduling arbitrary classes (e.g. a service bean or a class implementing a custom interface) that the Quartz scheduler cannot execute.","triggerScenarios":"A jobClassName that resolves to an org.jeecg.* class which does NOT implement org.quartz.Job — e.g. a plain @Service bean, an entity, or a class implementing only java.lang.Runnable.","commonSituations":"A developer wrote the job logic as a method on an existing service rather than a dedicated Job class; the class was refactored and the `implements Job` clause dropped; pointing the scheduler at a DTO/entity by mistake.","solutions":["Make the class implement org.quartz.Job (override execute(JobExecutionContext)) or extend a Job base such as QuartzJobBean.","Point sys_quartz_job.job_class_name at the class that already implements Job."],"exampleFix":"// before\npublic class ReportTask {\n    public void run() { ... }\n}\n// after\npublic class ReportTask implements org.quartz.Job {\n    @Override public void execute(JobExecutionContext ctx) { run(); }\n    public void run() { ... }\n}","handlingStrategy":"validation","validationCode":"// Confirm the class implements Job before scheduling.\nClass<?> c = Class.forName(jobClassName);\nif (!org.quartz.Job.class.isAssignableFrom(c)) {\n    throw new IllegalArgumentException(jobClassName + \" 必须实现 org.quartz.Job\");\n}","typeGuard":"public static boolean implementsJob(String name) {\n    try {\n        return org.quartz.Job.class.isAssignableFrom(\n            Class.forName(name, false, Thread.currentThread().getContextClassLoader()));\n    } catch (Throwable t) { return false; }\n}","tryCatchPattern":"try {\n    quartzJobService.schedulerAdd(job);\n} catch (JeecgBootException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"必须实现 org.quartz.Job\")) {\n        return Result.error(\"所选类未实现 Job 接口, 无法调度\");\n    }\n    throw e;\n}","preventionTips":["Create job classes from a Job base template so the interface is never forgotten.","Run an automated check that all scheduled job classes implement Job at build time."],"tags":["quartz","rce-prevention","security"],"backgroundTag":null,"analyzedSha":"96fb33f5ec68516da0b0147da06b2eb0419e063a","analyzedAt":"2026-08-14T00:04:16.786Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}