diff --git a/doc/XXL-JOB官方文档.md b/doc/XXL-JOB官方文档.md index 1a825994..796671a7 100644 --- a/doc/XXL-JOB官方文档.md +++ b/doc/XXL-JOB官方文档.md @@ -923,7 +923,7 @@ spring.mail.properties.mail.smtp.starttls.enable=true spring.mail.properties.mail.smtp.starttls.required=true spring.mail.properties.mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory -### 调度中心通讯TOKEN [选填]:非空时启用; +### 调度中心通讯TOKEN [必填]:安全性校验; xxl.job.accessToken= ### 调度中心通讯超时时间[选填],单位秒;默认3s; @@ -1016,7 +1016,7 @@ xuxueli/xxl-job-admin:{指定版本} ``` ### 调度中心部署根地址 [必填]:如调度中心集群部署存在多个地址则用逗号分隔。执行器将会使用该地址进行"执行器心跳注册"和"任务结果回调";为空则关闭自动注册; xxl.job.admin.addresses=http://127.0.0.1:8080 -### 调度中心通讯TOKEN [选填]:非空时启用; +### 调度中心通讯TOKEN [必填]:安全性校验; xxl.job.admin.accessToken=default_token ### 调度中心通讯超时时间[选填],单位秒;默认3s; xxl.job.admin.timeout=3 @@ -2904,6 +2904,7 @@ alter table xxl_job_log ### 7.47 版本 v3.5.0 Release Notes[ING] - 1、【新增】GLUE模式开关:新增GLUE模式开关(xxl.job.executor.glueenabled),支持执行器维度设置是否启用GLUE模式; - 2、【TODO】执行器AccessToken:执行器维度AccessToken,线上化动态管理;执行器间AccessToken隔离,提升安全性; + - 执行器 AccessToken: - 3、【TODO】调度中心OpenAPI增强:提供任务管理能力;封装Agent Skill并推送ClawHub; - 4、【TODO】配置线上化:发送邮箱配置线上管理、线程池配置调整; - 5、【TODO】任务告警:拆分“告警类型、告警配置”属性,支持Webhook、邮箱多种方式; diff --git a/pom.xml b/pom.xml index 73be234d..bec6e587 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ 2.0.18 - 6.1.0 + 6.0.3 3.0.0 diff --git a/xxl-job-admin/src/test/java/com/xxl/job/openapi/ExecutorBizTest.java b/xxl-job-admin/src/test/java/com/xxl/job/openapi/ExecutorBizTest.java index d2d03520..bf3926e5 100644 --- a/xxl-job-admin/src/test/java/com/xxl/job/openapi/ExecutorBizTest.java +++ b/xxl-job-admin/src/test/java/com/xxl/job/openapi/ExecutorBizTest.java @@ -22,12 +22,14 @@ public class ExecutorBizTest { private static String addressUrl = "http://127.0.0.1:9999/"; private static String accessToken = "default_token"; + private static String appname = "xxl-job-executor-sample"; private ExecutorBiz buildClient(){ return HttpTool.createClient() .url(addressUrl) .timeout(3 * 1000) .header(Const.XXL_JOB_ACCESS_TOKEN, accessToken) + .header(Const.XXL_JOB_APPNAME, appname) .proxy(ExecutorBiz.class); } @@ -36,12 +38,12 @@ public class ExecutorBizTest { ExecutorBiz executorBiz = buildClient(); // Act final Response retval = executorBiz.beat(); + logger.info("retval:{}", retval); // Assert result Assertions.assertNotNull(retval); Assertions.assertNull(((Response) retval).getData()); Assertions.assertEquals(200, retval.getCode()); - Assertions.assertNull(retval.getMsg()); } @Test diff --git a/xxl-job-core/src/main/java/com/xxl/job/core/constant/Const.java b/xxl-job-core/src/main/java/com/xxl/job/core/constant/Const.java index 45cf27cc..3e4c6fe6 100644 --- a/xxl-job-core/src/main/java/com/xxl/job/core/constant/Const.java +++ b/xxl-job-core/src/main/java/com/xxl/job/core/constant/Const.java @@ -12,6 +12,11 @@ public class Const { */ public static final String XXL_JOB_ACCESS_TOKEN = "XXL-JOB-ACCESS-TOKEN"; + /** + * app name + */ + public static final String XXL_JOB_APPNAME = "XXL-JOB-APPNAME"; + // ---------------------- for registry ---------------------- diff --git a/xxl-job-core/src/main/java/com/xxl/job/core/executor/XxlJobExecutor.java b/xxl-job-core/src/main/java/com/xxl/job/core/executor/XxlJobExecutor.java index bbe9f52f..9477c159 100644 --- a/xxl-job-core/src/main/java/com/xxl/job/core/executor/XxlJobExecutor.java +++ b/xxl-job-core/src/main/java/com/xxl/job/core/executor/XxlJobExecutor.java @@ -140,6 +140,9 @@ public class XxlJobExecutor { if (StringTool.isBlank(appname)) { throw new RuntimeException("xxl-job executor appname empty."); } + if (StringTool.isBlank(accessToken)) { + throw new RuntimeException("xxl-job executor accessToken empty."); + } // bind instance xxlJobExecutor = this; diff --git a/xxl-job-core/src/main/java/com/xxl/job/core/server/EmbedServer.java b/xxl-job-core/src/main/java/com/xxl/job/core/server/EmbedServer.java index 09db7199..6dcf47ec 100644 --- a/xxl-job-core/src/main/java/com/xxl/job/core/server/EmbedServer.java +++ b/xxl-job-core/src/main/java/com/xxl/job/core/server/EmbedServer.java @@ -3,11 +3,12 @@ package com.xxl.job.core.server; import com.xxl.job.core.constant.Const; import com.xxl.job.core.executor.XxlJobExecutor; import com.xxl.job.core.openapi.executor.ExecutorBiz; -import com.xxl.job.core.openapi.executor.impl.ExecutorBizImpl; import com.xxl.job.core.openapi.executor.dto.IdleBeatRequest; import com.xxl.job.core.openapi.executor.dto.KillRequest; import com.xxl.job.core.openapi.executor.dto.LogRequest; import com.xxl.job.core.openapi.executor.dto.TriggerRequest; +import com.xxl.job.core.openapi.executor.impl.ExecutorBizImpl; +import com.xxl.tool.core.StringTool; import com.xxl.tool.error.ThrowableTool; import com.xxl.tool.json.GsonTool; import com.xxl.tool.response.Response; @@ -83,7 +84,7 @@ public class EmbedServer { .addLast(new IdleStateHandler(0, 0, 30 * 3, TimeUnit.SECONDS)) // beat 3N, close if idle .addLast(new HttpServerCodec()) .addLast(new HttpObjectAggregator(5 * 1024 * 1024)) // merge request & reponse to FULL - .addLast(new EmbedHttpServerHandler(executorBiz, xxlJobExecutor.getAccessToken(), bizThreadPool)); + .addLast(new EmbedHttpServerHandler(executorBiz, xxlJobExecutor, bizThreadPool)); } }) .childOption(ChannelOption.SO_KEEPALIVE, true); @@ -143,32 +144,33 @@ public class EmbedServer { public static class EmbedHttpServerHandler extends SimpleChannelInboundHandler { private static final Logger logger = LoggerFactory.getLogger(EmbedHttpServerHandler.class); - private ExecutorBiz executorBiz; - private String accessToken; - private ThreadPoolExecutor bizThreadPool; + private final ExecutorBiz executorBiz; + private final XxlJobExecutor xxlJobExecutor; + private final ThreadPoolExecutor bizThreadPool; - public EmbedHttpServerHandler(ExecutorBiz executorBiz, String accessToken, ThreadPoolExecutor bizThreadPool) { + public EmbedHttpServerHandler(final ExecutorBiz executorBiz, final XxlJobExecutor xxlJobExecutor, final ThreadPoolExecutor bizThreadPool) { this.executorBiz = executorBiz; - this.accessToken = accessToken; + this.xxlJobExecutor = xxlJobExecutor; this.bizThreadPool = bizThreadPool; } @Override protected void channelRead0(final ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception { + // request parse - //final byte[] requestBytes = ByteBufUtil.getBytes(msg.content()); // byteBuf.toString(io.netty.util.CharsetUtil.UTF_8); - String requestData = msg.content().toString(CharsetUtil.UTF_8); - String uri = msg.uri(); HttpMethod httpMethod = msg.method(); + String uri = msg.uri(); + String requestData = msg.content().toString(CharsetUtil.UTF_8); boolean keepAlive = HttpUtil.isKeepAlive(msg); - String accessTokenReq = msg.headers().get(Const.XXL_JOB_ACCESS_TOKEN); + String accessToken = msg.headers().get(Const.XXL_JOB_ACCESS_TOKEN); + String appname = msg.headers().get(Const.XXL_JOB_APPNAME); // invoke bizThreadPool.execute(new Runnable() { @Override public void run() { // do invoke - Object responseObj = dispatchRequest(httpMethod, uri, requestData, accessTokenReq); + Object responseObj = dispatchRequest(httpMethod, uri, requestData, accessToken, appname); // to json String responseJson = GsonTool.toJson(responseObj); @@ -179,7 +181,10 @@ public class EmbedServer { }); } - private Object dispatchRequest(HttpMethod httpMethod, String uri, String requestData, String accessTokenReq) { + /** + * dispatch request + */ + private Object dispatchRequest(HttpMethod httpMethod, String uri, String requestData, String accessToken, String appname) { // valid if (HttpMethod.POST != httpMethod) { return Response.ofFail("invalid request, HttpMethod not support."); @@ -187,10 +192,14 @@ public class EmbedServer { if (uri == null || uri.trim().isEmpty()) { return Response.ofFail( "invalid request, uri-mapping empty."); } - if (accessToken != null - && !accessToken.trim().isEmpty() - && !accessToken.equals(accessTokenReq)) { - return Response.ofFail("The access token is wrong."); + + // valid access token + if (StringTool.isBlank(accessToken) || StringTool.isBlank(appname)) { + return Response.ofFail("invalid request, accessToken or appname is empty."); + } + if (!(accessToken.equals(xxlJobExecutor.getAccessToken()) + && appname.equals(xxlJobExecutor.getAppname()))) { + return Response.ofFail("invalid request, accessToken or appname invalid."); } // services mapping