Просмотр исходного кода

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

xuxueli 2 месяцев назад
Родитель
Сommit
cb1bd548a6
35 измененных файлов с 601 добавлено и 513 удалено
  1. 13 14
      doc/XXL-JOB官方文档.md
  2. 0 29
      xxl-job-admin/src/main/java/com/xxl/job/admin/annotation/PermissionLimit.java
  3. 7 0
      xxl-job-admin/src/main/java/com/xxl/job/admin/constant/Consts.java
  4. 3 3
      xxl-job-admin/src/main/java/com/xxl/job/admin/controller/biz/JobApiController.java
  5. 7 8
      xxl-job-admin/src/main/java/com/xxl/job/admin/controller/biz/JobCodeController.java
  6. 8 7
      xxl-job-admin/src/main/java/com/xxl/job/admin/controller/biz/JobGroupController.java
  7. 69 18
      xxl-job-admin/src/main/java/com/xxl/job/admin/controller/biz/JobInfoController.java
  8. 14 15
      xxl-job-admin/src/main/java/com/xxl/job/admin/controller/biz/JobLogController.java
  9. 16 13
      xxl-job-admin/src/main/java/com/xxl/job/admin/controller/biz/JobUserController.java
  10. 54 20
      xxl-job-admin/src/main/java/com/xxl/job/admin/controller/login/LoginController.java
  11. 5 0
      xxl-job-admin/src/main/java/com/xxl/job/admin/mapper/XxlJobUserMapper.java
  12. 10 1
      xxl-job-admin/src/main/java/com/xxl/job/admin/model/XxlJobUser.java
  13. 5 4
      xxl-job-admin/src/main/java/com/xxl/job/admin/service/XxlJobService.java
  14. 0 111
      xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/LoginService.java
  15. 15 21
      xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/XxlJobServiceImpl.java
  16. 2 1
      xxl-job-admin/src/main/java/com/xxl/job/admin/util/I18nUtil.java
  17. 1 1
      xxl-job-admin/src/main/java/com/xxl/job/admin/util/old/JacksonUtil.java
  18. 2 1
      xxl-job-admin/src/main/java/com/xxl/job/admin/web/interceptor/CommonDataInterceptor.java
  19. 0 131
      xxl-job-admin/src/main/java/com/xxl/job/admin/web/xxlsso/PermissionInterceptor.java
  20. 84 0
      xxl-job-admin/src/main/java/com/xxl/job/admin/web/xxlsso/SimpleLoginStore.java
  21. 0 25
      xxl-job-admin/src/main/java/com/xxl/job/admin/web/xxlsso/WebMvcConfig.java
  22. 62 0
      xxl-job-admin/src/main/java/com/xxl/job/admin/web/xxlsso/XxlSsoConfig.java
  23. 7 1
      xxl-job-admin/src/main/resources/application.properties
  24. 0 0
      xxl-job-admin/src/main/resources/mapper/XxlJobGroupMapper.xml
  25. 0 0
      xxl-job-admin/src/main/resources/mapper/XxlJobInfoMapper.xml
  26. 70 70
      xxl-job-admin/src/main/resources/mapper/XxlJobLogGlueMapper.xml
  27. 0 0
      xxl-job-admin/src/main/resources/mapper/XxlJobLogMapper.xml
  28. 0 0
      xxl-job-admin/src/main/resources/mapper/XxlJobLogReportMapper.xml
  29. 0 0
      xxl-job-admin/src/main/resources/mapper/XxlJobRegistryMapper.xml
  30. 14 0
      xxl-job-admin/src/main/resources/mapper/XxlJobUserMapper.xml
  31. 1 1
      xxl-job-admin/src/main/resources/static/js/login.1.js
  32. 3 3
      xxl-job-admin/src/main/resources/templates/common/common.macro.ftl
  33. 1 1
      xxl-job-admin/src/main/resources/templates/joblog/joblog.index.ftl
  34. 3 3
      xxl-job-admin/src/test/java/com/xxl/job/admin/controller/JobInfoControllerTest.java
  35. 125 11
      xxl-job-core/src/main/java/com/xxl/job/core/util/GsonTool.java

+ 13 - 14
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[规划中]

+ 0 - 29
xxl-job-admin/src/main/java/com/xxl/job/admin/annotation/PermissionLimit.java

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

+ 7 - 0
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";
+
+}

