登录认证重构:密码加密算法从Md5改为Sha256;登录态改为登录后动态随机生成;提升系统安全性;

3.2.0-release
xuxueli 4 weeks ago
parent e02f2c65c0
commit cb1bd548a6

@ -2545,20 +2545,7 @@ public void execute() {
### 7.40 版本 v3.2.0 Release Notes[规划中] ### 7.40 版本 v3.2.0 Release Notes[规划中]
- 1、【强化】AI任务ollamaJobHandler优化针对 “model” 模型配置信息,从执行器侧文件类配置调整至调度中心“任务参数”动态配置,支持集成多模型、并结合任务动态配置切换。 - 1、【强化】AI任务ollamaJobHandler优化针对 “model” 模型配置信息,从执行器侧文件类配置调整至调度中心“任务参数”动态配置,支持集成多模型、并结合任务动态配置切换。
- 2、【修复】漏洞修复CVE-2025-7787针对 httpJobHandler 支持配置URL白名单限制防止服务器端请求伪造SSRF攻击。 - 2、【安全】登录认证重构密码加密算法从Md5改为Sha256登录态改为登录后动态随机生成提升系统安全性需要针对用户表进行字段调整同时需要重新初始化密码信息相关SQL脚本如下
- 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脚本如下
``` ```
// 1、用户表password字段需要调整长度执行如下命令 // 1、用户表password字段需要调整长度执行如下命令
ALTER TABLE xxl_job_user ALTER TABLE xxl_job_user
@ -2569,6 +2556,18 @@ ALTER TABLE xxl_job_user
// 2、存量用户密码需要修改可执行如下命令将密码初始化 “123456”也可以自行通过 “SHA256Tool.sha256” 工具生成其他初始化密码; // 2、存量用户密码需要修改可执行如下命令将密码初始化 “123456”也可以自行通过 “SHA256Tool.sha256” 工具生成其他初始化密码;
UPDATE xxl_job_user t SET t.password = '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92' WHERE t.username = {用户名}; 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[规划中] ### 7.41 版本 v3.2.1 Release Notes[规划中]

@ -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;
}

@ -0,0 +1,7 @@
package com.xxl.job.admin.constant;
public class Consts {
public static final String ADMIN_ROLE = "ADMIN";
}

