diff --git a/doc/XXL-JOB官方文档.md b/doc/XXL-JOB官方文档.md index 1abb23d3..4199f61b 100644 --- a/doc/XXL-JOB官方文档.md +++ b/doc/XXL-JOB官方文档.md @@ -2545,20 +2545,7 @@ public void execute() { ### 7.40 版本 v3.2.0 Release Notes[规划中] - 1、【强化】AI任务(ollamaJobHandler)优化:针对 “model” 模型配置信息,从执行器侧文件类配置调整至调度中心“任务参数”动态配置,支持集成多模型、并结合任务动态配置切换。 -- 2、【修复】漏洞修复(CVE-2025-7787),针对 httpJobHandler 支持配置URL白名单限制,防止服务器端请求伪造(SSRF)攻击。 -- 3、【升级】升级多项maven依赖至较新版本,如 netty、groovy、mybatis、spring、spring-ai、dify 等; -- 4、【优化】登录信息页面空值处理优化,避免空值影响ftl渲染; -- 5、【优化】异常页面处理逻辑优化,新增兜底落地页配置; -- 6、【重构】ReturnT 重构,简化代码结构,提升API易用性以及可维护性; -- 7、【修复】合并PR-3738,修复拼写问题; -- 8、【修复】合并PR-3506,修复小概率情况下任务重复调度问题; -- 9、【修复】合并PR-3747,修复异常情况下资源泄漏风险; -- 10、【优化】调度中心系统日志调整,支持启动时指定 -DLOG_HOME 参数自定义日志位置;同时优化日志格式提升易读性; -- 11、【新增】GLUE模式(Python) 扩展,可选 "GLUE(Python3)" 或 "GLUE(Python2)" 两种模式,分别支持 python3/2 多版本; -- 12、【优化】任务Bean扫描规则调整,过滤冗余不必要扫描,避免系统组件提前初始化; -- 13、【重构】项目结构重构,提升可维护性与易读性; -- -- 14、【ING】登录认证重构,提升安全性。密码加密算法从Md5改为Sha256;登录态改为登录后动态随机生成;(需要针对用户表进行字段调整;同时需要重新初始化加密密码;相关SQL脚本如下;) +- 2、【安全】登录认证重构:密码加密算法从Md5改为Sha256;登录态改为登录后动态随机生成;提升系统安全性;(需要针对用户表进行字段调整,同时需要重新初始化密码信息;相关SQL脚本如下) ``` // 1、用户表password字段需要调整长度,执行如下命令 ALTER TABLE xxl_job_user @@ -2569,6 +2556,18 @@ ALTER TABLE xxl_job_user // 2、存量用户密码需要修改,可执行如下命令将密码初始化 “123456”;也可以自行通过 “SHA256Tool.sha256” 工具生成其他初始化密码; UPDATE xxl_job_user t SET t.password = '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92' WHERE t.username = {用户名}; ``` +- 3、【强化】GLUE模式(Python) 扩展,支持 "GLUE(Python3)" 与 "GLUE(Python2)" 两种模式,分别支持 python3/2 多版本; +- 4、【强化】调度中心系统日志调整,支持启动时指定 -DLOG_HOME 参数自定义日志位置;同时优化日志格式提升易读性; +- 5、【优化】任务Bean扫描规则调整,过滤冗余不必要扫描,避免系统组件提前初始化; +- 6、【优化】登录信息页面空值处理优化,避免空值影响ftl渲染; +- 7、【优化】异常页面处理逻辑优化,新增兜底落地页配置; +- 8、【重构】ReturnT 重构,简化代码结构,提升API易用性以及可维护性; +- 9、【重构】项目结构重构,提升可维护性与易读性; +- 10、【修复】漏洞修复(CVE-2025-7787),针对 httpJobHandler 支持配置URL白名单限制,防止服务器端请求伪造(SSRF)攻击。 +- 11、【修复】合并PR-3738,修复拼写问题; +- 12、【修复】合并PR-3506,修复小概率情况下任务重复调度问题; +- 13、【修复】合并PR-3747,修复异常情况下资源泄漏风险; +- 14、【升级】升级多项maven依赖至较新版本,如 netty、groovy、mybatis、spring、spring-ai、dify 等; ### 7.41 版本 v3.2.1 Release Notes[规划中] diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/annotation/PermissionLimit.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/annotation/PermissionLimit.java deleted file mode 100644 index cd181c2c..00000000 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/annotation/PermissionLimit.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.xxl.job.admin.annotation; - - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * 权限限制 - * @author xuxueli 2015-12-12 18:29:02 - */ -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.RUNTIME) -public @interface PermissionLimit { - - /** - * 登录拦截 (默认拦截) - */ - boolean limit() default true; - - /** - * 要求管理员权限 - * - * @return - */ - boolean adminuser() default false; - -} \ No newline at end of file diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/constant/Consts.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/constant/Consts.java new file mode 100644 index 00000000..640350ac --- /dev/null +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/constant/Consts.java @@ -0,0 +1,7 @@ +package com.xxl.job.admin.constant; + +public class Consts { + + public static final String ADMIN_ROLE = "ADMIN"; + +} diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/biz/JobApiController.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/biz/JobApiController.java index 0f0a2bf3..c4e1bd31 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/biz/JobApiController.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/biz/JobApiController.java @@ -1,13 +1,13 @@ package com.xxl.job.admin.controller.biz; -import com.xxl.job.admin.annotation.PermissionLimit; import com.xxl.job.admin.scheduler.conf.XxlJobAdminConfig; import com.xxl.job.core.biz.AdminBiz; import com.xxl.job.core.biz.model.HandleCallbackParam; import com.xxl.job.core.biz.model.RegistryParam; import com.xxl.job.core.biz.model.ReturnT; -import com.xxl.job.core.util.GsonTool; import com.xxl.job.core.util.XxlJobRemotingUtil; +import com.xxl.sso.core.annotation.XxlSso; +import com.xxl.tool.gson.GsonTool; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletRequest; import org.springframework.stereotype.Controller; @@ -37,7 +37,7 @@ public class JobApiController { */ @RequestMapping("/{uri}") @ResponseBody - @PermissionLimit(limit=false) + @XxlSso(login = false) public ReturnT api(HttpServletRequest request, @PathVariable("uri") String uri, @RequestBody(required = false) String data) { // valid diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/biz/JobCodeController.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/biz/JobCodeController.java index 715d85d8..a47d7362 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/biz/JobCodeController.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/biz/JobCodeController.java @@ -1,11 +1,10 @@ package com.xxl.job.admin.controller.biz; -import com.xxl.job.admin.web.xxlsso.PermissionInterceptor; +import com.xxl.job.admin.mapper.XxlJobInfoMapper; +import com.xxl.job.admin.mapper.XxlJobLogGlueMapper; import com.xxl.job.admin.model.XxlJobInfo; import com.xxl.job.admin.model.XxlJobLogGlue; import com.xxl.job.admin.util.I18nUtil; -import com.xxl.job.admin.mapper.XxlJobInfoMapper; -import com.xxl.job.admin.mapper.XxlJobLogGlueMapper; import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.glue.GlueTypeEnum; import jakarta.annotation.Resource; @@ -44,8 +43,8 @@ public class JobCodeController { throw new RuntimeException(I18nUtil.getString("jobinfo_glue_gluetype_unvalid")); } - // valid permission - PermissionInterceptor.validJobGroupPermission(request, jobInfo.getJobGroup()); + // valid jobGroup permission + JobInfoController.validJobGroupPermission(request, jobInfo.getJobGroup()); // Glue类型-字典 model.addAttribute("GlueTypeEnum", GlueTypeEnum.values()); @@ -74,9 +73,9 @@ public class JobCodeController { return new ReturnT(500, I18nUtil.getString("jobinfo_glue_jobid_unvalid")); } - // valid permission - PermissionInterceptor.validJobGroupPermission(request, existsJobInfo.getJobGroup()); - + // valid jobGroup permission + JobInfoController.validJobGroupPermission(request, existsJobInfo.getJobGroup()); + // update new code existsJobInfo.setGlueSource(glueSource); existsJobInfo.setGlueRemark(glueRemark); diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/biz/JobGroupController.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/biz/JobGroupController.java index c15b52cb..5468288c 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/biz/JobGroupController.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/biz/JobGroupController.java @@ -1,6 +1,6 @@ package com.xxl.job.admin.controller.biz; -import com.xxl.job.admin.annotation.PermissionLimit; +import com.xxl.job.admin.constant.Consts; import com.xxl.job.admin.model.XxlJobGroup; import com.xxl.job.admin.model.XxlJobRegistry; import com.xxl.job.admin.util.I18nUtil; @@ -9,6 +9,7 @@ import com.xxl.job.admin.mapper.XxlJobInfoMapper; import com.xxl.job.admin.mapper.XxlJobRegistryMapper; import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.enums.RegistryConfig; +import com.xxl.sso.core.annotation.XxlSso; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletRequest; import org.springframework.stereotype.Controller; @@ -35,14 +36,14 @@ public class JobGroupController { private XxlJobRegistryMapper xxlJobRegistryMapper; @RequestMapping - @PermissionLimit(adminuser = true) + @XxlSso(role = Consts.ADMIN_ROLE) public String index(Model model) { return "jobgroup/jobgroup.index"; } @RequestMapping("/pageList") @ResponseBody - @PermissionLimit(adminuser = true) + @XxlSso(role = Consts.ADMIN_ROLE) public Map pageList(HttpServletRequest request, @RequestParam(value = "start", required = false, defaultValue = "0") int start, @RequestParam(value = "length", required = false, defaultValue = "10") int length, @@ -63,7 +64,7 @@ public class JobGroupController { @RequestMapping("/save") @ResponseBody - @PermissionLimit(adminuser = true) + @XxlSso(role = Consts.ADMIN_ROLE) public ReturnT save(XxlJobGroup xxlJobGroup){ // valid @@ -107,7 +108,7 @@ public class JobGroupController { @RequestMapping("/update") @ResponseBody - @PermissionLimit(adminuser = true) + @XxlSso(role = Consts.ADMIN_ROLE) public ReturnT update(XxlJobGroup xxlJobGroup){ // valid if (xxlJobGroup.getAppname()==null || xxlJobGroup.getAppname().trim().length()==0) { @@ -176,7 +177,7 @@ public class JobGroupController { @RequestMapping("/remove") @ResponseBody - @PermissionLimit(adminuser = true) + @XxlSso(role = Consts.ADMIN_ROLE) public ReturnT remove(@RequestParam("id") int id){ // valid @@ -196,7 +197,7 @@ public class JobGroupController { @RequestMapping("/loadById") @ResponseBody - @PermissionLimit(adminuser = true) + @XxlSso(role = Consts.ADMIN_ROLE) public ReturnT loadById(@RequestParam("id") int id){ XxlJobGroup jobGroup = xxlJobGroupMapper.load(id); return jobGroup!=null?ReturnT.ofSuccess(jobGroup):ReturnT.ofFail(); diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/biz/JobInfoController.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/biz/JobInfoController.java index 7b94e436..7aab4472 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/biz/JobInfoController.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/biz/JobInfoController.java @@ -1,21 +1,24 @@ package com.xxl.job.admin.controller.biz; -import com.xxl.job.admin.web.xxlsso.PermissionInterceptor; -import com.xxl.job.admin.scheduler.exception.XxlJobException; +import com.xxl.job.admin.constant.Consts; +import com.xxl.job.admin.mapper.XxlJobGroupMapper; import com.xxl.job.admin.model.XxlJobGroup; import com.xxl.job.admin.model.XxlJobInfo; -import com.xxl.job.admin.model.XxlJobUser; +import com.xxl.job.admin.scheduler.exception.XxlJobException; import com.xxl.job.admin.scheduler.route.ExecutorRouteStrategyEnum; import com.xxl.job.admin.scheduler.scheduler.MisfireStrategyEnum; import com.xxl.job.admin.scheduler.scheduler.ScheduleTypeEnum; import com.xxl.job.admin.scheduler.thread.JobScheduleHelper; -import com.xxl.job.admin.util.I18nUtil; -import com.xxl.job.admin.mapper.XxlJobGroupMapper; import com.xxl.job.admin.service.XxlJobService; +import com.xxl.job.admin.util.I18nUtil; import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.enums.ExecutorBlockStrategyEnum; import com.xxl.job.core.glue.GlueTypeEnum; import com.xxl.job.core.util.DateUtil; +import com.xxl.sso.core.helper.XxlSsoHelper; +import com.xxl.sso.core.model.LoginInfo; +import com.xxl.tool.core.StringTool; +import com.xxl.tool.response.Response; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletRequest; import org.slf4j.Logger; @@ -26,7 +29,10 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; -import java.util.*; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; /** * index controller @@ -53,11 +59,11 @@ public class JobInfoController { model.addAttribute("MisfireStrategyEnum", MisfireStrategyEnum.values()); // 调度过期策略 // 执行器列表 - List jobGroupList_all = xxlJobGroupMapper.findAll(); + List jobGroupListTotal = xxlJobGroupMapper.findAll(); // filter group - List jobGroupList = PermissionInterceptor.filterJobGroupByRole(request, jobGroupList_all); - if (jobGroupList==null || jobGroupList.size()==0) { + List jobGroupList = filterJobGroupByPermission(request, jobGroupListTotal); + if (jobGroupList==null || jobGroupList.isEmpty()) { throw new XxlJobException(I18nUtil.getString("jobgroup_empty")); } @@ -84,22 +90,20 @@ public class JobInfoController { @ResponseBody public ReturnT add(HttpServletRequest request, XxlJobInfo jobInfo) { // valid permission - PermissionInterceptor.validJobGroupPermission(request, jobInfo.getJobGroup()); + LoginInfo loginInfo = validJobGroupPermission(request, jobInfo.getJobGroup()); // opt - XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request); - return xxlJobService.add(jobInfo, loginUser); + return xxlJobService.add(jobInfo, loginInfo); } - + @RequestMapping("/update") @ResponseBody public ReturnT update(HttpServletRequest request, XxlJobInfo jobInfo) { // valid permission - PermissionInterceptor.validJobGroupPermission(request, jobInfo.getJobGroup()); + LoginInfo loginInfo = validJobGroupPermission(request, jobInfo.getJobGroup()); // opt - XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request); - return xxlJobService.update(jobInfo, loginUser); + return xxlJobService.update(jobInfo, loginInfo); } @RequestMapping("/remove") @@ -128,9 +132,10 @@ public class JobInfoController { @RequestParam("addressList") String addressList) { // login user - XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request); + Response loginInfoResponse = XxlSsoHelper.loginCheckWithAttr(request); + // trigger - return xxlJobService.trigger(loginUser, id, executorParam, addressList); + return xxlJobService.trigger(loginInfoResponse.getData(), id, executorParam, addressList); } @RequestMapping("/nextTriggerTime") @@ -160,5 +165,51 @@ public class JobInfoController { return ReturnT.ofSuccess(result); } + + + // -------------------- tool -------------------- + + /** + * check if has jobgroup permission + */ + public static boolean hasJobGroupPermission(LoginInfo loginInfo, int jobGroup){ + if (XxlSsoHelper.hasRole(loginInfo, Consts.ADMIN_ROLE).isSuccess()) { + return true; + } else { + List jobGroups = (loginInfo.getExtraInfo()!=null && loginInfo.getExtraInfo().containsKey("jobGroups")) + ? List.of(StringTool.tokenizeToArray(loginInfo.getExtraInfo().get("jobGroups"), ",")) :new ArrayList<>(); + return jobGroups.contains(String.valueOf(jobGroup)); + } + } + + /** + * valid jobGroup permission + */ + public static LoginInfo validJobGroupPermission(HttpServletRequest request, int jobGroup) { + Response loginInfoResponse = XxlSsoHelper.loginCheckWithAttr(request); + if (!(loginInfoResponse.isSuccess() && hasJobGroupPermission(loginInfoResponse.getData(), jobGroup))) { + throw new RuntimeException(I18nUtil.getString("system_permission_limit") + "[username="+ loginInfoResponse.getData().getUserName() +"]"); + } + return loginInfoResponse.getData(); + } + + /** + * filter jobGroupList by permission + */ + public static List filterJobGroupByPermission(HttpServletRequest request, List jobGroupListTotal){ + Response loginInfoResponse = XxlSsoHelper.loginCheckWithAttr(request); + + if (XxlSsoHelper.hasRole(loginInfoResponse.getData(), Consts.ADMIN_ROLE).isSuccess()) { + return jobGroupListTotal; + } else { + List jobGroups = (loginInfoResponse.getData().getExtraInfo()!=null && loginInfoResponse.getData().getExtraInfo().containsKey("jobGroups")) + ? List.of(StringTool.tokenizeToArray(loginInfoResponse.getData().getExtraInfo().get("jobGroups"), ",")) :new ArrayList<>(); + + return jobGroupListTotal + .stream() + .filter(jobGroup -> jobGroups.contains(String.valueOf(jobGroup.getId()))) + .toList(); + } + } } diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/biz/JobLogController.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/biz/JobLogController.java index 1785c48d..f4e385a2 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/biz/JobLogController.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/biz/JobLogController.java @@ -1,16 +1,15 @@ package com.xxl.job.admin.controller.biz; -import com.xxl.job.admin.web.xxlsso.PermissionInterceptor; -import com.xxl.job.admin.scheduler.complete.XxlJobCompleter; -import com.xxl.job.admin.scheduler.exception.XxlJobException; +import com.xxl.job.admin.mapper.XxlJobGroupMapper; +import com.xxl.job.admin.mapper.XxlJobInfoMapper; +import com.xxl.job.admin.mapper.XxlJobLogMapper; import com.xxl.job.admin.model.XxlJobGroup; import com.xxl.job.admin.model.XxlJobInfo; import com.xxl.job.admin.model.XxlJobLog; +import com.xxl.job.admin.scheduler.complete.XxlJobCompleter; +import com.xxl.job.admin.scheduler.exception.XxlJobException; import com.xxl.job.admin.scheduler.scheduler.XxlJobScheduler; import com.xxl.job.admin.util.I18nUtil; -import com.xxl.job.admin.mapper.XxlJobGroupMapper; -import com.xxl.job.admin.mapper.XxlJobInfoMapper; -import com.xxl.job.admin.mapper.XxlJobLogMapper; import com.xxl.job.core.biz.ExecutorBiz; import com.xxl.job.core.biz.model.KillParam; import com.xxl.job.core.biz.model.LogParam; @@ -54,11 +53,11 @@ public class JobLogController { public String index(HttpServletRequest request, Model model, @RequestParam(value = "jobId", required = false, defaultValue = "0") Integer jobId) { // 执行器列表 - List jobGroupList_all = xxlJobGroupMapper.findAll(); + List jobGroupListTotal = xxlJobGroupMapper.findAll(); // filter group - List jobGroupList = PermissionInterceptor.filterJobGroupByRole(request, jobGroupList_all); - if (jobGroupList==null || jobGroupList.size()==0) { + List jobGroupList = JobInfoController.filterJobGroupByPermission(request, jobGroupListTotal); + if (jobGroupList==null || jobGroupList.isEmpty()) { throw new XxlJobException(I18nUtil.getString("jobgroup_empty")); } @@ -74,7 +73,7 @@ public class JobLogController { model.addAttribute("jobInfo", jobInfo); // valid permission - PermissionInterceptor.validJobGroupPermission(request, jobInfo.getJobGroup()); + JobInfoController.validJobGroupPermission(request, jobInfo.getJobGroup()); } return "joblog/joblog.index"; @@ -97,9 +96,9 @@ public class JobLogController { @RequestParam("logStatus") int logStatus, @RequestParam("filterTime") String filterTime) { - // valid permission - PermissionInterceptor.validJobGroupPermission(request, jobGroup); // 仅管理员支持查询全部;普通用户仅支持查询有权限的 jobGroup - + // valid jobGroup permission + JobInfoController.validJobGroupPermission(request, jobGroup); + // parse param Date triggerTimeStart = null; Date triggerTimeEnd = null; @@ -133,7 +132,7 @@ public class JobLogController { } // valid permission - PermissionInterceptor.validJobGroupPermission(request, jobLog.getJobGroup()); + JobInfoController.validJobGroupPermission(request, jobLog.getJobGroup()); // data model.addAttribute("triggerCode", jobLog.getTriggerCode()); @@ -249,7 +248,7 @@ public class JobLogController { @RequestParam("jobId") int jobId, @RequestParam("type") int type){ // valid permission - PermissionInterceptor.validJobGroupPermission(request, jobGroup); + JobInfoController.validJobGroupPermission(request, jobGroup); // opt Date clearBeforeTime = null; diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/biz/JobUserController.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/biz/JobUserController.java index 8b91f763..674e6871 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/biz/JobUserController.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/biz/JobUserController.java @@ -1,14 +1,17 @@ package com.xxl.job.admin.controller.biz; -import com.xxl.job.admin.annotation.PermissionLimit; +import com.xxl.job.admin.constant.Consts; import com.xxl.job.admin.mapper.XxlJobGroupMapper; import com.xxl.job.admin.mapper.XxlJobUserMapper; import com.xxl.job.admin.model.XxlJobGroup; import com.xxl.job.admin.model.XxlJobUser; import com.xxl.job.admin.util.I18nUtil; -import com.xxl.job.admin.web.xxlsso.PermissionInterceptor; import com.xxl.job.core.biz.model.ReturnT; +import com.xxl.sso.core.annotation.XxlSso; +import com.xxl.sso.core.helper.XxlSsoHelper; +import com.xxl.sso.core.model.LoginInfo; import com.xxl.tool.encrypt.SHA256Tool; +import com.xxl.tool.response.Response; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletRequest; import org.springframework.stereotype.Controller; @@ -35,7 +38,7 @@ public class JobUserController { private XxlJobGroupMapper xxlJobGroupMapper; @RequestMapping - @PermissionLimit(adminuser = true) + @XxlSso(role = Consts.ADMIN_ROLE) public String index(Model model) { // 执行器列表 @@ -47,7 +50,7 @@ public class JobUserController { @RequestMapping("/pageList") @ResponseBody - @PermissionLimit(adminuser = true) + @XxlSso(role = Consts.ADMIN_ROLE) public Map pageList(@RequestParam(value = "start", required = false, defaultValue = "0") int start, @RequestParam(value = "length", required = false, defaultValue = "10") int length, @RequestParam("username") String username, @@ -74,7 +77,7 @@ public class JobUserController { @RequestMapping("/add") @ResponseBody - @PermissionLimit(adminuser = true) + @XxlSso(role = Consts.ADMIN_ROLE) public ReturnT add(XxlJobUser xxlJobUser) { // valid username @@ -110,12 +113,12 @@ public class JobUserController { @RequestMapping("/update") @ResponseBody - @PermissionLimit(adminuser = true) + @XxlSso(role = Consts.ADMIN_ROLE) public ReturnT update(HttpServletRequest request, XxlJobUser xxlJobUser) { // avoid opt login seft - XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request); - if (loginUser.getUsername().equals(xxlJobUser.getUsername())) { + Response loginInfoResponse = XxlSsoHelper.loginCheckWithAttr(request); + if (loginInfoResponse.getData().getUserName().equals(xxlJobUser.getUsername())) { return ReturnT.ofFail(I18nUtil.getString("user_update_loginuser_limit")); } @@ -139,12 +142,12 @@ public class JobUserController { @RequestMapping("/remove") @ResponseBody - @PermissionLimit(adminuser = true) + @XxlSso(role = Consts.ADMIN_ROLE) public ReturnT remove(HttpServletRequest request, @RequestParam("id") int id) { // avoid opt login seft - XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request); - if (loginUser.getId() == id) { + Response loginInfoResponse = XxlSsoHelper.loginCheckWithAttr(request); + if (Integer.parseInt(loginInfoResponse.getData().getUserId()) == id) { return ReturnT.ofFail(I18nUtil.getString("user_update_loginuser_limit")); } @@ -175,8 +178,8 @@ public class JobUserController { String passwordHash = SHA256Tool.sha256(password); // valid old pwd - XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request); - XxlJobUser existUser = xxlJobUserMapper.loadByUserName(loginUser.getUsername()); + Response loginInfoResponse = XxlSsoHelper.loginCheckWithAttr(request); + XxlJobUser existUser = xxlJobUserMapper.loadByUserName(loginInfoResponse.getData().getUserName()); if (!oldPasswordHash.equals(existUser.getPassword())) { return ReturnT.ofFail(I18nUtil.getString("change_pwd_field_oldpwd") + I18nUtil.getString("system_unvalid")); } diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/login/LoginController.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/login/LoginController.java index e4304440..bcfe8ee7 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/login/LoginController.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/controller/login/LoginController.java @@ -1,8 +1,16 @@ package com.xxl.job.admin.controller.login; -import com.xxl.job.admin.annotation.PermissionLimit; -import com.xxl.job.admin.service.impl.LoginService; +import com.xxl.job.admin.mapper.XxlJobUserMapper; +import com.xxl.job.admin.model.XxlJobUser; +import com.xxl.job.admin.util.I18nUtil; import com.xxl.job.core.biz.model.ReturnT; +import com.xxl.sso.core.annotation.XxlSso; +import com.xxl.sso.core.helper.XxlSsoHelper; +import com.xxl.sso.core.model.LoginInfo; +import com.xxl.tool.core.StringTool; +import com.xxl.tool.encrypt.SHA256Tool; +import com.xxl.tool.id.UUIDTool; +import com.xxl.tool.response.Response; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @@ -22,39 +30,65 @@ import org.springframework.web.servlet.view.RedirectView; @RequestMapping("/auth") public class LoginController { - @Resource - private LoginService loginService; + private XxlJobUserMapper xxlJobUserMapper; + @RequestMapping("/login") + @XxlSso(login = false) + public ModelAndView login(HttpServletRequest request, HttpServletResponse response, ModelAndView modelAndView) { - @RequestMapping("/toLogin") - @PermissionLimit(limit=false) - public ModelAndView toLogin(HttpServletRequest request, HttpServletResponse response, ModelAndView modelAndView) { - if (loginService.ifLogin(request, response) != null) { + // xxl-sso, logincheck + Response loginInfoResponse = XxlSsoHelper.loginCheckWithCookie(request, response); + if (loginInfoResponse.isSuccess()) { modelAndView.setView(new RedirectView("/",true,false)); return modelAndView; } + return new ModelAndView("login"); } - @RequestMapping(value="/login", method=RequestMethod.POST) + @RequestMapping(value="/doLogin", method=RequestMethod.POST) @ResponseBody - @PermissionLimit(limit=false) - public ReturnT loginDo(HttpServletRequest request, - HttpServletResponse response, - @RequestParam("userName") String userName, - @RequestParam("password") String password, - @RequestParam(value = "ifRemember", required = false) String ifRemember){ - - boolean ifRem = (ifRemember!=null && ifRemember.trim().length()>0 && "on".equals(ifRemember))?true:false; - return loginService.login(request, response, userName, password, ifRem); + @XxlSso(login = false) + public ReturnT doLogin(HttpServletRequest request, + HttpServletResponse response, + @RequestParam("userName") String userName, + @RequestParam("password") String password, + @RequestParam(value = "ifRemember", required = false) String ifRemember){ + + // param + boolean ifRem = StringTool.isNotBlank(ifRemember) && "on".equals(ifRemember); + if (StringTool.isBlank(userName) || StringTool.isBlank(password)){ + return ReturnT.ofFail( I18nUtil.getString("login_param_empty") ); + } + + // valid user、status + XxlJobUser xxlJobUser = xxlJobUserMapper.loadByUserName(userName); + if (xxlJobUser == null) { + return ReturnT.ofFail( I18nUtil.getString("login_param_unvalid") ); + } + + // valid passowrd + String passwordHash = SHA256Tool.sha256(password); + if (!passwordHash.equals(xxlJobUser.getPassword())) { + return ReturnT.ofFail( I18nUtil.getString("login_param_unvalid") ); + } + + // xxl-sso, do login + LoginInfo loginInfo = new LoginInfo(String.valueOf(xxlJobUser.getId()), UUIDTool.getSimpleUUID()); + Response result= XxlSsoHelper.loginWithCookie(loginInfo, response, ifRem); + + return ReturnT.of(result.getCode(), result.getMsg()); } @RequestMapping(value="/logout", method=RequestMethod.POST) @ResponseBody - @PermissionLimit(limit=false) + @XxlSso(login = false) public ReturnT logout(HttpServletRequest request, HttpServletResponse response){ - return loginService.logout(request, response); + // xxl-sso, do logout + Response result = XxlSsoHelper.logoutWithCookie(request, response); + + return ReturnT.of(result.getCode(), result.getMsg()); } } diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/mapper/XxlJobUserMapper.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/mapper/XxlJobUserMapper.java index b6ab0148..c70e02be 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/mapper/XxlJobUserMapper.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/mapper/XxlJobUserMapper.java @@ -1,6 +1,7 @@ package com.xxl.job.admin.mapper; import com.xxl.job.admin.model.XxlJobUser; +import com.xxl.tool.response.Response; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import java.util.List; @@ -22,10 +23,14 @@ public interface XxlJobUserMapper { public XxlJobUser loadByUserName(@Param("username") String username); + public XxlJobUser loadById(@Param("id") int id); + public int save(XxlJobUser xxlJobUser); public int update(XxlJobUser xxlJobUser); public int delete(@Param("id") int id); + public int updateToken(@Param("id") int id, @Param("token") String token); + } diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/model/XxlJobUser.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/model/XxlJobUser.java index 459da82c..7e1419ff 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/model/XxlJobUser.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/model/XxlJobUser.java @@ -10,8 +10,9 @@ public class XxlJobUser { private int id; private String username; // 账号 private String password; // 密码 + private String token; // 登录token private int role; // 角色:0-普通用户、1-管理员 - private String permission; // 权限:执行器ID列表,多个逗号分割 + private String permission; // 权限:执行器ID列表,多个逗号分割 public int getId() { return id; @@ -37,6 +38,14 @@ public class XxlJobUser { this.password = password; } + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + public int getRole() { return role; } diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/service/XxlJobService.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/service/XxlJobService.java index f95fe7d7..167df74e 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/service/XxlJobService.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/service/XxlJobService.java @@ -4,6 +4,7 @@ package com.xxl.job.admin.service; import com.xxl.job.admin.model.XxlJobInfo; import com.xxl.job.admin.model.XxlJobUser; import com.xxl.job.core.biz.model.ReturnT; +import com.xxl.sso.core.model.LoginInfo; import java.util.Date; import java.util.Map; @@ -34,7 +35,7 @@ public interface XxlJobService { * @param jobInfo * @return */ - public ReturnT add(XxlJobInfo jobInfo, XxlJobUser loginUser); + public ReturnT add(XxlJobInfo jobInfo, LoginInfo loginInfo); /** * update job @@ -42,7 +43,7 @@ public interface XxlJobService { * @param jobInfo * @return */ - public ReturnT update(XxlJobInfo jobInfo, XxlJobUser loginUser); + public ReturnT update(XxlJobInfo jobInfo, LoginInfo loginInfo); /** * remove job @@ -71,13 +72,13 @@ public interface XxlJobService { /** * trigger * - * @param loginUser + * @param loginInfo * @param jobId * @param executorParam * @param addressList * @return */ - public ReturnT trigger(XxlJobUser loginUser, int jobId, String executorParam, String addressList); + public ReturnT trigger(LoginInfo loginInfo, int jobId, String executorParam, String addressList); /** * dashboard info diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/LoginService.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/LoginService.java deleted file mode 100644 index 4777ce4f..00000000 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/LoginService.java +++ /dev/null @@ -1,111 +0,0 @@ -package com.xxl.job.admin.service.impl; - -import com.xxl.job.admin.mapper.XxlJobUserMapper; -import com.xxl.job.admin.model.XxlJobUser; -import com.xxl.job.admin.util.CookieUtil; -import com.xxl.job.admin.util.I18nUtil; -import com.xxl.job.core.biz.model.ReturnT; -import com.xxl.job.core.util.GsonTool; -import com.xxl.tool.encrypt.SHA256Tool; -import jakarta.annotation.Resource; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.stereotype.Service; - -import java.math.BigInteger; - -/** - * @author xuxueli 2019-05-04 22:13:264 - */ -@Service -public class LoginService { - - public static final String LOGIN_IDENTITY_KEY = "XXL_JOB_LOGIN_IDENTITY"; - - @Resource - private XxlJobUserMapper xxlJobUserMapper; - - - // ---------------------- token tool ---------------------- - - private String makeToken(XxlJobUser xxlJobUser){ - String tokenJson = GsonTool.toJson(xxlJobUser); - String tokenHex = new BigInteger(tokenJson.getBytes()).toString(16); - return tokenHex; - } - private XxlJobUser parseToken(String tokenHex){ - XxlJobUser xxlJobUser = null; - if (tokenHex != null) { - String tokenJson = new String(new BigInteger(tokenHex, 16).toByteArray()); // username_password(md5) - xxlJobUser = GsonTool.fromJson(tokenJson, XxlJobUser.class); - } - return xxlJobUser; - } - - - // ---------------------- login tool, with cookie and db ---------------------- - - public ReturnT login(HttpServletRequest request, HttpServletResponse response, String username, String password, boolean ifRemember){ - - // param - if (username==null || username.trim().length()==0 || password==null || password.trim().length()==0){ - return new ReturnT(500, I18nUtil.getString("login_param_empty")); - } - - // valid passowrd - XxlJobUser xxlJobUser = xxlJobUserMapper.loadByUserName(username); - if (xxlJobUser == null) { - return new ReturnT(500, I18nUtil.getString("login_param_unvalid")); - } - String passwordHash = SHA256Tool.sha256(password); - if (!passwordHash.equals(xxlJobUser.getPassword())) { - return new ReturnT(500, I18nUtil.getString("login_param_unvalid")); - } - - String loginToken = makeToken(xxlJobUser); - - // do login - CookieUtil.set(response, LOGIN_IDENTITY_KEY, loginToken, ifRemember); - return ReturnT.ofSuccess(); - } - - /** - * logout - * - * @param request - * @param response - */ - public ReturnT logout(HttpServletRequest request, HttpServletResponse response){ - CookieUtil.remove(request, response, LOGIN_IDENTITY_KEY); - return ReturnT.ofSuccess(); - } - - /** - * logout - * - * @param request - * @return - */ - public XxlJobUser ifLogin(HttpServletRequest request, HttpServletResponse response){ - String cookieToken = CookieUtil.getValue(request, LOGIN_IDENTITY_KEY); - if (cookieToken != null) { - XxlJobUser cookieUser = null; - try { - cookieUser = parseToken(cookieToken); - } catch (Exception e) { - logout(request, response); - } - if (cookieUser != null) { - XxlJobUser dbUser = xxlJobUserMapper.loadByUserName(cookieUser.getUsername()); - if (dbUser != null) { - if (cookieUser.getPassword().equals(dbUser.getPassword())) { - return dbUser; - } - } - } - } - return null; - } - - -} diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/XxlJobServiceImpl.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/XxlJobServiceImpl.java index 437a1534..12467db6 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/XxlJobServiceImpl.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/XxlJobServiceImpl.java @@ -1,23 +1,25 @@ package com.xxl.job.admin.service.impl; -import com.xxl.job.admin.scheduler.cron.CronExpression; +import com.xxl.job.admin.controller.biz.JobInfoController; +import com.xxl.job.admin.mapper.*; import com.xxl.job.admin.model.XxlJobGroup; import com.xxl.job.admin.model.XxlJobInfo; import com.xxl.job.admin.model.XxlJobLogReport; import com.xxl.job.admin.model.XxlJobUser; +import com.xxl.job.admin.scheduler.cron.CronExpression; import com.xxl.job.admin.scheduler.route.ExecutorRouteStrategyEnum; import com.xxl.job.admin.scheduler.scheduler.MisfireStrategyEnum; import com.xxl.job.admin.scheduler.scheduler.ScheduleTypeEnum; import com.xxl.job.admin.scheduler.thread.JobScheduleHelper; import com.xxl.job.admin.scheduler.thread.JobTriggerPoolHelper; import com.xxl.job.admin.scheduler.trigger.TriggerTypeEnum; -import com.xxl.job.admin.util.I18nUtil; -import com.xxl.job.admin.mapper.*; import com.xxl.job.admin.service.XxlJobService; +import com.xxl.job.admin.util.I18nUtil; import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.enums.ExecutorBlockStrategyEnum; import com.xxl.job.core.glue.GlueTypeEnum; import com.xxl.job.core.util.DateUtil; +import com.xxl.sso.core.model.LoginInfo; import jakarta.annotation.Resource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -61,7 +63,7 @@ public class XxlJobServiceImpl implements XxlJobService { } @Override - public ReturnT add(XxlJobInfo jobInfo, XxlJobUser loginUser) { + public ReturnT add(XxlJobInfo jobInfo, LoginInfo loginInfo) { // valid base XxlJobGroup group = xxlJobGroupMapper.load(jobInfo.getJobGroup()); @@ -131,7 +133,8 @@ public class XxlJobServiceImpl implements XxlJobService { return new ReturnT(ReturnT.FAIL_CODE, MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_not_found")), childJobIdItem)); } - if (!loginUser.validPermission(childJobInfo.getJobGroup())) { + // valid jobGroup permission + if (!JobInfoController.hasJobGroupPermission(loginInfo, childJobInfo.getJobGroup())) { return new ReturnT(ReturnT.FAIL_CODE, MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_permission_limit")), childJobIdItem)); } @@ -175,7 +178,7 @@ public class XxlJobServiceImpl implements XxlJobService { } @Override - public ReturnT update(XxlJobInfo jobInfo, XxlJobUser loginUser) { + public ReturnT update(XxlJobInfo jobInfo, LoginInfo loginInfo) { // valid base if (jobInfo.getJobDesc()==null || jobInfo.getJobDesc().trim().length()==0) { @@ -236,7 +239,8 @@ public class XxlJobServiceImpl implements XxlJobService { return new ReturnT(ReturnT.FAIL_CODE, MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_not_found")), childJobIdItem)); } - if (!loginUser.validPermission(childJobInfo.getJobGroup())) { + // valid jobGroup permission + if (!JobInfoController.hasJobGroupPermission(loginInfo, childJobInfo.getJobGroup())) { return new ReturnT(ReturnT.FAIL_CODE, MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_permission_limit")), childJobIdItem)); } @@ -378,16 +382,17 @@ public class XxlJobServiceImpl implements XxlJobService { @Override - public ReturnT trigger(XxlJobUser loginUser, int jobId, String executorParam, String addressList) { + public ReturnT trigger(LoginInfo loginInfo, int jobId, String executorParam, String addressList) { // permission - if (loginUser == null) { + if (loginInfo == null) { return ReturnT.ofFail(I18nUtil.getString("system_permission_limit")); } XxlJobInfo xxlJobInfo = xxlJobInfoMapper.loadById(jobId); if (xxlJobInfo == null) { return ReturnT.ofFail(I18nUtil.getString("jobinfo_glue_jobid_unvalid")); } - if (!hasPermission(loginUser, xxlJobInfo.getJobGroup())) { + + if (!JobInfoController.hasJobGroupPermission(loginInfo, xxlJobInfo.getJobGroup())) { return ReturnT.ofFail(I18nUtil.getString("system_permission_limit")); } @@ -400,17 +405,6 @@ public class XxlJobServiceImpl implements XxlJobService { return ReturnT.ofSuccess(); } - private boolean hasPermission(XxlJobUser loginUser, int jobGroup){ - if (loginUser.getRole() == 1) { - return true; - } - List groupIdStrs = new ArrayList<>(); - if (loginUser.getPermission()!=null && loginUser.getPermission().trim().length()>0) { - groupIdStrs = Arrays.asList(loginUser.getPermission().trim().split(",")); - } - return groupIdStrs.contains(String.valueOf(jobGroup)); - } - @Override public Map dashboardInfo() { diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/util/I18nUtil.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/util/I18nUtil.java index 9385c595..0dd8edef 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/util/I18nUtil.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/util/I18nUtil.java @@ -1,7 +1,7 @@ package com.xxl.job.admin.util; import com.xxl.job.admin.scheduler.conf.XxlJobAdminConfig; -import com.xxl.job.core.util.GsonTool; +import com.xxl.tool.gson.GsonTool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.io.ClassPathResource; @@ -76,4 +76,5 @@ public class I18nUtil { return GsonTool.toJson(map); } + } diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/util/old/JacksonUtil.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/util/old/JacksonUtil.java index 51a26986..61ef49ea 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/util/old/JacksonUtil.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/util/old/JacksonUtil.java @@ -1,4 +1,4 @@ -//package com.xxl.job.admin.util; +//package com.xxl.job.admin.util.old; // //import com.fasterxml.jackson.core.JsonGenerationException; //import com.fasterxml.jackson.core.JsonParseException; diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/web/interceptor/CommonDataInterceptor.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/web/interceptor/CommonDataInterceptor.java index a99b822d..1ecc92de 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/web/interceptor/CommonDataInterceptor.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/web/interceptor/CommonDataInterceptor.java @@ -5,6 +5,7 @@ import com.xxl.tool.freemarker.FtlTool; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Component; import org.springframework.web.servlet.AsyncHandlerInterceptor; import org.springframework.web.servlet.ModelAndView; @@ -18,7 +19,7 @@ import java.util.HashMap; * * @author xuxueli 2015-12-12 18:09:04 */ -@Component +@Configuration public class CommonDataInterceptor implements WebMvcConfigurer { @Override diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/web/xxlsso/PermissionInterceptor.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/web/xxlsso/PermissionInterceptor.java deleted file mode 100644 index fa346c41..00000000 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/web/xxlsso/PermissionInterceptor.java +++ /dev/null @@ -1,131 +0,0 @@ -package com.xxl.job.admin.web.xxlsso; - -import com.xxl.job.admin.annotation.PermissionLimit; -import com.xxl.job.admin.model.XxlJobGroup; -import com.xxl.job.admin.model.XxlJobUser; -import com.xxl.job.admin.util.I18nUtil; -import com.xxl.job.admin.service.impl.LoginService; -import jakarta.annotation.Resource; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.stereotype.Component; -import org.springframework.web.method.HandlerMethod; -import org.springframework.web.servlet.AsyncHandlerInterceptor; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * 权限拦截 - * - * @author xuxueli 2015-12-12 18:09:04 - */ -@Component -public class PermissionInterceptor implements AsyncHandlerInterceptor { - - @Resource - private LoginService loginService; - - @Override - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { - - if (!(handler instanceof HandlerMethod)) { - return true; // proceed with the next interceptor - } - - // if need login - boolean needLogin = true; - boolean needAdminuser = false; - HandlerMethod method = (HandlerMethod)handler; - PermissionLimit permission = method.getMethodAnnotation(PermissionLimit.class); - if (permission!=null) { - needLogin = permission.limit(); - needAdminuser = permission.adminuser(); - } - - if (needLogin) { - XxlJobUser loginUser = loginService.ifLogin(request, response); - if (loginUser == null) { - response.setStatus(302); - response.setHeader("location", request.getContextPath()+"/auth/toLogin"); - return false; - } - if (needAdminuser && loginUser.getRole()!=1) { - throw new RuntimeException(I18nUtil.getString("system_permission_limit")); - } - - // set loginUser, with request - setLoginUser(request, loginUser); - } - - return true; // proceed with the next interceptor - } - - - // -------------------- permission tool -------------------- - - /** - * set loginUser - * - * @param request - * @param loginUser - */ - private static void setLoginUser(HttpServletRequest request, XxlJobUser loginUser){ - request.setAttribute("loginUser", loginUser); - } - - /** - * get loginUser - * - * @param request - * @return - */ - public static XxlJobUser getLoginUser(HttpServletRequest request){ - XxlJobUser loginUser = (XxlJobUser) request.getAttribute("loginUser"); // get loginUser, with request - return loginUser; - } - - /** - * valid permission by JobGroup - * - * @param request - * @param jobGroup - */ - public static void validJobGroupPermission(HttpServletRequest request, int jobGroup) { - XxlJobUser loginUser = getLoginUser(request); - if (!loginUser.validPermission(jobGroup)) { - throw new RuntimeException(I18nUtil.getString("system_permission_limit") + "[username="+ loginUser.getUsername() +"]"); - } - } - - /** - * filter XxlJobGroup by role - * - * @param request - * @param jobGroupList_all - * @return - */ - public static List filterJobGroupByRole(HttpServletRequest request, List jobGroupList_all){ - List jobGroupList = new ArrayList<>(); - if (jobGroupList_all!=null && jobGroupList_all.size()>0) { - XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request); - if (loginUser.getRole() == 1) { - jobGroupList = jobGroupList_all; - } else { - List groupIdStrs = new ArrayList<>(); - if (loginUser.getPermission()!=null && loginUser.getPermission().trim().length()>0) { - groupIdStrs = Arrays.asList(loginUser.getPermission().trim().split(",")); - } - for (XxlJobGroup groupItem:jobGroupList_all) { - if (groupIdStrs.contains(String.valueOf(groupItem.getId()))) { - jobGroupList.add(groupItem); - } - } - } - } - return jobGroupList; - } - - -} diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/web/xxlsso/SimpleLoginStore.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/web/xxlsso/SimpleLoginStore.java new file mode 100644 index 00000000..aeb8b14a --- /dev/null +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/web/xxlsso/SimpleLoginStore.java @@ -0,0 +1,84 @@ +package com.xxl.job.admin.web.xxlsso; + +import com.xxl.job.admin.constant.Consts; +import com.xxl.job.admin.mapper.XxlJobUserMapper; +import com.xxl.job.admin.model.XxlJobUser; +import com.xxl.sso.core.model.LoginInfo; +import com.xxl.sso.core.store.LoginStore; +import com.xxl.tool.core.MapTool; +import com.xxl.tool.response.Response; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; + +/** + * Simple LoginStore + * + * 1、store by database; + * 2、If you have higher performance requirements, it is recommended to use RedisLoginStore; + * + * @author xuxueli 2025-08-03 + */ +@Component +public class SimpleLoginStore implements LoginStore { + + + @Resource + private XxlJobUserMapper xxlJobUserMapper; + + + @Override + public Response set(LoginInfo loginInfo) { + + // parse token-signature + String token_sign = loginInfo.getSignature(); + + // write token by UserId + int ret = xxlJobUserMapper.updateToken(Integer.parseInt(loginInfo.getUserId()), token_sign); + return ret > 0 ? Response.ofSuccess() : Response.ofFail("token set fail"); + } + + @Override + public Response update(LoginInfo loginInfo) { + return Response.ofFail("not support"); + } + + @Override + public Response remove(String userId) { + // delete token-signature + int ret = xxlJobUserMapper.updateToken(Integer.parseInt(userId), ""); + return ret > 0 ? Response.ofSuccess() : Response.ofFail("token remove fail"); + } + + /** + * check through DB query + */ + @Override + public Response get(String userId) { + + // load login-user + XxlJobUser user = xxlJobUserMapper.loadById(Integer.parseInt(userId)); + if (user == null) { + return Response.ofFail("userId invalid."); + } + + // parse role + List roleList = user.getRole()==1? List.of(Consts.ADMIN_ROLE):null; + + // parse jobGroup permission + Map extraInfo = MapTool.newHashMap( + "jobGroups", user.getPermission() + ); + + // build LoginInfo + LoginInfo loginInfo = new LoginInfo(userId, user.getToken()); + loginInfo.setUserName(user.getUsername()); + loginInfo.setRoleList(roleList); + loginInfo.setExtraInfo(extraInfo); + + return Response.ofSuccess(loginInfo); + } + +} diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/web/xxlsso/WebMvcConfig.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/web/xxlsso/WebMvcConfig.java deleted file mode 100644 index 1179501a..00000000 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/web/xxlsso/WebMvcConfig.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.xxl.job.admin.web.xxlsso; - -import com.xxl.job.admin.web.interceptor.CommonDataInterceptor; -import jakarta.annotation.Resource; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.config.annotation.InterceptorRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -/** - * web mvc config - * - * @author xuxueli 2018-04-02 20:48:20 - */ -@Configuration -public class WebMvcConfig implements WebMvcConfigurer { - - @Resource - private PermissionInterceptor permissionInterceptor; - - @Override - public void addInterceptors(InterceptorRegistry registry) { - registry.addInterceptor(permissionInterceptor).addPathPatterns("/**"); - } - -} \ No newline at end of file diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/web/xxlsso/XxlSsoConfig.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/web/xxlsso/XxlSsoConfig.java new file mode 100644 index 00000000..6b10f873 --- /dev/null +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/web/xxlsso/XxlSsoConfig.java @@ -0,0 +1,62 @@ +package com.xxl.job.admin.web.xxlsso; + +import com.xxl.sso.core.auth.interceptor.XxlSsoWebInterceptor; +import com.xxl.sso.core.bootstrap.XxlSsoBootstrap; +import jakarta.annotation.Resource; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * @author xuxueli 2018-11-15 + */ +@Configuration +public class XxlSsoConfig implements WebMvcConfigurer { + + + @Value("${xxl-sso.token.key}") + private String tokenKey; + + @Value("${xxl-sso.token.timeout}") + private long tokenTimeout; + + @Value("${xxl-sso.client.excluded.paths}") + private String excludedPaths; + + @Value("${xxl-sso.client.login.path}") + private String loginPath; + + + @Resource + private SimpleLoginStore loginStore; + + + /** + * 1、配置 XxlSsoBootstrap + */ + @Bean(initMethod = "start", destroyMethod = "stop") + public XxlSsoBootstrap xxlSsoBootstrap() { + + XxlSsoBootstrap bootstrap = new XxlSsoBootstrap(); + bootstrap.setLoginStore(loginStore); + bootstrap.setTokenKey(tokenKey); + bootstrap.setTokenTimeout(tokenTimeout); + return bootstrap; + } + + /** + * 2、配置 XxlSso 拦截器 + */ + @Override + public void addInterceptors(InterceptorRegistry registry) { + + // 2.1、build xxl-sso interceptor + XxlSsoWebInterceptor webInterceptor = new XxlSsoWebInterceptor(excludedPaths, loginPath); + + // 2.2、add interceptor + registry.addInterceptor(webInterceptor).addPathPatterns("/**"); + } + +} diff --git a/xxl-job-admin/src/main/resources/application.properties b/xxl-job-admin/src/main/resources/application.properties index 39380c83..a5d21fcc 100644 --- a/xxl-job-admin/src/main/resources/application.properties +++ b/xxl-job-admin/src/main/resources/application.properties @@ -20,7 +20,7 @@ spring.freemarker.settings.number_format=0.########## spring.freemarker.settings.new_builtin_class_resolver=safer ### mybatis -mybatis.mapper-locations=classpath:/mybatis-mapper/*Mapper.xml +mybatis.mapper-locations=classpath:/mapper/*Mapper.xml ### datasource-pool spring.datasource.type=com.zaxxer.hikari.HikariDataSource @@ -66,3 +66,9 @@ xxl.job.triggerpool.slow.max=100 ### xxl-job, log retention days xxl.job.logretentiondays=30 + +### xxl-sso +xxl-sso.token.key=xxl_job_login_token +xxl-sso.token.timeout=604800000 +xxl-sso.client.excluded.paths= +xxl-sso.client.login.path=/auth/login diff --git a/xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobGroupMapper.xml b/xxl-job-admin/src/main/resources/mapper/XxlJobGroupMapper.xml similarity index 100% rename from xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobGroupMapper.xml rename to xxl-job-admin/src/main/resources/mapper/XxlJobGroupMapper.xml diff --git a/xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobInfoMapper.xml b/xxl-job-admin/src/main/resources/mapper/XxlJobInfoMapper.xml similarity index 100% rename from xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobInfoMapper.xml rename to xxl-job-admin/src/main/resources/mapper/XxlJobInfoMapper.xml diff --git a/xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobLogGlueMapper.xml b/xxl-job-admin/src/main/resources/mapper/XxlJobLogGlueMapper.xml similarity index 96% rename from xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobLogGlueMapper.xml rename to xxl-job-admin/src/main/resources/mapper/XxlJobLogGlueMapper.xml index d7a68c7e..e75fc55c 100644 --- a/xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobLogGlueMapper.xml +++ b/xxl-job-admin/src/main/resources/mapper/XxlJobLogGlueMapper.xml @@ -1,71 +1,71 @@ - - - - - - - - - - - - - - - - t.id, - t.job_id, - t.glue_type, - t.glue_source, - t.glue_remark, - t.add_time, - t.update_time - - - - INSERT INTO xxl_job_logglue ( - `job_id`, - `glue_type`, - `glue_source`, - `glue_remark`, - `add_time`, - `update_time` - ) VALUES ( - #{jobId}, - #{glueType}, - #{glueSource}, - #{glueRemark}, - #{addTime}, - #{updateTime} - ); - - - - - - - DELETE FROM xxl_job_logglue - WHERE id NOT in( - SELECT id FROM( - SELECT id FROM xxl_job_logglue - WHERE `job_id` = #{jobId} - ORDER BY update_time desc - LIMIT 0, #{limit} - ) t1 - ) AND `job_id` = #{jobId} - - - - DELETE FROM xxl_job_logglue - WHERE `job_id` = #{jobId} - - + + + + + + + + + + + + + + + + t.id, + t.job_id, + t.glue_type, + t.glue_source, + t.glue_remark, + t.add_time, + t.update_time + + + + INSERT INTO xxl_job_logglue ( + `job_id`, + `glue_type`, + `glue_source`, + `glue_remark`, + `add_time`, + `update_time` + ) VALUES ( + #{jobId}, + #{glueType}, + #{glueSource}, + #{glueRemark}, + #{addTime}, + #{updateTime} + ); + + + + + + + DELETE FROM xxl_job_logglue + WHERE id NOT in( + SELECT id FROM( + SELECT id FROM xxl_job_logglue + WHERE `job_id` = #{jobId} + ORDER BY update_time desc + LIMIT 0, #{limit} + ) t1 + ) AND `job_id` = #{jobId} + + + + DELETE FROM xxl_job_logglue + WHERE `job_id` = #{jobId} + + \ No newline at end of file diff --git a/xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobLogMapper.xml b/xxl-job-admin/src/main/resources/mapper/XxlJobLogMapper.xml similarity index 100% rename from xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobLogMapper.xml rename to xxl-job-admin/src/main/resources/mapper/XxlJobLogMapper.xml diff --git a/xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobLogReportMapper.xml b/xxl-job-admin/src/main/resources/mapper/XxlJobLogReportMapper.xml similarity index 100% rename from xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobLogReportMapper.xml rename to xxl-job-admin/src/main/resources/mapper/XxlJobLogReportMapper.xml diff --git a/xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobRegistryMapper.xml b/xxl-job-admin/src/main/resources/mapper/XxlJobRegistryMapper.xml similarity index 100% rename from xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobRegistryMapper.xml rename to xxl-job-admin/src/main/resources/mapper/XxlJobRegistryMapper.xml diff --git a/xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobUserMapper.xml b/xxl-job-admin/src/main/resources/mapper/XxlJobUserMapper.xml similarity index 85% rename from xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobUserMapper.xml rename to xxl-job-admin/src/main/resources/mapper/XxlJobUserMapper.xml index a5a885d5..420572b1 100644 --- a/xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobUserMapper.xml +++ b/xxl-job-admin/src/main/resources/mapper/XxlJobUserMapper.xml @@ -7,6 +7,7 @@ + @@ -15,6 +16,7 @@ t.id, t.username, t.password, + t.token, t.role, t.permission @@ -53,6 +55,12 @@ WHERE t.username = #{username} + + INSERT INTO xxl_job_user ( username, @@ -84,4 +92,10 @@ WHERE id = #{id} + + UPDATE xxl_job_user + SET token = #{token} + WHERE id = #{id} + + \ No newline at end of file diff --git a/xxl-job-admin/src/main/resources/static/js/login.1.js b/xxl-job-admin/src/main/resources/static/js/login.1.js index 92fb57ef..9adea338 100644 --- a/xxl-job-admin/src/main/resources/static/js/login.1.js +++ b/xxl-job-admin/src/main/resources/static/js/login.1.js @@ -46,7 +46,7 @@ $(function(){ element.parent('div').append(error); }, submitHandler : function(form) { - $.post(base_url + "/auth/login", $("#loginForm").serialize(), function(data, status) { + $.post(base_url + "/auth/doLogin", $("#loginForm").serialize(), function(data, status) { if (data.code == "200") { layer.msg( I18n.login_success ); setTimeout(function(){ diff --git a/xxl-job-admin/src/main/resources/templates/common/common.macro.ftl b/xxl-job-admin/src/main/resources/templates/common/common.macro.ftl index cf633a68..3f3a49b2 100644 --- a/xxl-job-admin/src/main/resources/templates/common/common.macro.ftl +++ b/xxl-job-admin/src/main/resources/templates/common/common.macro.ftl @@ -83,8 +83,8 @@