+ 3 - 3
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<String> api(HttpServletRequest request, @PathVariable("uri") String uri, @RequestBody(required = false) String data) {
 
         // valid

+ 7 - 8
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<String>(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);

+ 8 - 7
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<String, Object> 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<String> save(XxlJobGroup xxlJobGroup){
 
 		// valid
@@ -107,7 +108,7 @@ public class JobGroupController {
 
 	@RequestMapping("/update")
 	@ResponseBody
-	@PermissionLimit(adminuser = true)
+	@XxlSso(role = Consts.ADMIN_ROLE)
 	public ReturnT<String> 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<String> 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<XxlJobGroup> loadById(@RequestParam("id") int id){
 		XxlJobGroup jobGroup = xxlJobGroupMapper.load(id);
 		return jobGroup!=null?ReturnT.ofSuccess(jobGroup):ReturnT.ofFail();

+ 69 - 18
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<XxlJobGroup> jobGroupList_all =  xxlJobGroupMapper.findAll();
+		List<XxlJobGroup> jobGroupListTotal =  xxlJobGroupMapper.findAll();
 
 		// filter group
-		List<XxlJobGroup> jobGroupList = PermissionInterceptor.filterJobGroupByRole(request, jobGroupList_all);
-		if (jobGroupList==null || jobGroupList.size()==0) {
+		List<XxlJobGroup> 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<String> 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<String> 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<LoginInfo> 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<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();
+		}
+	}
 	
 }

+ 14 - 15
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<XxlJobGroup> jobGroupList_all =  xxlJobGroupMapper.findAll();
+		List<XxlJobGroup> jobGroupListTotal =  xxlJobGroupMapper.findAll();
 
 		// filter group
-		List<XxlJobGroup> jobGroupList = PermissionInterceptor.filterJobGroupByRole(request, jobGroupList_all);
-		if (jobGroupList==null || jobGroupList.size()==0) {
+		List<XxlJobGroup> 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;

+ 16 - 13
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<String, Object> 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<String> 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<String> update(HttpServletRequest request, XxlJobUser xxlJobUser) {
 
         // avoid opt login seft
-        XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request);
-        if (loginUser.getUsername().equals(xxlJobUser.getUsername())) {
+        Response<LoginInfo> 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<String> remove(HttpServletRequest request, @RequestParam("id") int id) {
 
         // avoid opt login seft
-        XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request);
-        if (loginUser.getId() == id) {
+        Response<LoginInfo> 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<LoginInfo> 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"));
         }

+ 54 - 20
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<LoginInfo> 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<String> 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<String> 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<String> 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<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());
 	}
 
 }

+ 5 - 0
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);
+
 }

+ 10 - 1
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;
 	}

+ 5 - 4
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<String> add(XxlJobInfo jobInfo, XxlJobUser loginUser);
+	public ReturnT<String> add(XxlJobInfo jobInfo, LoginInfo loginInfo);
 
 	/**
 	 * update job
@@ -42,7 +43,7 @@ public interface XxlJobService {
 	 * @param jobInfo
 	 * @return
 	 */