@ -1,13 +1,13 @@
package com.xxl.job.admin.controller.biz; 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.admin.scheduler.conf.XxlJobAdminConfig;
import com.xxl.job.core.biz.AdminBiz; import com.xxl.job.core.biz.AdminBiz;
import com.xxl.job.core.biz.model.HandleCallbackParam; import com.xxl.job.core.biz.model.HandleCallbackParam;
import com.xxl.job.core.biz.model.RegistryParam; import com.xxl.job.core.biz.model.RegistryParam;
import com.xxl.job.core.biz.model.ReturnT; 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.job.core.util.XxlJobRemotingUtil;
import com.xxl.sso.core.annotation.XxlSso;
import com.xxl.tool.gson.GsonTool;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
@ -37,7 +37,7 @@ public class JobApiController {
*/ */
@RequestMapping("/{uri}") @RequestMapping("/{uri}")
@ResponseBody @ResponseBody
@PermissionLimit(limit=false) @XxlSso(login = false)
public ReturnT<String> api(HttpServletRequest request, @PathVariable("uri") String uri, @RequestBody(required = false) String data) { public ReturnT<String> api(HttpServletRequest request, @PathVariable("uri") String uri, @RequestBody(required = false) String data) {
// valid // valid

@ -1,11 +1,10 @@
package com.xxl.job.admin.controller.biz; 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.XxlJobInfo;
import com.xxl.job.admin.model.XxlJobLogGlue; import com.xxl.job.admin.model.XxlJobLogGlue;
import com.xxl.job.admin.util.I18nUtil; 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.biz.model.ReturnT;
import com.xxl.job.core.glue.GlueTypeEnum; import com.xxl.job.core.glue.GlueTypeEnum;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
@ -44,8 +43,8 @@ public class JobCodeController {
throw new RuntimeException(I18nUtil.getString("jobinfo_glue_gluetype_unvalid")); throw new RuntimeException(I18nUtil.getString("jobinfo_glue_gluetype_unvalid"));
} }
// valid permission // valid jobGroup permission
PermissionInterceptor.validJobGroupPermission(request, jobInfo.getJobGroup()); JobInfoController.validJobGroupPermission(request, jobInfo.getJobGroup());
// Glue类型-字典 // Glue类型-字典
model.addAttribute("GlueTypeEnum", GlueTypeEnum.values()); model.addAttribute("GlueTypeEnum", GlueTypeEnum.values());
@ -74,9 +73,9 @@ public class JobCodeController {
return new ReturnT<String>(500, I18nUtil.getString("jobinfo_glue_jobid_unvalid")); return new ReturnT<String>(500, I18nUtil.getString("jobinfo_glue_jobid_unvalid"));
} }
// valid permission // valid jobGroup permission
PermissionInterceptor.validJobGroupPermission(request, existsJobInfo.getJobGroup()); JobInfoController.validJobGroupPermission(request, existsJobInfo.getJobGroup());
// update new code // update new code
existsJobInfo.setGlueSource(glueSource); existsJobInfo.setGlueSource(glueSource);
existsJobInfo.setGlueRemark(glueRemark); existsJobInfo.setGlueRemark(glueRemark);

@ -1,6 +1,6 @@
package com.xxl.job.admin.controller.biz; 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.XxlJobGroup;
import com.xxl.job.admin.model.XxlJobRegistry; import com.xxl.job.admin.model.XxlJobRegistry;
import com.xxl.job.admin.util.I18nUtil; 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.admin.mapper.XxlJobRegistryMapper;
import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.enums.RegistryConfig; import com.xxl.job.core.enums.RegistryConfig;
import com.xxl.sso.core.annotation.XxlSso;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
@ -35,14 +36,14 @@ public class JobGroupController {
private XxlJobRegistryMapper xxlJobRegistryMapper; private XxlJobRegistryMapper xxlJobRegistryMapper;
@RequestMapping @RequestMapping
@PermissionLimit(adminuser = true) @XxlSso(role = Consts.ADMIN_ROLE)
public String index(Model model) { public String index(Model model) {
return "jobgroup/jobgroup.index"; return "jobgroup/jobgroup.index";
} }
@RequestMapping("/pageList") @RequestMapping("/pageList")
@ResponseBody @ResponseBody
@PermissionLimit(adminuser = true) @XxlSso(role = Consts.ADMIN_ROLE)
public Map<String, Object> pageList(HttpServletRequest request, public Map<String, Object> pageList(HttpServletRequest request,
@RequestParam(value = "start", required = false, defaultValue = "0") int start, @RequestParam(value = "start", required = false, defaultValue = "0") int start,
@RequestParam(value = "length", required = false, defaultValue = "10") int length, @RequestParam(value = "length", required = false, defaultValue = "10") int length,
@ -63,7 +64,7 @@ public class JobGroupController {
@RequestMapping("/save") @RequestMapping("/save")
@ResponseBody @ResponseBody
@PermissionLimit(adminuser = true) @XxlSso(role = Consts.ADMIN_ROLE)
public ReturnT<String> save(XxlJobGroup xxlJobGroup){ public ReturnT<String> save(XxlJobGroup xxlJobGroup){
// valid // valid
@ -107,7 +108,7 @@ public class JobGroupController {
@RequestMapping("/update") @RequestMapping("/update")
@ResponseBody @ResponseBody
@PermissionLimit(adminuser = true) @XxlSso(role = Consts.ADMIN_ROLE)
public ReturnT<String> update(XxlJobGroup xxlJobGroup){ public ReturnT<String> update(XxlJobGroup xxlJobGroup){
// valid // valid
if (xxlJobGroup.getAppname()==null || xxlJobGroup.getAppname().trim().length()==0) { if (xxlJobGroup.getAppname()==null || xxlJobGroup.getAppname().trim().length()==0) {
@ -176,7 +177,7 @@ public class JobGroupController {
@RequestMapping("/remove") @RequestMapping("/remove")
@ResponseBody @ResponseBody
@PermissionLimit(adminuser = true) @XxlSso(role = Consts.ADMIN_ROLE)
public ReturnT<String> remove(@RequestParam("id") int id){ public ReturnT<String> remove(@RequestParam("id") int id){
// valid // valid
@ -196,7 +197,7 @@ public class JobGroupController {
@RequestMapping("/loadById") @RequestMapping("/loadById")
@ResponseBody @ResponseBody
@PermissionLimit(adminuser = true) @XxlSso(role = Consts.ADMIN_ROLE)
public ReturnT<XxlJobGroup> loadById(@RequestParam("id") int id){ public ReturnT<XxlJobGroup> loadById(@RequestParam("id") int id){
XxlJobGroup jobGroup = xxlJobGroupMapper.load(id); XxlJobGroup jobGroup = xxlJobGroupMapper.load(id);
return jobGroup!=null?ReturnT.ofSuccess(jobGroup):ReturnT.ofFail(); return jobGroup!=null?ReturnT.ofSuccess(jobGroup):ReturnT.ofFail();

@ -1,21 +1,24 @@
package com.xxl.job.admin.controller.biz; package com.xxl.job.admin.controller.biz;
import com.xxl.job.admin.web.xxlsso.PermissionInterceptor; import com.xxl.job.admin.constant.Consts;
import com.xxl.job.admin.scheduler.exception.XxlJobException; import com.xxl.job.admin.mapper.XxlJobGroupMapper;
import com.xxl.job.admin.model.XxlJobGroup; import com.xxl.job.admin.model.XxlJobGroup;
import com.xxl.job.admin.model.XxlJobInfo; 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.route.ExecutorRouteStrategyEnum;
import com.xxl.job.admin.scheduler.scheduler.MisfireStrategyEnum; import com.xxl.job.admin.scheduler.scheduler.MisfireStrategyEnum;
import com.xxl.job.admin.scheduler.scheduler.ScheduleTypeEnum; import com.xxl.job.admin.scheduler.scheduler.ScheduleTypeEnum;
import com.xxl.job.admin.scheduler.thread.JobScheduleHelper; 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.service.XxlJobService;
import com.xxl.job.admin.util.I18nUtil;
import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.enums.ExecutorBlockStrategyEnum; import com.xxl.job.core.enums.ExecutorBlockStrategyEnum;
import com.xxl.job.core.glue.GlueTypeEnum; import com.xxl.job.core.glue.GlueTypeEnum;
import com.xxl.job.core.util.DateUtil; 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.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger; 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.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody; 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 * index controller
@ -53,11 +59,11 @@ public class JobInfoController {
model.addAttribute("MisfireStrategyEnum", MisfireStrategyEnum.values()); // 调度过期策略 model.addAttribute("MisfireStrategyEnum", MisfireStrategyEnum.values()); // 调度过期策略
// 执行器列表 // 执行器列表
List<XxlJobGroup> jobGroupList_all = xxlJobGroupMapper.findAll(); List<XxlJobGroup> jobGroupListTotal = xxlJobGroupMapper.findAll();
// filter group // filter group
List<XxlJobGroup> jobGroupList = PermissionInterceptor.filterJobGroupByRole(request, jobGroupList_all); List<XxlJobGroup> jobGroupList = filterJobGroupByPermission(request, jobGroupListTotal);
if (jobGroupList==null || jobGroupList.size()==0) { if (jobGroupList==null || jobGroupList.isEmpty()) {
throw new XxlJobException(I18nUtil.getString("jobgroup_empty")); throw new XxlJobException(I18nUtil.getString("jobgroup_empty"));
} }
@ -84,22 +90,20 @@ public class JobInfoController {
@ResponseBody @ResponseBody
public ReturnT<String> add(HttpServletRequest request, XxlJobInfo jobInfo) { public ReturnT<String> add(HttpServletRequest request, XxlJobInfo jobInfo) {
// valid permission // valid permission
PermissionInterceptor.validJobGroupPermission(request, jobInfo.getJobGroup()); LoginInfo loginInfo = validJobGroupPermission(request, jobInfo.getJobGroup());
// opt // opt
XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request); return xxlJobService.add(jobInfo, loginInfo);
return xxlJobService.add(jobInfo, loginUser);
} }
@RequestMapping("/update") @RequestMapping("/update")
@ResponseBody @ResponseBody
public ReturnT<String> update(HttpServletRequest request, XxlJobInfo jobInfo) { public ReturnT<String> update(HttpServletRequest request, XxlJobInfo jobInfo) {
// valid permission // valid permission
PermissionInterceptor.validJobGroupPermission(request, jobInfo.getJobGroup()); LoginInfo loginInfo = validJobGroupPermission(request, jobInfo.getJobGroup());
// opt // opt
XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request); return xxlJobService.update(jobInfo, loginInfo);
return xxlJobService.update(jobInfo, loginUser);
} }
@RequestMapping("/remove") @RequestMapping("/remove")
@ -128,9 +132,10 @@ public class JobInfoController {
@RequestParam("addressList") String addressList) { @RequestParam("addressList") String addressList) {
// login user // login user
XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request); Response<LoginInfo> loginInfoResponse = XxlSsoHelper.loginCheckWithAttr(request);
// trigger // trigger
return xxlJobService.trigger(loginUser, id, executorParam, addressList); return xxlJobService.trigger(loginInfoResponse.getData(), id, executorParam, addressList);
} }
@RequestMapping("/nextTriggerTime") @RequestMapping("/nextTriggerTime")
@ -160,5 +165,51 @@ public class JobInfoController {
return ReturnT.ofSuccess(result); 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<String> 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<LoginInfo> 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<XxlJobGroup> filterJobGroupByPermission(HttpServletRequest request, List<XxlJobGroup> jobGroupListTotal){
Response<LoginInfo> loginInfoResponse = XxlSsoHelper.loginCheckWithAttr(request);
if (XxlSsoHelper.hasRole(loginInfoResponse.getData(), Consts.ADMIN_ROLE).isSuccess()) {
return jobGroupListTotal;
} else {
List<String> 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();
}
}
} }

@ -1,16 +1,15 @@
package com.xxl.job.admin.controller.biz; package com.xxl.job.admin.controller.biz;
import com.xxl.job.admin.web.xxlsso.PermissionInterceptor; import com.xxl.job.admin.mapper.XxlJobGroupMapper;
import com.xxl.job.admin.scheduler.complete.XxlJobCompleter; import com.xxl.job.admin.mapper.XxlJobInfoMapper;
import com.xxl.job.admin.scheduler.exception.XxlJobException; import com.xxl.job.admin.mapper.XxlJobLogMapper;
import com.xxl.job.admin.model.XxlJobGroup; import com.xxl.job.admin.model.XxlJobGroup;
import com.xxl.job.admin.model.XxlJobInfo; import com.xxl.job.admin.model.XxlJobInfo;
import com.xxl.job.admin.model.XxlJobLog; 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.scheduler.scheduler.XxlJobScheduler;
import com.xxl.job.admin.util.I18nUtil; 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.ExecutorBiz;
import com.xxl.job.core.biz.model.KillParam; import com.xxl.job.core.biz.model.KillParam;
import com.xxl.job.core.biz.model.LogParam; 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) { public String index(HttpServletRequest request, Model model, @RequestParam(value = "jobId", required = false, defaultValue = "0") Integer jobId) {
// 执行器列表 // 执行器列表
List<XxlJobGroup> jobGroupList_all = xxlJobGroupMapper.findAll(); List<XxlJobGroup> jobGroupListTotal = xxlJobGroupMapper.findAll();
// filter group // filter group
List<XxlJobGroup> jobGroupList = PermissionInterceptor.filterJobGroupByRole(request, jobGroupList_all); List<XxlJobGroup> jobGroupList = JobInfoController.filterJobGroupByPermission(request, jobGroupListTotal);
if (jobGroupList==null || jobGroupList.size()==0) { if (jobGroupList==null || jobGroupList.isEmpty()) {
throw new XxlJobException(I18nUtil.getString("jobgroup_empty")); throw new XxlJobException(I18nUtil.getString("jobgroup_empty"));
} }
@ -74,7 +73,7 @@ public class JobLogController {
model.addAttribute("jobInfo", jobInfo); model.addAttribute("jobInfo", jobInfo);
// valid permission // valid permission
PermissionInterceptor.validJobGroupPermission(request, jobInfo.getJobGroup()); JobInfoController.validJobGroupPermission(request, jobInfo.getJobGroup());
} }
return "joblog/joblog.index"; return "joblog/joblog.index";
@ -97,9 +96,9 @@ public class JobLogController {
@RequestParam("logStatus") int logStatus, @RequestParam("logStatus") int logStatus,
@RequestParam("filterTime") String filterTime) { @RequestParam("filterTime") String filterTime) {
// valid permission // valid jobGroup permission
PermissionInterceptor.validJobGroupPermission(request, jobGroup); // 仅管理员支持查询全部;普通用户仅支持查询有权限的 jobGroup JobInfoController.validJobGroupPermission(request, jobGroup);
// parse param // parse param
Date triggerTimeStart = null; Date triggerTimeStart = null;
Date triggerTimeEnd = null; Date triggerTimeEnd = null;
@ -133,7 +132,7 @@ public class JobLogController {
} }
// valid permission // valid permission
PermissionInterceptor.validJobGroupPermission(request, jobLog.getJobGroup()); JobInfoController.validJobGroupPermission(request, jobLog.getJobGroup());
// data // data
model.addAttribute("triggerCode", jobLog.getTriggerCode()); model.addAttribute("triggerCode", jobLog.getTriggerCode());
@ -249,7 +248,7 @@ public class JobLogController {
@RequestParam("jobId") int jobId, @RequestParam("jobId") int jobId,
@RequestParam("type") int type){ @RequestParam("type") int type){
// valid permission // valid permission
PermissionInterceptor.validJobGroupPermission(request, jobGroup); JobInfoController.validJobGroupPermission(request, jobGroup);
// opt // opt
Date clearBeforeTime = null; Date clearBeforeTime = null;

@ -1,14 +1,17 @@
package com.xxl.job.admin.controller.biz; 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.XxlJobGroupMapper;
import com.xxl.job.admin.mapper.XxlJobUserMapper; import com.xxl.job.admin.mapper.XxlJobUserMapper;
import com.xxl.job.admin.model.XxlJobGroup; import com.xxl.job.admin.model.XxlJobGroup;
import com.xxl.job.admin.model.XxlJobUser; import com.xxl.job.admin.model.XxlJobUser;
import com.xxl.job.admin.util.I18nUtil; 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.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.encrypt.SHA256Tool;
import com.xxl.tool.response.Response;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
@ -35,7 +38,7 @@ public class JobUserController {
private XxlJobGroupMapper xxlJobGroupMapper; private XxlJobGroupMapper xxlJobGroupMapper;
@RequestMapping @RequestMapping
@PermissionLimit(adminuser = true) @XxlSso(role = Consts.ADMIN_ROLE)
public String index(Model model) { public String index(Model model) {
// 执行器列表 // 执行器列表
@ -47,7 +50,7 @@ public class JobUserController {
@RequestMapping("/pageList") @RequestMapping("/pageList")
@ResponseBody @ResponseBody
@PermissionLimit(adminuser = true) @XxlSso(role = Consts.ADMIN_ROLE)
public Map<String, Object> pageList(@RequestParam(value = "start", required = false, defaultValue = "0") int start, public Map<String, Object> pageList(@RequestParam(value = "start", required = false, defaultValue = "0") int start,
@RequestParam(value = "length", required = false, defaultValue = "10") int length, @RequestParam(value = "length", required = false, defaultValue = "10") int length,
@RequestParam("username") String username, @RequestParam("username") String username,
@ -74,7 +77,7 @@ public class JobUserController {
@RequestMapping("/add") @RequestMapping("/add")
@ResponseBody @ResponseBody
@PermissionLimit(adminuser = true) @XxlSso(role = Consts.ADMIN_ROLE)
public ReturnT<String> add(XxlJobUser xxlJobUser) { public ReturnT<String> add(XxlJobUser xxlJobUser) {
// valid username // valid username
@ -110,12 +113,12 @@ public class JobUserController {
@RequestMapping("/update") @RequestMapping("/update")
@ResponseBody @ResponseBody
@PermissionLimit(adminuser = true) @XxlSso(role = Consts.ADMIN_ROLE)
public ReturnT<String> update(HttpServletRequest request, XxlJobUser xxlJobUser) { public ReturnT<String> update(HttpServletRequest request, XxlJobUser xxlJobUser) {
// avoid opt login seft // avoid opt login seft
XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request); Response<LoginInfo> loginInfoResponse = XxlSsoHelper.loginCheckWithAttr(request);
if (loginUser.getUsername().equals(xxlJobUser.getUsername())) { if (loginInfoResponse.getData().getUserName().equals(xxlJobUser.getUsername())) {
return ReturnT.ofFail(I18nUtil.getString("user_update_loginuser_limit")); return ReturnT.ofFail(I18nUtil.getString("user_update_loginuser_limit"));
} }
@ -139,12 +142,12 @@ public class JobUserController {
@RequestMapping("/remove") @RequestMapping("/remove")
@ResponseBody @ResponseBody
@PermissionLimit(adminuser = true) @XxlSso(role = Consts.ADMIN_ROLE)
public ReturnT<String> remove(HttpServletRequest request, @RequestParam("id") int id) { public ReturnT<String> remove(HttpServletRequest request, @RequestParam("id") int id) {
// avoid opt login seft // avoid opt login seft
XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request); Response<LoginInfo> loginInfoResponse = XxlSsoHelper.loginCheckWithAttr(request);
if (loginUser.getId() == id) { if (Integer.parseInt(loginInfoResponse.getData().getUserId()) == id) {
return ReturnT.ofFail(I18nUtil.getString("user_update_loginuser_limit")); return ReturnT.ofFail(I18nUtil.getString("user_update_loginuser_limit"));
} }
@ -175,8 +178,8 @@ public class JobUserController {
String passwordHash = SHA256Tool.sha256(password); String passwordHash = SHA256Tool.sha256(password);
// valid old pwd // valid old pwd
XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request); Response<LoginInfo> loginInfoResponse = XxlSsoHelper.loginCheckWithAttr(request);
XxlJobUser existUser = xxlJobUserMapper.loadByUserName(loginUser.getUsername()); XxlJobUser existUser = xxlJobUserMapper.loadByUserName(loginInfoResponse.getData().getUserName());
if (!oldPasswordHash.equals(existUser.getPassword())) { if (!oldPasswordHash.equals(existUser.getPassword())) {
return ReturnT.ofFail(I18nUtil.getString("change_pwd_field_oldpwd") + I18nUtil.getString("system_unvalid")); return ReturnT.ofFail(I18nUtil.getString("change_pwd_field_oldpwd") + I18nUtil.getString("system_unvalid"));
} }

@ -1,8 +1,16 @@
package com.xxl.job.admin.controller.login; package com.xxl.job.admin.controller.login;
import com.xxl.job.admin.annotation.PermissionLimit; import com.xxl.job.admin.mapper.XxlJobUserMapper;
import com.xxl.job.admin.service.impl.LoginService; 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.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.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
@ -22,39 +30,65 @@ import org.springframework.web.servlet.view.RedirectView;
@RequestMapping("/auth") @RequestMapping("/auth")
public class LoginController { public class LoginController {
@Resource @Resource
private LoginService loginService; private XxlJobUserMapper xxlJobUserMapper;
@RequestMapping("/login")
@XxlSso(login = false)
public ModelAndView login(HttpServletRequest request, HttpServletResponse response, ModelAndView modelAndView) {
@RequestMapping("/toLogin") // xxl-sso, logincheck
@PermissionLimit(limit=false) Response<LoginInfo> loginInfoResponse = XxlSsoHelper.loginCheckWithCookie(request, response);
public ModelAndView toLogin(HttpServletRequest request, HttpServletResponse response, ModelAndView modelAndView) { if (loginInfoResponse.isSuccess()) {
if (loginService.ifLogin(request, response) != null) {
modelAndView.setView(new RedirectView("/",true,false)); modelAndView.setView(new RedirectView("/",true,false));
return modelAndView; return modelAndView;
} }
return new ModelAndView("login"); return new ModelAndView("login");
} }
@RequestMapping(value="/login", method=RequestMethod.POST) @RequestMapping(value="/doLogin", method=RequestMethod.POST)
@ResponseBody @ResponseBody
@PermissionLimit(limit=false) @XxlSso(login = false)
public ReturnT<String> loginDo(HttpServletRequest request, public ReturnT<String> doLogin(HttpServletRequest request,
HttpServletResponse response, HttpServletResponse response,
@RequestParam("userName") String userName, @RequestParam("userName") String userName,
@RequestParam("password") String password, @RequestParam("password") String password,
@RequestParam(value = "ifRemember", required = false) String ifRemember){ @RequestParam(value = "ifRemember", required = false) String ifRemember){
boolean ifRem = (ifRemember!=null && ifRemember.trim().length()>0 && "on".equals(ifRemember))?true:false; // param
return loginService.login(request, response, userName, password, ifRem); 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<String> result= XxlSsoHelper.loginWithCookie(loginInfo, response, ifRem);
return ReturnT.of(result.getCode(), result.getMsg());
} }
@RequestMapping(value="/logout", method=RequestMethod.POST) @RequestMapping(value="/logout", method=RequestMethod.POST)
@ResponseBody @ResponseBody
@PermissionLimit(limit=false) @XxlSso(login = false)
public ReturnT<String> logout(HttpServletRequest request, HttpServletResponse response){ public ReturnT<String> logout(HttpServletRequest request, HttpServletResponse response){
return loginService.logout(request, response); // xxl-sso, do logout
Response<String> result = XxlSsoHelper.logoutWithCookie(request, response);
return ReturnT.of(result.getCode(), result.getMsg());
} }
} }

@ -1,6 +1,7 @@
package com.xxl.job.admin.mapper; package com.xxl.job.admin.mapper;
import com.xxl.job.admin.model.XxlJobUser; import com.xxl.job.admin.model.XxlJobUser;
import com.xxl.tool.response.Response;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import java.util.List; import java.util.List;
@ -22,10 +23,14 @@ public interface XxlJobUserMapper {
public XxlJobUser loadByUserName(@Param("username") String username); public XxlJobUser loadByUserName(@Param("username") String username);
public XxlJobUser loadById(@Param("id") int id);
public int save(XxlJobUser xxlJobUser); public int save(XxlJobUser xxlJobUser);
public int update(XxlJobUser xxlJobUser); public int update(XxlJobUser xxlJobUser);
public int delete(@Param("id") int id); public int delete(@Param("id") int id);
public int updateToken(@Param("id") int id, @Param("token") String token);
} }

@ -10,8 +10,9 @@ public class XxlJobUser {
private int id; private int id;
private String username; // 账号 private String username; // 账号
private String password; // 密码 private String password; // 密码
private String token; // 登录token
private int role; // 角色0-普通用户、1-管理员 private int role; // 角色0-普通用户、1-管理员
private String permission; // 权限执行器ID列表多个逗号分割 private String permission; // 权限执行器ID列表多个逗号分割
public int getId() { public int getId() {
return id; return id;
@ -37,6 +38,14 @@ public class XxlJobUser {
this.password = password; this.password = password;
} }
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public int getRole() { public int getRole() {
return role; return role;
} }

@ -4,6 +4,7 @@ package com.xxl.job.admin.service;
import com.xxl.job.admin.model.XxlJobInfo; import com.xxl.job.admin.model.XxlJobInfo;
import com.xxl.job.admin.model.XxlJobUser; import com.xxl.job.admin.model.XxlJobUser;
import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.sso.core.model.LoginInfo;
import java.util.Date; import java.util.Date;
import java.util.Map; import java.util.Map;
@ -34,7 +35,7 @@ public interface XxlJobService {
* @param jobInfo * @param jobInfo
* @return * @return
*/ */
public ReturnT<String> add(XxlJobInfo jobInfo, XxlJobUser loginUser); public ReturnT<String> add(XxlJobInfo jobInfo, LoginInfo loginInfo);
/** /**
* update job * update job
@ -42,7 +43,7 @@ public interface XxlJobService {
* @param jobInfo * @param jobInfo
* @return * @return
*/ */
public ReturnT<String> update(XxlJobInfo jobInfo, XxlJobUser loginUser); public ReturnT<String> update(XxlJobInfo jobInfo, LoginInfo loginInfo);
/** /**
* remove job * remove job
@ -71,13 +72,13 @@ public interface XxlJobService {
/** /**
* trigger * trigger
* *
* @param loginUser * @param loginInfo
* @param jobId * @param jobId
* @param executorParam * @param executorParam
* @param addressList * @param addressList
* @return * @return
*/ */
public ReturnT<String> trigger(XxlJobUser loginUser, int jobId, String executorParam, String addressList); public ReturnT<String> trigger(LoginInfo loginInfo, int jobId, String executorParam, String addressList);
/** /**
* dashboard info * dashboard info

@ -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<String> 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<String>(500, I18nUtil.getString("login_param_empty"));
}
// valid passowrd
XxlJobUser xxlJobUser = xxlJobUserMapper.loadByUserName(username);
if (xxlJobUser == null) {
return new ReturnT<String>(500, I18nUtil.getString("login_param_unvalid"));
}
String passwordHash = SHA256Tool.sha256(password);
if (!passwordHash.equals(xxlJobUser.getPassword())) {
return new ReturnT<String>(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<String> 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;
}
}

@ -1,23 +1,25 @@
package com.xxl.job.admin.service.impl; 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.XxlJobGroup;
import com.xxl.job.admin.model.XxlJobInfo; import com.xxl.job.admin.model.XxlJobInfo;
import com.xxl.job.admin.model.XxlJobLogReport; import com.xxl.job.admin.model.XxlJobLogReport;
import com.xxl.job.admin.model.XxlJobUser; 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.route.ExecutorRouteStrategyEnum;
import com.xxl.job.admin.scheduler.scheduler.MisfireStrategyEnum; import com.xxl.job.admin.scheduler.scheduler.MisfireStrategyEnum;
import com.xxl.job.admin.scheduler.scheduler.ScheduleTypeEnum; import com.xxl.job.admin.scheduler.scheduler.ScheduleTypeEnum;
import com.xxl.job.admin.scheduler.thread.JobScheduleHelper; import com.xxl.job.admin.scheduler.thread.JobScheduleHelper;
import com.xxl.job.admin.scheduler.thread.JobTriggerPoolHelper; import com.xxl.job.admin.scheduler.thread.JobTriggerPoolHelper;
import com.xxl.job.admin.scheduler.trigger.TriggerTypeEnum; 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.service.XxlJobService;
import com.xxl.job.admin.util.I18nUtil;
import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.enums.ExecutorBlockStrategyEnum; import com.xxl.job.core.enums.ExecutorBlockStrategyEnum;
import com.xxl.job.core.glue.GlueTypeEnum; import com.xxl.job.core.glue.GlueTypeEnum;
import com.xxl.job.core.util.DateUtil; import com.xxl.job.core.util.DateUtil;
import com.xxl.sso.core.model.LoginInfo;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -61,7 +63,7 @@ public class XxlJobServiceImpl implements XxlJobService {
} }
@Override @Override
public ReturnT<String> add(XxlJobInfo jobInfo, XxlJobUser loginUser) { public ReturnT<String> add(XxlJobInfo jobInfo, LoginInfo loginInfo) {
// valid base // valid base
XxlJobGroup group = xxlJobGroupMapper.load(jobInfo.getJobGroup()); XxlJobGroup group = xxlJobGroupMapper.load(jobInfo.getJobGroup());
@ -131,7 +133,8 @@ public class XxlJobServiceImpl implements XxlJobService {
return new ReturnT<String>(ReturnT.FAIL_CODE, return new ReturnT<String>(ReturnT.FAIL_CODE,
MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_not_found")), childJobIdItem)); 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<String>(ReturnT.FAIL_CODE, return new ReturnT<String>(ReturnT.FAIL_CODE,
MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_permission_limit")), childJobIdItem)); MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_permission_limit")), childJobIdItem));
} }
@ -175,7 +178,7 @@ public class XxlJobServiceImpl implements XxlJobService {
} }
@Override @Override
public ReturnT<String> update(XxlJobInfo jobInfo, XxlJobUser loginUser) { public ReturnT<String> update(XxlJobInfo jobInfo, LoginInfo loginInfo) {
// valid base // valid base
if (jobInfo.getJobDesc()==null || jobInfo.getJobDesc().trim().length()==0) { if (jobInfo.getJobDesc()==null || jobInfo.getJobDesc().trim().length()==0) {
@ -236,7 +239,8 @@ public class XxlJobServiceImpl implements XxlJobService {
return new ReturnT<String>(ReturnT.FAIL_CODE, return new ReturnT<String>(ReturnT.FAIL_CODE,
MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_not_found")), childJobIdItem)); 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<String>(ReturnT.FAIL_CODE, return new ReturnT<String>(ReturnT.FAIL_CODE,
MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_permission_limit")), childJobIdItem)); MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_permission_limit")), childJobIdItem));
} }
@ -378,16 +382,17 @@ public class XxlJobServiceImpl implements XxlJobService {
@Override @Override
public ReturnT<String> trigger(XxlJobUser loginUser, int jobId, String executorParam, String addressList) { public ReturnT<String> trigger(LoginInfo loginInfo, int jobId, String executorParam, String addressList) {
// permission // permission
if (loginUser == null) { if (loginInfo == null) {
return ReturnT.ofFail(I18nUtil.getString("system_permission_limit")); return ReturnT.ofFail(I18nUtil.getString("system_permission_limit"));
} }
XxlJobInfo xxlJobInfo = xxlJobInfoMapper.loadById(jobId); XxlJobInfo xxlJobInfo = xxlJobInfoMapper.loadById(jobId);
if (xxlJobInfo == null) { if (xxlJobInfo == null) {
return ReturnT.ofFail(I18nUtil.getString("jobinfo_glue_jobid_unvalid")); 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")); return ReturnT.ofFail(I18nUtil.getString("system_permission_limit"));
} }
@ -400,17 +405,6 @@ public class XxlJobServiceImpl implements XxlJobService {
return ReturnT.ofSuccess(); return ReturnT.ofSuccess();
} }
private boolean hasPermission(XxlJobUser loginUser, int jobGroup){
if (loginUser.getRole() == 1) {
return true;
}
List<String> 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 @Override
public Map<String, Object> dashboardInfo() { public Map<String, Object> dashboardInfo() {

@ -1,7 +1,7 @@
package com.xxl.job.admin.util; package com.xxl.job.admin.util;
import com.xxl.job.admin.scheduler.conf.XxlJobAdminConfig; 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.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
@ -76,4 +76,5 @@ public class I18nUtil {
return GsonTool.toJson(map); return GsonTool.toJson(map);
} }
} }

@ -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.JsonGenerationException;
//import com.fasterxml.jackson.core.JsonParseException; //import com.fasterxml.jackson.core.JsonParseException;

@ -5,6 +5,7 @@ import com.xxl.tool.freemarker.FtlTool;
import jakarta.servlet.http.Cookie; import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.servlet.AsyncHandlerInterceptor; import org.springframework.web.servlet.AsyncHandlerInterceptor;
import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.ModelAndView;
@ -18,7 +19,7 @@ import java.util.HashMap;
* *
* @author xuxueli 2015-12-12 18:09:04 * @author xuxueli 2015-12-12 18:09:04
*/ */
@Component @Configuration
public class CommonDataInterceptor implements WebMvcConfigurer { public class CommonDataInterceptor implements WebMvcConfigurer {
@Override @Override

@ -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<XxlJobGroup> filterJobGroupByRole(HttpServletRequest request, List<XxlJobGroup> jobGroupList_all){
List<XxlJobGroup> 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<String> 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;
}
}

@ -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
*
* 1store by database
* 2If 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<String> 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<String> update(LoginInfo loginInfo) {
return Response.ofFail("not support");
}
@Override
public Response<String> 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<LoginInfo> get(String userId) {
// load login-user
XxlJobUser user = xxlJobUserMapper.loadById(Integer.parseInt(userId));
if (user == null) {
return Response.ofFail("userId invalid.");
}
// parse role
List<String> roleList = user.getRole()==1? List.of(Consts.ADMIN_ROLE):null;
// parse jobGroup permission
Map<String, String> 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);
}
}

@ -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("/**");
}
}

@ -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("/**");
}
}

@ -20,7 +20,7 @@ spring.freemarker.settings.number_format=0.##########
spring.freemarker.settings.new_builtin_class_resolver=safer spring.freemarker.settings.new_builtin_class_resolver=safer
### mybatis ### mybatis
mybatis.mapper-locations=classpath:/mybatis-mapper/*Mapper.xml mybatis.mapper-locations=classpath:/mapper/*Mapper.xml
### datasource-pool ### datasource-pool
spring.datasource.type=com.zaxxer.hikari.HikariDataSource spring.datasource.type=com.zaxxer.hikari.HikariDataSource
@ -66,3 +66,9 @@ xxl.job.triggerpool.slow.max=100
### xxl-job, log retention days ### xxl-job, log retention days
xxl.job.logretentiondays=30 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

@ -1,71 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xxl.job.admin.mapper.XxlJobLogGlueMapper"> <mapper namespace="com.xxl.job.admin.mapper.XxlJobLogGlueMapper">
<resultMap id="XxlJobLogGlue" type="com.xxl.job.admin.model.XxlJobLogGlue" > <resultMap id="XxlJobLogGlue" type="com.xxl.job.admin.model.XxlJobLogGlue" >
<result column="id" property="id" /> <result column="id" property="id" />
<result column="job_id" property="jobId" /> <result column="job_id" property="jobId" />
<result column="glue_type" property="glueType" /> <result column="glue_type" property="glueType" />
<result column="glue_source" property="glueSource" /> <result column="glue_source" property="glueSource" />
<result column="glue_remark" property="glueRemark" /> <result column="glue_remark" property="glueRemark" />
<result column="add_time" property="addTime" /> <result column="add_time" property="addTime" />
<result column="update_time" property="updateTime" /> <result column="update_time" property="updateTime" />
</resultMap> </resultMap>
<sql id="Base_Column_List"> <sql id="Base_Column_List">
t.id, t.id,
t.job_id, t.job_id,
t.glue_type, t.glue_type,
t.glue_source, t.glue_source,
t.glue_remark, t.glue_remark,
t.add_time, t.add_time,
t.update_time t.update_time
</sql> </sql>
<insert id="save" parameterType="com.xxl.job.admin.model.XxlJobLogGlue" useGeneratedKeys="true" keyProperty="id" > <insert id="save" parameterType="com.xxl.job.admin.model.XxlJobLogGlue" useGeneratedKeys="true" keyProperty="id" >
INSERT INTO xxl_job_logglue ( INSERT INTO xxl_job_logglue (
`job_id`, `job_id`,
`glue_type`, `glue_type`,
`glue_source`, `glue_source`,
`glue_remark`, `glue_remark`,
`add_time`, `add_time`,
`update_time` `update_time`
) VALUES ( ) VALUES (
#{jobId}, #{jobId},
#{glueType}, #{glueType},
#{glueSource}, #{glueSource},
#{glueRemark}, #{glueRemark},
#{addTime}, #{addTime},
#{updateTime} #{updateTime}
); );
<!--<selectKey resultType="java.lang.Integer" order="AFTER" keyProperty="id"> <!--<selectKey resultType="java.lang.Integer" order="AFTER" keyProperty="id">
SELECT LAST_INSERT_ID() SELECT LAST_INSERT_ID()
</selectKey>--> </selectKey>-->
</insert> </insert>
<select id="findByJobId" parameterType="java.lang.Integer" resultMap="XxlJobLogGlue"> <select id="findByJobId" parameterType="java.lang.Integer" resultMap="XxlJobLogGlue">
SELECT <include refid="Base_Column_List" /> SELECT <include refid="Base_Column_List" />
FROM xxl_job_logglue AS t FROM xxl_job_logglue AS t
WHERE t.job_id = #{jobId} WHERE t.job_id = #{jobId}
ORDER BY id DESC ORDER BY id DESC
</select> </select>
<delete id="removeOld" > <delete id="removeOld" >
DELETE FROM xxl_job_logglue DELETE FROM xxl_job_logglue
WHERE id NOT in( WHERE id NOT in(
SELECT id FROM( SELECT id FROM(
SELECT id FROM xxl_job_logglue SELECT id FROM xxl_job_logglue
WHERE `job_id` = #{jobId} WHERE `job_id` = #{jobId}
ORDER BY update_time desc ORDER BY update_time desc
LIMIT 0, #{limit} LIMIT 0, #{limit}
) t1 ) t1
) AND `job_id` = #{jobId} ) AND `job_id` = #{jobId}
</delete> </delete>
<delete id="deleteByJobId" parameterType="java.lang.Integer" > <delete id="deleteByJobId" parameterType="java.lang.Integer" >
DELETE FROM xxl_job_logglue DELETE FROM xxl_job_logglue
WHERE `job_id` = #{jobId} WHERE `job_id` = #{jobId}
</delete> </delete>
</mapper> </mapper>

@ -7,6 +7,7 @@
<result column="id" property="id" /> <result column="id" property="id" />
<result column="username" property="username" /> <result column="username" property="username" />
<result column="password" property="password" /> <result column="password" property="password" />
<result column="token" property="token" />
<result column="role" property="role" /> <result column="role" property="role" />
<result column="permission" property="permission" /> <result column="permission" property="permission" />
</resultMap> </resultMap>
@ -15,6 +16,7 @@
t.id, t.id,
t.username, t.username,
t.password, t.password,
t.token,
t.role, t.role,
t.permission t.permission
</sql> </sql>
@ -53,6 +55,12 @@
WHERE t.username = #{username} WHERE t.username = #{username}
</select> </select>
<select id="loadById" parameterType="java.util.HashMap" resultMap="XxlJobUser">
SELECT <include refid="Base_Column_List" />
FROM xxl_job_user AS t
WHERE t.id = #{id}
</select>
<insert id="save" parameterType="com.xxl.job.admin.model.XxlJobUser" useGeneratedKeys="true" keyProperty="id" > <insert id="save" parameterType="com.xxl.job.admin.model.XxlJobUser" useGeneratedKeys="true" keyProperty="id" >
INSERT INTO xxl_job_user ( INSERT INTO xxl_job_user (
username, username,
@ -84,4 +92,10 @@
WHERE id = #{id} WHERE id = #{id}
</delete> </delete>
<update id="updateToken" parameterType="java.util.HashMap" >
UPDATE xxl_job_user
SET token = #{token}
WHERE id = #{id}
</update>
</mapper> </mapper>

@ -46,7 +46,7 @@ $(function(){
element.parent('div').append(error); element.parent('div').append(error);
}, },
submitHandler : function(form) { 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") { if (data.code == "200") {
layer.msg( I18n.login_success ); layer.msg( I18n.login_success );
setTimeout(function(){ setTimeout(function(){

@ -83,8 +83,8 @@
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">
<#-- login user --> <#-- login user -->
<li class="dropdown"> <li class="dropdown">
<a href="javascript:" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false"> <a href="javascript:" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false" style="font-weight: bold;">
${I18n.system_welcome} ${loginUser.username!} ${I18n.system_welcome} ${xxl_sso_user.userName!}
<span class="caret"></span> <span class="caret"></span>
</a> </a>
<ul class="dropdown-menu" role="menu"> <ul class="dropdown-menu" role="menu">
@ -141,7 +141,7 @@
<li class="nav-click <#if pageName == "index">active</#if>" ><a href="${request.contextPath}/"><i class="fa fa-circle-o text-aqua"></i><span>${I18n.job_dashboard_name}</span></a></li> <li class="nav-click <#if pageName == "index">active</#if>" ><a href="${request.contextPath}/"><i class="fa fa-circle-o text-aqua"></i><span>${I18n.job_dashboard_name}</span></a></li>
<li class="nav-click <#if pageName == "jobinfo">active</#if>" ><a href="${request.contextPath}/jobinfo"><i class="fa fa-circle-o text-yellow"></i><span>${I18n.jobinfo_name}</span></a></li> <li class="nav-click <#if pageName == "jobinfo">active</#if>" ><a href="${request.contextPath}/jobinfo"><i class="fa fa-circle-o text-yellow"></i><span>${I18n.jobinfo_name}</span></a></li>
<li class="nav-click <#if pageName == "joblog">active</#if>" ><a href="${request.contextPath}/joblog"><i class="fa fa-circle-o text-green"></i><span>${I18n.joblog_name}</span></a></li> <li class="nav-click <#if pageName == "joblog">active</#if>" ><a href="${request.contextPath}/joblog"><i class="fa fa-circle-o text-green"></i><span>${I18n.joblog_name}</span></a></li>
<#if loginUser.role == 1> <#if xxl_sso_user.roleList?? && xxl_sso_user.roleList?seq_contains("ADMIN") >
<li class="nav-click <#if pageName == "jobgroup">active</#if>" ><a href="${request.contextPath}/jobgroup"><i class="fa fa-circle-o text-red"></i><span>${I18n.jobgroup_name}</span></a></li> <li class="nav-click <#if pageName == "jobgroup">active</#if>" ><a href="${request.contextPath}/jobgroup"><i class="fa fa-circle-o text-red"></i><span>${I18n.jobgroup_name}</span></a></li>
<li class="nav-click <#if pageName == "user">active</#if>" ><a href="${request.contextPath}/user"><i class="fa fa-circle-o text-purple"></i><span>${I18n.user_manage}</span></a></li> <li class="nav-click <#if pageName == "user">active</#if>" ><a href="${request.contextPath}/user"><i class="fa fa-circle-o text-purple"></i><span>${I18n.user_manage}</span></a></li>
</#if> </#if>

@ -30,7 +30,7 @@
<div class="input-group"> <div class="input-group">
<span class="input-group-addon">${I18n.jobinfo_field_jobgroup}</span> <span class="input-group-addon">${I18n.jobinfo_field_jobgroup}</span>
<select class="form-control" id="jobGroup" paramVal="<#if jobInfo?exists>${jobInfo.jobGroup}</#if>" > <select class="form-control" id="jobGroup" paramVal="<#if jobInfo?exists>${jobInfo.jobGroup}</#if>" >
<#if loginUser.role == 1> <#if xxl_sso_user.roleList?? && xxl_sso_user.roleList?seq_contains("ADMIN") >
<option value="0" >${I18n.system_all}</option> <#-- jobGroup --> <option value="0" >${I18n.system_all}</option> <#-- jobGroup -->
</#if> </#if>
<#list JobGroupList as group> <#list JobGroupList as group>

@ -1,6 +1,6 @@
package com.xxl.job.admin.controller; package com.xxl.job.admin.controller;
import com.xxl.job.admin.service.impl.LoginService; import com.xxl.sso.core.constant.Const;
import jakarta.servlet.http.Cookie; import jakarta.servlet.http.Cookie;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -21,12 +21,12 @@ public class JobInfoControllerTest extends AbstractSpringMvcTest {
@BeforeEach @BeforeEach
public void login() throws Exception { public void login() throws Exception {
MvcResult ret = mockMvc.perform( MvcResult ret = mockMvc.perform(
post("/auth/login") post("/auth/doLogin")
.contentType(MediaType.APPLICATION_FORM_URLENCODED) .contentType(MediaType.APPLICATION_FORM_URLENCODED)
.param("userName", "admin") .param("userName", "admin")
.param("password", "123456") .param("password", "123456")
).andReturn(); ).andReturn();
cookie = ret.getResponse().getCookie(LoginService.LOGIN_IDENTITY_KEY); cookie = ret.getResponse().getCookie(Const.XXL_SSO_TOKEN);
} }
@Test @Test

@ -2,25 +2,32 @@ package com.xxl.job.core.util;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.List; import java.util.ArrayList;
import java.util.HashMap;
/** /**
* gson tool From https://github.com/xuxueli/xxl-tool
*
* @author xuxueli 2020-04-11 20:56:31 * @author xuxueli 2020-04-11 20:56:31
*/ */
public class GsonTool { public class GsonTool {
private static Gson gson = null; private static Gson gson = null;
static { static {
gson= new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create(); gson= new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").disableHtmlEscaping().create();
} }
/** /**
* Object json * Object json
* *
* <pre>
* String json = GsonTool.toJson(new Demo());
* </pre>
*
* @param src * @param src
* @return String * @return String
*/ */
@ -31,6 +38,10 @@ public class GsonTool {
/** /**
* json clsObject * json clsObject
* *
* <pre>
* Demo demo = GsonTool.fromJson(json, Demo.class);
* </pre>
*
* @param json * @param json
* @param classOfT * @param classOfT
* @return * @return
@ -42,12 +53,16 @@ public class GsonTool {
/** /**
* json rawClass<classOfT> Object * json rawClass<classOfT> Object
* *
* <pre>
* Response<Demo> response = GsonTool.fromJson(json, Response.class, Demo.class);
* </pre>
*
* @param json * @param json
* @param classOfT * @param classOfT
* @param argClassOfT * @param argClassOfT
* @return * @return
*/ */
public static <T> T fromJson(String json, Class<T> classOfT, Class argClassOfT) { /*public static <T> T fromJson(String json, Class<T> classOfT, Class argClassOfT) {
Type type = new ParameterizedType4ReturnT(classOfT, new Class[]{argClassOfT}); Type type = new ParameterizedType4ReturnT(classOfT, new Class[]{argClassOfT});
return gson.fromJson(json, type); return gson.fromJson(json, type);
} }
@ -68,21 +83,120 @@ public class GsonTool {
} }
@Override @Override
public Type getOwnerType() {return null;} public Type getOwnerType() {return null;}
}*/
/**
* json Type Object
*
* @param json
* @param typeOfT
* @return
* @param <T>
*/
public static <T> T fromJson(String json, Type typeOfT) {
return gson.fromJson(json, typeOfT);
}
/**
* json Type Object
*
* <pre>
* Response<Demo> response = GsonTool.fromJson(json, Response.class, Demo.class);
* </pre>
*
* @param json
* @param rawType
* @param typeArguments
* @return
*/
public static <T> T fromJson(String json, Type rawType, Type... typeArguments) {
Type type = TypeToken.getParameterized(rawType, typeArguments).getType();
return gson.fromJson(json, type);
}
/**
* json cls ArrayList
*
* <pre>
* List<Demo> demoList = GsonTool.fromJsonList(json, Demo.class);
* </pre>
*
* @param json
* @param classOfT
* @return
*/
public static <T> ArrayList<T> fromJsonList(String json, Class<T> classOfT) {
Type type = TypeToken.getParameterized(ArrayList.class, classOfT).getType();
return gson.fromJson(json, type);
}
/**
* json cls HashMap
*
* <pre>
* HashMap<String, Demo> map = GsonTool.fromJsonMap(json, String.class, Demo.class);
* </pre>
*
* @param json
* @param keyClass
* @param valueClass
* @return
* @param <K>
* @param <V>
*/
public static <K, V> HashMap<K, V> fromJsonMap(String json, Class<K> keyClass, Class<V> valueClass) {
Type type = TypeToken.getParameterized(HashMap.class, keyClass, valueClass).getType();
return gson.fromJson(json, type);
}
// ---------------------------------
/**
* Object JsonElement
*
* @param src
* @return
*/
public static JsonElement toJsonElement(Object src) {
return gson.toJsonTree(src);
} }
/** /**
* json clslist * JsonElement clsObject
* *
* @param json * @param json
* @param classOfT * @param classOfT
* @return * @return
* @param <T>
*/
public static <T> T fromJsonElement(JsonElement json, Class<T> classOfT) {
return gson.fromJson(json, classOfT);
}
/**
* JsonElement rawClass<classOfT> Object
*
* @param json
* @param typeOfT
* @return
* @param <T>
*/
public static <T> T fromJsonElement(JsonElement json, Type typeOfT) {
return gson.fromJson(json, typeOfT);
}
/**
* JsonElement Type Object
*
* @param json
* @param rawType
* @param typeArguments
* @return
* @param <T>
*/ */
public static <T> List<T> fromJsonList(String json, Class<T> classOfT) { public static <T> T fromJsonElement(JsonElement json, Type rawType, Type... typeArguments) {
return gson.fromJson( Type typeOfT = TypeToken.getParameterized(rawType, typeArguments).getType();
json, return gson.fromJson(json, typeOfT);
new TypeToken<List<T>>() {
}.getType()
);
} }
} }

Loading…
Cancel
Save