-	public ReturnT<String> update(XxlJobInfo jobInfo, XxlJobUser loginUser);
+	public ReturnT<String> 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<String> trigger(XxlJobUser loginUser, int jobId, String executorParam, String addressList);
+	public ReturnT<String> trigger(LoginInfo loginInfo, int jobId, String executorParam, String addressList);
 
 	/**
 	 * dashboard info

+ 0 - 111
xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/LoginService.java

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

+ 15 - 21
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<String> add(XxlJobInfo jobInfo, XxlJobUser loginUser) {
+	public ReturnT<String> 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<String>(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<String>(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<String> update(XxlJobInfo jobInfo, XxlJobUser loginUser) {
+	public ReturnT<String> 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<String>(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<String>(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<String> trigger(XxlJobUser loginUser, int jobId, String executorParam, String addressList) {
+	public ReturnT<String> 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<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
 	public Map<String, Object> dashboardInfo() {
 

+ 2 - 1
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);
     }
 
+
 }

+ 1 - 1
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;

+ 2 - 1
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

+ 0 - 131
xxl-job-admin/src/main/java/com/xxl/job/admin/web/xxlsso/PermissionInterceptor.java

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

+ 84 - 0
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<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);
+    }
+
+}

+ 0 - 25
xxl-job-admin/src/main/java/com/xxl/job/admin/web/xxlsso/WebMvcConfig.java

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

+ 62 - 0
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("/**");
+    }
+
+}

+ 7 - 1
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

+ 0 - 0
xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobGroupMapper.xml → xxl-job-admin/src/main/resources/mapper/XxlJobGroupMapper.xml


+ 0 - 0
xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobInfoMapper.xml → xxl-job-admin/src/main/resources/mapper/XxlJobInfoMapper.xml


+ 70 - 70
xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobLogGlueMapper.xml → xxl-job-admin/src/main/resources/mapper/XxlJobLogGlueMapper.xml

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

+ 0 - 0
xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobLogMapper.xml → xxl-job-admin/src/main/resources/mapper/XxlJobLogMapper.xml


+ 0 - 0
xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobLogReportMapper.xml → xxl-job-admin/src/main/resources/mapper/XxlJobLogReportMapper.xml


+ 0 - 0
xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobRegistryMapper.xml → xxl-job-admin/src/main/resources/mapper/XxlJobRegistryMapper.xml


+ 14 - 0
xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobUserMapper.xml → xxl-job-admin/src/main/resources/mapper/XxlJobUserMapper.xml

@@ -7,6 +7,7 @@
 		<result column="id" property="id" />
 		<result column="username" property="username" />
 	    <result column="password" property="password" />
+		<result column="token" property="token" />
 	    <result column="role" property="role" />
 	    <result column="permission" property="permission" />
 	</resultMap>
@@ -15,6 +16,7 @@
 		t.id,
 		t.username,
 		t.password,
+		t.token,
 		t.role,
 		t.permission
 	</sql>
@@ -53,6 +55,12 @@
 		WHERE t.username = #{username}
 	</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 INTO xxl_job_user (
 			username,
@@ -84,4 +92,10 @@
 		WHERE id = #{id}
 	</delete>
 
+	<update id="updateToken" parameterType="java.util.HashMap" >
+		UPDATE xxl_job_user
+		SET token = #{token}
+		WHERE id = #{id}
+	</update>
+
 </mapper>

+ 1 - 1
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(){

+ 3 - 3
xxl-job-admin/src/main/resources/templates/common/common.macro.ftl

@@ -83,8 +83,8 @@
 				<ul class="nav navbar-nav">
 					<#-- login user -->
                     <li class="dropdown">
-                        <a href="javascript:" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
-                            ${I18n.system_welcome} ${loginUser.username!}
+                        <a href="javascript:" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false" style="font-weight: bold;">
+                            ${I18n.system_welcome} ${xxl_sso_user.userName!}
                             <span class="caret"></span>
                         </a>
                         <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 == "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>
-				<#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 == "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>

+ 1 - 1
xxl-job-admin/src/main/resources/templates/joblog/joblog.index.ftl

@@ -30,7 +30,7 @@
  					<div class="input-group">
 	                	<span class="input-group-addon">${I18n.jobinfo_field_jobgroup}</span>
                 		<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 -->
                             </#if>
                 			<#list JobGroupList as group>

+ 3 - 3
xxl-job-admin/src/test/java/com/xxl/job/admin/controller/JobInfoControllerTest.java

@@ -1,6 +1,6 @@
 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 org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -21,12 +21,12 @@ public class JobInfoControllerTest extends AbstractSpringMvcTest {
   @BeforeEach
   public void login() throws Exception {
     MvcResult ret = mockMvc.perform(
-        post("/auth/login")
+        post("/auth/doLogin")
             .contentType(MediaType.APPLICATION_FORM_URLENCODED)
             .param("userName", "admin")
             .param("password", "123456")
     ).andReturn();
-    cookie = ret.getResponse().getCookie(LoginService.LOGIN_IDENTITY_KEY);
+    cookie = ret.getResponse().getCookie(Const.XXL_SSO_TOKEN);
   }
 
   @Test

+ 125 - 11
xxl-job-core/src/main/java/com/xxl/job/core/util/GsonTool.java

@@ -2,25 +2,32 @@ package com.xxl.job.core.util;
 
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
+import com.google.gson.JsonElement;
 import com.google.gson.reflect.TypeToken;
 
-import java.lang.reflect.ParameterizedType;
 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
  */
 public class GsonTool {
 
     private static Gson gson = null;
     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
      *
+     * <pre>
+     *     String json = GsonTool.toJson(new Demo());
+     * </pre>
+     *
      * @param src
      * @return String
      */
@@ -31,6 +38,10 @@ public class GsonTool {
     /**
      * json 转成 特定的cls的Object
      *
+     * <pre>
+     *     Demo demo = GsonTool.fromJson(json, Demo.class);
+     * </pre>
+     *
      * @param json
      * @param classOfT
      * @return
@@ -42,12 +53,16 @@ public class GsonTool {
     /**
      * json 转成 特定的 rawClass<classOfT> 的Object
      *
+     * <pre>
+     *     Response<Demo> response = GsonTool.fromJson(json, Response.class, Demo.class);
+     * </pre>
+     *
      * @param json
      * @param classOfT
      * @param argClassOfT
      * @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});
         return gson.fromJson(json, type);
     }
@@ -68,21 +83,120 @@ public class GsonTool {
         }
         @Override
         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 转成 特定的cls的list
+     * JsonElement 转成 特定的cls的Object
      *
      * @param json
      * @param classOfT
      * @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) {
-        return gson.fromJson(
-                json,
-                new TypeToken<List<T>>() {
-                }.getType()
-        );
+    public static <T> T fromJsonElement(JsonElement json, Type rawType, Type... typeArguments) {
+        Type typeOfT = TypeToken.getParameterized(rawType, typeArguments).getType();
+        return gson.fromJson(json, typeOfT);
     }
 
 }