additionalParameter = new HashMap<>();
+ additionalParameter.put("username", username);
+
+ RegisteredClientRepository registeredClientRepository = SpringContextUtils.getBean(RegisteredClientRepository.class);
+ SelfAuthenticationProvider selfAuthenticationProvider = SpringContextUtils.getBean(SelfAuthenticationProvider.class);
+
+ OAuth2ClientAuthenticationToken client = new OAuth2ClientAuthenticationToken(Objects.requireNonNull(registeredClientRepository.findByClientId("jeecg-client")), ClientAuthenticationMethod.CLIENT_SECRET_BASIC, null);
+ client.setAuthenticated(true);
+ SelfAuthenticationToken selfAuthenticationToken = new SelfAuthenticationToken(client, additionalParameter);
+ selfAuthenticationToken.setAuthenticated(true);
+ OAuth2AccessTokenAuthenticationToken accessToken = (OAuth2AccessTokenAuthenticationToken) selfAuthenticationProvider.authenticate(selfAuthenticationToken);
+ return accessToken.getAccessToken().getTokenValue();
}
@@ -180,7 +209,7 @@ public class JwtUtil {
//2.通过shiro获取登录用户信息
LoginUser sysUser = null;
try {
- sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+ sysUser = SecureUtil.currentUser();
} catch (Exception e) {
log.warn("SecurityUtils.getSubject() 获取用户信息异常:" + e.getMessage());
}
diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/LoginUser.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/LoginUser.java
index 32d4b493a139676c5cb09c22130ab6b725e38fe7..963ec05bce5b98bfc22ff1074cd2d79384972833 100644
--- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/LoginUser.java
+++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/LoginUser.java
@@ -1,13 +1,18 @@
package org.jeecg.common.system.vo;
+import com.alibaba.fastjson2.JSON;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.jeecg.common.desensitization.annotation.SensitiveField;
import org.springframework.format.annotation.DateTimeFormat;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+import java.io.Serializable;
import java.util.Date;
+import java.util.Set;
/**
*
@@ -20,8 +25,10 @@ import java.util.Date;
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
-public class LoginUser {
+public class LoginUser implements Serializable {
+
+ private static final long serialVersionUID = -7143159031677245866L;
/**
* 登录人id
*/
@@ -138,4 +145,29 @@ public class LoginUser {
/**设备id uniapp推送用*/
private String clientId;
+ @SensitiveField
+ private String salt;
+
+ @Override
+ public String toString() {
+ // 重新构建对象过滤一些敏感字段
+ LoginUser loginUser = new LoginUser();
+ loginUser.setId(id);
+ loginUser.setUsername(username);
+ loginUser.setRealname(realname);
+ loginUser.setOrgCode(orgCode);
+ loginUser.setSex(sex);
+ loginUser.setEmail(email);
+ loginUser.setPhone(phone);
+ loginUser.setDelFlag(delFlag);
+ loginUser.setStatus(status);
+ loginUser.setActivitiSync(activitiSync);
+ loginUser.setUserIdentity(userIdentity);
+ loginUser.setDepartIds(departIds);
+ loginUser.setPost(post);
+ loginUser.setTelephone(telephone);
+ loginUser.setRelTenantIds(relTenantIds);
+ loginUser.setClientId(clientId);
+ return JSON.toJSONString(loginUser);
+ }
}
diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/BrowserUtils.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/BrowserUtils.java
index 08b3fbcb128809b227350abe0a281ad1859c46eb..479a9174b84c2b0a0d4ffd177fd051429bf65e57 100644
--- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/BrowserUtils.java
+++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/BrowserUtils.java
@@ -5,7 +5,7 @@ import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import javax.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequest;
/**
*
diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/CommonUtils.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/CommonUtils.java
index 9294a5c20a446887663d5d862ba5638724db545e..b7e790f489482c93185660765863bf692202e060 100644
--- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/CommonUtils.java
+++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/CommonUtils.java
@@ -19,7 +19,7 @@ import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;
-import javax.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequest;
import javax.sql.DataSource;
import java.io.ByteArrayInputStream;
import java.io.File;
diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/FileDownloadUtils.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/FileDownloadUtils.java
index d9d6ff86b2816454290472fec4fa83d4a81416ad..acdfeb40bfaa2e60739e64793d83aa1f16af73e1 100644
--- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/FileDownloadUtils.java
+++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/FileDownloadUtils.java
@@ -1,10 +1,10 @@
package org.jeecg.common.util;
+import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.jeecg.common.exception.JeecgBootException;
-import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/FillRuleUtil.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/FillRuleUtil.java
index d58f8fa3bda5a8f3c5ee6706f373052f0f4f454b..cac263abf001ddbf237220a46c9457d7f5812706 100644
--- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/FillRuleUtil.java
+++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/FillRuleUtil.java
@@ -4,13 +4,13 @@ import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.constant.SymbolConstant;
import org.jeecg.common.handler.IFillRuleHandler;
import org.jeecg.common.system.query.QueryGenerator;
-import javax.servlet.http.HttpServletRequest;
/**
diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/IpUtils.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/IpUtils.java
index 22a9395b47e6faa3d346b8171118f933f4bbe952..7add5d2f3daeb929eb2aef9e4c9632f83040793b 100644
--- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/IpUtils.java
+++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/IpUtils.java
@@ -1,6 +1,6 @@
package org.jeecg.common.util;
-import javax.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.constant.CommonConstant;
diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/SpringContextUtils.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/SpringContextUtils.java
index bd46b7d82935181acee0e14f673096a47dabcc28..8a6a4aa4417dee468c211c461d9a2b3aec6ba1b9 100644
--- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/SpringContextUtils.java
+++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/SpringContextUtils.java
@@ -1,7 +1,7 @@
package org.jeecg.common.util;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.ServiceNameConstants;
diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/TokenUtils.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/TokenUtils.java
index 83efa1914b47af250aeac49496ffa8b04a7ab7a1..95ddab7af72cff895acac498c8fbc68490871f85 100644
--- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/TokenUtils.java
+++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/TokenUtils.java
@@ -1,5 +1,6 @@
package org.jeecg.common.util;
+import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.api.CommonAPI;
@@ -11,8 +12,6 @@ import org.jeecg.common.exception.JeecgBoot401Exception;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.LoginUser;
-import javax.servlet.http.HttpServletRequest;
-
/**
* @Author scott
* @Date 2019/9/23 14:12
@@ -106,8 +105,8 @@ public class TokenUtils {
}
// 查询用户信息
- LoginUser user = TokenUtils.getLoginUser(username, commonApi, redisUtil);
- //LoginUser user = commonApi.getUserByName(username);
+ //LoginUser user = TokenUtils.getLoginUser(username, commonApi, redisUtil);
+ LoginUser user = commonApi.getUserByName(username);
if (user == null) {
throw new JeecgBoot401Exception("用户不存在!");
}
@@ -158,10 +157,11 @@ public class TokenUtils {
//【重要】此处通过redis原生获取缓存用户,是为了解决微服务下system服务挂了,其他服务互调不通问题---
if (redisUtil.hasKey(loginUserKey)) {
try {
- loginUser = (LoginUser) redisUtil.get(loginUserKey);
+ Object obj = redisUtil.get(loginUserKey);
+ loginUser = (LoginUser) obj;
//解密用户
SensitiveInfoUtil.handlerObject(loginUser, false);
- } catch (IllegalAccessException e) {
+ } catch (Exception e) {
e.printStackTrace();
}
} else {
diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/encryption/AesEncryptUtil.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/encryption/AesEncryptUtil.java
index 6a4db42155d34b02f094d30486bca2e97ff56cd9..e75092cca2ff29129b9ddb3a31d7beb7cc93e55e 100644
--- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/encryption/AesEncryptUtil.java
+++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/encryption/AesEncryptUtil.java
@@ -1,10 +1,9 @@
package org.jeecg.common.util.encryption;
-import org.apache.shiro.lang.codec.Base64;
-
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
+import java.util.Base64;
/**
* @Description: AES 加密
@@ -49,7 +48,7 @@ public class AesEncryptUtil {
cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
byte[] encrypted = cipher.doFinal(plaintext);
- return Base64.encodeToString(encrypted);
+ return Base64.getEncoder().encodeToString(encrypted);
} catch (Exception e) {
e.printStackTrace();
@@ -67,7 +66,7 @@ public class AesEncryptUtil {
*/
public static String desEncrypt(String data, String key, String iv) throws Exception {
//update-begin-author:taoyan date:2022-5-23 for:VUEN-1084 【vue3】online表单测试发现的新问题 6、解密报错 ---解码失败应该把异常抛出去,在外面处理
- byte[] encrypted1 = Base64.decode(data);
+ byte[] encrypted1 = Base64.getDecoder().decode(data);
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/oConvertUtils.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/oConvertUtils.java
index 11f16b7be75f42c7e1cabdcd9329829ef0dddba2..54c48061bf5782e6b88bc13e329e32ba951bf335 100644
--- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/oConvertUtils.java
+++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/oConvertUtils.java
@@ -9,7 +9,7 @@ import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.SymbolConstant;
import org.springframework.beans.BeanUtils;
-import javax.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/AutoPoiDictConfig.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/AutoPoiDictConfig.java
index 93f571c68c64977f357766a549fc118ab65093f5..a21ab3540a9747f9622fb48c35fdfe0617a7b265 100644
--- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/AutoPoiDictConfig.java
+++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/AutoPoiDictConfig.java
@@ -3,7 +3,7 @@ package org.jeecg.config;
import java.util.ArrayList;
import java.util.List;
-import javax.annotation.Resource;
+import jakarta.annotation.Resource;
import org.jeecg.common.api.CommonAPI;
import org.jeecg.common.system.vo.DictModel;
diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/DruidConfig.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/DruidConfig.java
index 7659172bacb16a1fc1ffb8a1182739cf19371d16..84b33f5a5f2914f34de261d7ea2d85a583bc1ca2 100644
--- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/DruidConfig.java
+++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/DruidConfig.java
@@ -2,7 +2,9 @@ package org.jeecg.config;
import java.io.IOException;
-import javax.servlet.*;
+import com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceAutoConfigure;
+import com.alibaba.druid.spring.boot3.autoconfigure.properties.DruidStatProperties;
+import jakarta.servlet.*;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -11,8 +13,6 @@ import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
-import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
import com.alibaba.druid.util.Utils;
/**
diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/DruidWallConfigRegister.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/DruidWallConfigRegister.java
index 6c3c1a1d933aeb7072854ca5c8ce27bccd6d1763..5dd2d9d4f37913360bac33565492dee17a766f62 100644
--- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/DruidWallConfigRegister.java
+++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/DruidWallConfigRegister.java
@@ -12,6 +12,7 @@ import java.util.HashMap;
import java.util.Map;
/**
+ * @author eightmonth@qq.com
* 启动程序修改DruidWallConfig配置
* 允许SELECT语句的WHERE子句是一个永真条件
* @author eightmonth
diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/JeecgBaseConfig.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/JeecgBaseConfig.java
index 0aac0e35c53bae3acdbac8968bf0787edd969844..8dd0093a88d3d76c4aec52c9150a241891e8c7e5 100644
--- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/JeecgBaseConfig.java
+++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/JeecgBaseConfig.java
@@ -1,7 +1,10 @@
package org.jeecg.config;
import org.jeecg.config.vo.*;
+import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Role;
import org.springframework.stereotype.Component;
@@ -11,6 +14,7 @@ import org.springframework.stereotype.Component;
*/
@Component("jeecgBaseConfig")
@ConfigurationProperties(prefix = "jeecg")
+@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class JeecgBaseConfig {
/**
* 签名密钥串(字典等敏感接口)
@@ -36,10 +40,6 @@ public class JeecgBaseConfig {
*/
private Firewall firewall;
- /**
- * shiro拦截排除
- */
- private Shiro shiro;
/**
* 上传文件配置
*/
@@ -104,14 +104,6 @@ public class JeecgBaseConfig {
this.signatureSecret = signatureSecret;
}
- public Shiro getShiro() {
- return shiro;
- }
-
- public void setShiro(Shiro shiro) {
- this.shiro = shiro;
- }
-
public Path getPath() {
return path;
}
diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/UndertowCustomizer.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/UndertowCustomizer.java
new file mode 100644
index 0000000000000000000000000000000000000000..afc484b98d323b23149031ec7d56a8266e0ac5d1
--- /dev/null
+++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/UndertowCustomizer.java
@@ -0,0 +1,19 @@
+package org.jeecg.config;
+
+import io.undertow.server.DefaultByteBufferPool;
+import io.undertow.websockets.jsr.WebSocketDeploymentInfo;
+import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
+import org.springframework.boot.web.server.WebServerFactoryCustomizer;
+import org.springframework.stereotype.Component;
+
+@Component
+public class UndertowCustomizer implements WebServerFactoryCustomizer {
+ @Override
+ public void customize(UndertowServletWebServerFactory factory) {
+ factory.addDeploymentInfoCustomizers(deploymentInfo -> {
+ WebSocketDeploymentInfo webSocketDeploymentInfo = new WebSocketDeploymentInfo();
+ webSocketDeploymentInfo.setBuffers(new DefaultByteBufferPool(false, 1024));
+ deploymentInfo.addServletContextAttribute("io.undertow.websockets.jsr.WebSocketDeploymentInfo", webSocketDeploymentInfo);
+ });
+ }
+}
diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/WebMvcConfiguration.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/WebMvcConfiguration.java
index 2515f5d030a3e43ddd4498ab98f2606226337ecb..a5d45ab19086c3905c6ccabd6f9d3f756590dbd5 100644
--- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/WebMvcConfiguration.java
+++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/WebMvcConfiguration.java
@@ -10,20 +10,23 @@ import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
-import io.micrometer.prometheus.PrometheusMeterRegistry;
+import io.micrometer.prometheusmetrics.PrometheusMeterRegistry;
+import jakarta.annotation.Resource;
import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanPostProcessor;
-import org.springframework.boot.actuate.trace.http.InMemoryHttpTraceRepository;
+import org.springframework.boot.actuate.web.exchanges.InMemoryHttpExchangeRepository;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.jackson.JacksonProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.CacheControl;
import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
@@ -33,7 +36,6 @@ import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
-import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/filter/RequestBodyReserveFilter.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/filter/RequestBodyReserveFilter.java
index fd4f07a943446b142b4c3ef2e22feaa528d7ed52..050c174395aeef3f992ef072c946aa30a06492fd 100644
--- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/filter/RequestBodyReserveFilter.java
+++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/filter/RequestBodyReserveFilter.java
@@ -3,8 +3,8 @@ package org.jeecg.config.filter;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.config.sign.util.BodyReaderHttpServletRequestWrapper;
-import javax.servlet.*;
-import javax.servlet.http.HttpServletRequest;
+import jakarta.servlet.*;
+import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/filter/WebsocketFilter.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/filter/WebsocketFilter.java
index 55417e153b72fb16b9aedbe83d98833f4830c4fb..b9a788e4c0a718c83914bfd4a89f234b1f23679c 100644
--- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/filter/WebsocketFilter.java
+++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/filter/WebsocketFilter.java
@@ -7,9 +7,9 @@ import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.TokenUtils;
import org.jeecg.common.util.oConvertUtils;
-import javax.servlet.*;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.*;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/firewall/interceptor/LowCodeModeInterceptor.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/firewall/interceptor/LowCodeModeInterceptor.java
index 9bdcbd6439ebe85e982ed51c4be5694f9a7a5b7b..d5f5bb2661ea21a221a551c4484a223b9e25fabe 100644
--- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/firewall/interceptor/LowCodeModeInterceptor.java
+++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/firewall/interceptor/LowCodeModeInterceptor.java
@@ -2,7 +2,6 @@ package org.jeecg.config.firewall.interceptor;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
-import org.apache.shiro.SecurityUtils;
import org.jeecg.common.api.CommonAPI;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
@@ -11,12 +10,13 @@ import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.CommonUtils;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.config.JeecgBaseConfig;
+import org.jeecg.config.security.utils.SecureUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.HandlerInterceptor;
-import javax.annotation.Resource;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.annotation.Resource;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Set;
@@ -63,7 +63,7 @@ public class LowCodeModeInterceptor implements HandlerInterceptor {
if (jeecgBaseConfig.getFirewall()!=null && LowCodeModeInterceptor.LOW_CODE_MODE_PROD.equals(jeecgBaseConfig.getFirewall().getLowCodeMode())) {
String requestURI = request.getRequestURI().substring(request.getContextPath().length());
log.info("低代码模式,拦截请求路径:" + requestURI);
- LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+ LoginUser loginUser = SecureUtil.currentUser();
Set hasRoles = null;
if (loginUser == null) {
loginUser = commonAPI.getUserByName(JwtUtil.getUserNameByToken(SpringContextUtils.getHttpServletRequest()));
diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/MybatisInterceptor.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/MybatisInterceptor.java
index aef226679cd7dd83b0bdd86c6e7dbdc727375c36..5745e1086019d41fdaaecdc752b4f1bcd70ed7f1 100644
--- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/MybatisInterceptor.java
+++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/MybatisInterceptor.java
@@ -6,13 +6,13 @@ import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
-import org.apache.shiro.SecurityUtils;
import org.jeecg.common.config.TenantContext;
import org.jeecg.common.constant.TenantConstant;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.TokenUtils;
import org.jeecg.common.util.oConvertUtils;
+import org.jeecg.config.security.utils.SecureUtil;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
@@ -192,7 +192,7 @@ public class MybatisInterceptor implements Interceptor {
private LoginUser getLoginUser() {
LoginUser sysUser = null;
try {
- sysUser = SecurityUtils.getSubject().getPrincipal() != null ? (LoginUser) SecurityUtils.getSubject().getPrincipal() : null;
+ sysUser = SecureUtil.currentUser() != null ? SecureUtil.currentUser() : null;
} catch (Exception e) {
//e.printStackTrace();
sysUser = null;
diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/MybatisPlusSaasConfig.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/MybatisPlusSaasConfig.java
index 2821ac6b1b90e88d2ac6c468fca0cc3efc50b789..fe9099ea840133c8a64e42561bed995b986136a4 100644
--- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/MybatisPlusSaasConfig.java
+++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/MybatisPlusSaasConfig.java
@@ -6,6 +6,8 @@ import java.util.List;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.plugins.inner.DynamicTableNameInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
+import net.sf.jsqlparser.expression.Expression;
+import net.sf.jsqlparser.expression.LongValue;
import org.jeecg.common.config.TenantContext;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.TenantConstant;
@@ -21,8 +23,6 @@ import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
-import net.sf.jsqlparser.expression.Expression;
-import net.sf.jsqlparser.expression.LongValue;
/**
* 单数据源配置(jeecg.datasource.open = false时生效)
diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/aspect/DynamicTableAspect.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/aspect/DynamicTableAspect.java
index b0d67cbcb6f0280f4702c528bdd4d150e1051c3e..df4dc302d6c84ec25c20e1679a4dffe0114696bb 100644
--- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/aspect/DynamicTableAspect.java
+++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/aspect/DynamicTableAspect.java
@@ -11,7 +11,7 @@ import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.config.mybatis.ThreadLocalDataHelper;
import org.springframework.stereotype.Component;
-import javax.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
/**
diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/interceptor/DynamicDatasourceInterceptor.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/interceptor/DynamicDatasourceInterceptor.java
index 3461cbd7e7cf68e36bed7fa94b26102b303bbf15..9dfe334291efa81c754bc969eba02939a952458d 100644
--- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/interceptor/DynamicDatasourceInterceptor.java
+++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/interceptor/DynamicDatasourceInterceptor.java
@@ -6,8 +6,8 @@ import org.apache.commons.lang3.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
/**
* 动态数据源切换拦截器
diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/oss/MinioConfig.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/oss/MinioConfig.java
index 3a19ab480d3d4478c29cbbcdbe87cccf0b3110a7..42aff79b3661a4172e18581c83f061288c82f96d 100644
--- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/oss/MinioConfig.java
+++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/oss/MinioConfig.java
@@ -1,5 +1,6 @@
package org.jeecg.config.oss;
+import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.SymbolConstant;
@@ -26,7 +27,7 @@ public class MinioConfig {
@Value(value = "${jeecg.minio.bucketName}")
private String bucketName;
- @Bean
+ @PostConstruct
public void initMinio(){
if(!minioUrl.startsWith(CommonConstant.STR_HTTP)){
minioUrl = "http://" + minioUrl;
diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/oss/OssConfiguration.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/oss/OssConfiguration.java
index 0734b95b9ba4b2bd4908f083414a76a0c2c3ec38..b7dd763ad7af2085913cca0eaf1d34db12cff5fb 100644
--- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/oss/OssConfiguration.java
+++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/oss/OssConfiguration.java
@@ -1,5 +1,6 @@
package org.jeecg.config.oss;
+import jakarta.annotation.PostConstruct;
import org.jeecg.common.util.oss.OssBootUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -26,7 +27,7 @@ public class OssConfiguration {
private String staticDomain;
- @Bean
+ @PostConstruct
public void initOssBootConfiguration() {
OssBootUtil.setEndPoint(endpoint);
OssBootUtil.setAccessKeyId(accessKeyId);
diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/ClientService.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/ClientService.java
new file mode 100644
index 0000000000000000000000000000000000000000..c13189985c549de690b01604b4dc5954e0f12882
--- /dev/null
+++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/ClientService.java
@@ -0,0 +1,90 @@
+package org.jeecg.config.security;
+
+import lombok.AllArgsConstructor;
+import org.springframework.security.oauth2.core.AuthorizationGrantType;
+import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
+import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
+import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
+import org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;
+import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;
+import org.springframework.stereotype.Component;
+
+import java.time.Duration;
+import java.util.Set;
+
+/**
+ * spring authorization server 注册客户端便捷工具类
+ * @author eightmonth@qq.com
+ * @date 2024/3/7 11:22
+ */
+@Component
+@AllArgsConstructor
+public class ClientService {
+
+ private RegisteredClientRepository registeredClientRepository;
+
+ /**
+ * 修改客户端token有效期
+ * 认证码、设备码有效期与accessToken有效期保持一致
+ */
+ public void updateTokenValidation(String clientId, Long accessTokenValidation, Long refreshTokenValidation){
+ RegisteredClient registeredClient = findByClientId(clientId);
+ RegisteredClient.Builder builder = RegisteredClient.from(registeredClient);
+ TokenSettings tokenSettings = TokenSettings.builder()
+ .idTokenSignatureAlgorithm(SignatureAlgorithm.RS256)
+ .accessTokenTimeToLive(Duration.ofSeconds(accessTokenValidation))
+ .accessTokenFormat(OAuth2TokenFormat.SELF_CONTAINED)
+ .reuseRefreshTokens(true)
+ .refreshTokenTimeToLive(Duration.ofSeconds(refreshTokenValidation))
+ .authorizationCodeTimeToLive(Duration.ofSeconds(accessTokenValidation))
+ .deviceCodeTimeToLive(Duration.ofSeconds(accessTokenValidation))
+ .build();
+ builder.tokenSettings(tokenSettings);
+ registeredClientRepository.save(builder.build());
+ }
+
+ /**
+ * 修改客户端授权类型
+ * @param clientId
+ * @param grantTypes
+ */
+ public void updateGrantType(String clientId, Set grantTypes) {
+ RegisteredClient registeredClient = findByClientId(clientId);
+ RegisteredClient.Builder builder = RegisteredClient.from(registeredClient);
+ for (AuthorizationGrantType grantType : grantTypes) {
+ builder.authorizationGrantType(grantType);
+ }
+ registeredClientRepository.save(builder.build());
+ }
+
+ /**
+ * 修改客户端重定向uri
+ * @param clientId
+ * @param redirectUris
+ */
+ public void updateRedirectUris(String clientId, String redirectUris) {
+ RegisteredClient registeredClient = findByClientId(clientId);
+ RegisteredClient.Builder builder = RegisteredClient.from(registeredClient);
+ builder.redirectUri(redirectUris);
+ registeredClientRepository.save(builder.build());
+ }
+
+ /**
+ * 修改客户端授权范围
+ * @param clientId
+ * @param scopes
+ */
+ public void updateScopes(String clientId, Set scopes) {
+ RegisteredClient registeredClient = findByClientId(clientId);
+ RegisteredClient.Builder builder = RegisteredClient.from(registeredClient);
+ for (String scope : scopes) {
+ builder.scope(scope);
+ }
+ registeredClientRepository.save(builder.build());
+ }
+
+ public RegisteredClient findByClientId(String clientId) {
+ return registeredClientRepository.findByClientId(clientId);
+ }
+
+}
diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/CopyTokenFilter.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/CopyTokenFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..de18fbeb09acacb983c09b571770a0e2b196ffa2
--- /dev/null
+++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/CopyTokenFilter.java
@@ -0,0 +1,45 @@
+package org.jeecg.config.security;
+
+import io.undertow.servlet.spec.HttpServletRequestImpl;
+import io.undertow.util.HttpString;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import java.io.IOException;
+
+/**
+ * 复制仪盘表请求query体携带的token
+ * @author eightmonth
+ * @date 2024/7/3 14:04
+ */
+@Component
+@Order(value = Integer.MIN_VALUE)
+public class CopyTokenFilter extends OncePerRequestFilter {
+ @Override
+ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
+ // 以下为undertow定制代码,如切换其它servlet容器,需要同步更换
+ HttpServletRequestImpl undertowRequest = (HttpServletRequestImpl) request;
+ String token = request.getHeader("Authorization");
+ if (StringUtils.hasText(token)) {
+ undertowRequest.getExchange().getRequestHeaders().remove("Authorization");
+ undertowRequest.getExchange().getRequestHeaders().add(new HttpString("Authorization"), "bearer " + token);
+ } else {
+ String bearerToken = request.getParameter("token");
+ String headerBearerToken = request.getHeader("X-Access-Token");
+ if (StringUtils.hasText(bearerToken)) {
+ undertowRequest.getExchange().getRequestHeaders().add(new HttpString("Authorization"), "bearer " + bearerToken);
+ } else if (StringUtils.hasText(headerBearerToken)) {
+ undertowRequest.getExchange().getRequestHeaders().add(new HttpString("Authorization"), "bearer " + headerBearerToken);
+ }
+ }
+ filterChain.doFilter(undertowRequest, response);
+ }
+
+
+}
diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/JeecgAuthenticationConvert.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/JeecgAuthenticationConvert.java
new file mode 100644
index 0000000000000000000000000000000000000000..321f2157892b956b1501b986eb36ad65fda9d727
--- /dev/null
+++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/JeecgAuthenticationConvert.java
@@ -0,0 +1,34 @@
+package org.jeecg.config.security;
+
+import org.jeecg.common.api.CommonAPI;
+import org.jeecg.common.system.vo.LoginUser;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.oauth2.jwt.Jwt;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+
+/**
+ * token只存储用户名与过期时间
+ * 这里通过取用户名转全量用户信息存储到Security中
+ * @author eightmonth@qq.com
+ * @date 2024/7/15 11:05
+ */
+@Component
+public class JeecgAuthenticationConvert implements Converter {
+
+ @Lazy
+ @Autowired
+ private CommonAPI commonAPI;
+
+ @Override
+ public AbstractAuthenticationToken convert(Jwt source) {
+ String username = source.getClaims().get("username").toString();
+ LoginUser loginUser = commonAPI.getUserByName(username);
+ return new UsernamePasswordAuthenticationToken(loginUser, null, new ArrayList<>());
+ }
+}
diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/JeecgOAuth2AccessTokenGenerator.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/JeecgOAuth2AccessTokenGenerator.java
new file mode 100644
index 0000000000000000000000000000000000000000..00960db8fa3c1c584cd62a02cacf2e19e98db495
--- /dev/null
+++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/JeecgOAuth2AccessTokenGenerator.java
@@ -0,0 +1,135 @@
+package org.jeecg.config.security;
+
+import org.jeecg.common.system.util.JwtUtil;
+import org.springframework.lang.Nullable;
+import org.springframework.security.oauth2.core.ClaimAccessor;
+import org.springframework.security.oauth2.core.OAuth2AccessToken;
+import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
+import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
+import org.springframework.security.oauth2.jwt.JwsHeader;
+import org.springframework.security.oauth2.jwt.JwtClaimsSet;
+import org.springframework.security.oauth2.jwt.JwtEncoder;
+import org.springframework.security.oauth2.jwt.JwtEncoderParameters;
+import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
+import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationGrantAuthenticationToken;
+import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
+import org.springframework.security.oauth2.server.authorization.token.*;
+import org.springframework.util.Assert;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.time.temporal.Temporal;
+import java.time.temporal.TemporalUnit;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author eightmonth@qq.com
+ * @date 2024/7/11 17:10
+ */
+public class JeecgOAuth2AccessTokenGenerator implements OAuth2TokenGenerator {
+ private final JwtEncoder jwtEncoder;
+
+ private OAuth2TokenCustomizer accessTokenCustomizer;
+
+ public JeecgOAuth2AccessTokenGenerator(JwtEncoder jwtEncoder) {
+ this.jwtEncoder = jwtEncoder;
+ }
+
+ @Nullable
+ @Override
+ public OAuth2AccessToken generate(OAuth2TokenContext context) {
+ if (!OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType())) {
+ return null;
+ }
+
+ String issuer = null;
+ if (context.getAuthorizationServerContext() != null) {
+ issuer = context.getAuthorizationServerContext().getIssuer();
+ }
+ RegisteredClient registeredClient = context.getRegisteredClient();
+
+ Instant issuedAt = Instant.now();
+ Instant expiresAt = issuedAt.plusMillis(JwtUtil.EXPIRE_TIME);
+
+ OAuth2TokenClaimsSet.Builder claimsBuilder = OAuth2TokenClaimsSet.builder();
+ if (StringUtils.hasText(issuer)) {
+ claimsBuilder.issuer(issuer);
+ }
+ claimsBuilder
+ .subject(context.getPrincipal().getName())
+ .audience(Collections.singletonList(registeredClient.getClientId()))
+ .issuedAt(issuedAt)
+ .expiresAt(expiresAt)
+ .notBefore(issuedAt)
+ .id(UUID.randomUUID().toString());
+ if (!CollectionUtils.isEmpty(context.getAuthorizedScopes())) {
+ claimsBuilder.claim(OAuth2ParameterNames.SCOPE, context.getAuthorizedScopes());
+ }
+
+ if (this.accessTokenCustomizer != null) {
+ OAuth2TokenClaimsContext.Builder accessTokenContextBuilder = OAuth2TokenClaimsContext.with(claimsBuilder)
+ .registeredClient(context.getRegisteredClient())
+ .principal(context.getPrincipal())
+ .authorizationServerContext(context.getAuthorizationServerContext())
+ .authorizedScopes(context.getAuthorizedScopes())
+ .tokenType(context.getTokenType())
+ .authorizationGrantType(context.getAuthorizationGrantType());
+ if (context.getAuthorization() != null) {
+ accessTokenContextBuilder.authorization(context.getAuthorization());
+ }
+ if (context.getAuthorizationGrant() != null) {
+ accessTokenContextBuilder.authorizationGrant(context.getAuthorizationGrant());
+ }
+
+ OAuth2TokenClaimsContext accessTokenContext = accessTokenContextBuilder.build();
+ this.accessTokenCustomizer.customize(accessTokenContext);
+ }
+
+
+ OAuth2TokenClaimsSet accessTokenClaimsSet = claimsBuilder.build();
+ OAuth2AuthorizationGrantAuthenticationToken oAuth2ResourceOwnerBaseAuthenticationToken = context.getAuthorizationGrant();
+ String username = (String) oAuth2ResourceOwnerBaseAuthenticationToken.getAdditionalParameters().get("username");
+ String tokenValue = jwtEncoder.encode(JwtEncoderParameters.from(JwsHeader.with(SignatureAlgorithm.ES256).keyId("jeecg").build(),
+ JwtClaimsSet.builder().claim("username", username).expiresAt(expiresAt).build())).getTokenValue();
+
+ //此处可以做改造将tokenValue随机数换成用户信息,方便后续多系统token互通认证(通过解密token得到username)
+ return new OAuth2AccessTokenClaims(OAuth2AccessToken.TokenType.BEARER, tokenValue,
+ accessTokenClaimsSet.getIssuedAt(), accessTokenClaimsSet.getExpiresAt(), context.getAuthorizedScopes(),
+ accessTokenClaimsSet.getClaims());
+ }
+
+ /**
+ * Sets the {@link OAuth2TokenCustomizer} that customizes the
+ * {@link OAuth2TokenClaimsContext#getClaims() claims} for the
+ * {@link OAuth2AccessToken}.
+ * @param accessTokenCustomizer the {@link OAuth2TokenCustomizer} that customizes the
+ * claims for the {@code OAuth2AccessToken}
+ */
+ public void setAccessTokenCustomizer(OAuth2TokenCustomizer accessTokenCustomizer) {
+ Assert.notNull(accessTokenCustomizer, "accessTokenCustomizer cannot be null");
+ this.accessTokenCustomizer = accessTokenCustomizer;
+ }
+
+ private static final class OAuth2AccessTokenClaims extends OAuth2AccessToken implements ClaimAccessor {
+
+ private final Map claims;
+
+ private OAuth2AccessTokenClaims(TokenType tokenType, String tokenValue, Instant issuedAt, Instant expiresAt,
+ Set scopes, Map claims) {
+ super(tokenType, tokenValue, issuedAt, expiresAt, scopes);
+ this.claims = claims;
+ }
+
+ @Override
+ public Map getClaims() {
+ return this.claims;
+ }
+
+ }
+}
diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/JeecgPermissionService.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/JeecgPermissionService.java
new file mode 100644
index 0000000000000000000000000000000000000000..8beaab02f7fcf430ff746b6acdc57298ef511222
--- /dev/null
+++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/JeecgPermissionService.java
@@ -0,0 +1,104 @@
+package org.jeecg.config.security;
+
+import cn.hutool.core.util.ArrayUtil;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.jeecg.common.api.CommonAPI;
+import org.jeecg.common.system.vo.LoginUser;
+import org.jeecg.common.util.RedisUtil;
+import org.jeecg.config.security.utils.SecureUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Service;
+import org.springframework.util.PatternMatchUtils;
+import org.springframework.util.StringUtils;
+
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * spring authorization server自定义权限处理,根据@PreAuthorize注解,判断当前用户是否具备权限
+ * @author EightMonth
+ * @date 2024/1/10 17:00
+ */
+@Service("jps")
+@Slf4j
+public class JeecgPermissionService {
+ private final String SPLIT = "::";
+ private final String PERM_PREFIX = "jps" + SPLIT;
+
+ @Lazy
+ @Autowired
+ private CommonAPI commonAPI;
+ @Autowired
+ private RedisUtil redisUtil;
+
+ /**
+ * 判断接口是否有任意xxx,xxx权限
+ * @param permissions 权限
+ * @return {boolean}
+ */
+ public boolean requiresPermissions(String... permissions) {
+ if (ArrayUtil.isEmpty(permissions)) {
+ return false;
+ }
+ LoginUser loginUser = SecureUtil.currentUser();
+
+ Object cache = redisUtil.get(buildKey("permission", loginUser.getId()));
+ Set permissionList;
+ if (Objects.nonNull(cache)) {
+ permissionList = (Set) cache;
+ } else {
+ permissionList = commonAPI.queryUserAuths(loginUser.getId());
+ redisUtil.set(buildKey("permission", loginUser.getId()), permissionList);
+ }
+
+ boolean pass = permissionList.stream().filter(StringUtils::hasText)
+ .anyMatch(x -> PatternMatchUtils.simpleMatch(permissions, x));
+ if (!pass) {
+ log.error("权限不足,缺少权限:"+ Arrays.toString(permissions));
+ }
+ return pass;
+ }
+
+ /**
+ * 判断接口是否有任意xxx,xxx角色
+ * @param roles 角色
+ * @return {boolean}
+ */
+ public boolean requiresRoles(String... roles) {
+ if (ArrayUtil.isEmpty(roles)) {
+ return false;
+ }
+ LoginUser loginUser = SecureUtil.currentUser();
+
+ Object cache = redisUtil.get(buildKey("role", loginUser.getUsername()));
+ Set roleList;
+ if (Objects.nonNull(cache)) {
+ roleList = (Set) cache;
+ } else {
+ roleList = commonAPI.queryUserRoles(loginUser.getUsername());
+ redisUtil.set(buildKey("role", loginUser.getUsername()), roleList);
+ }
+
+ boolean pass = roleList.stream().filter(StringUtils::hasText)
+ .anyMatch(x -> PatternMatchUtils.simpleMatch(roles, x));
+ if (!pass) {
+ log.error("权限不足,缺少角色:" + Arrays.toString(roles));
+ }
+ return pass;
+ }
+
+ /**
+ * 由于缓存key是以人的维度,角色列表、权限列表在值中,jeecg是以权限列表绑定在角色上,形成的权限集合
+ * 权限发生变更时,需要清理全部人的权限缓存
+ */
+ public void clearCache() {
+ redisUtil.removeAll(PERM_PREFIX);
+ }
+
+ private String buildKey(String type, String username) {
+ return PERM_PREFIX + type + SPLIT + username;
+ }
+}
diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/JeecgRedisOAuth2AuthorizationConsentService.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/JeecgRedisOAuth2AuthorizationConsentService.java
new file mode 100644
index 0000000000000000000000000000000000000000..5efb246a0e3e7ddcec00b0c88cae2cd360064612
--- /dev/null
+++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/JeecgRedisOAuth2AuthorizationConsentService.java
@@ -0,0 +1,54 @@
+package org.jeecg.config.security;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsent;
+import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;
+import org.springframework.stereotype.Component;
+import org.springframework.util.Assert;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * spring authorization server 自定义redis保存授权范围信息
+ */
+@Component
+@RequiredArgsConstructor
+public class JeecgRedisOAuth2AuthorizationConsentService implements OAuth2AuthorizationConsentService {
+
+ private final RedisTemplate redisTemplate;
+
+ private final static Long TIMEOUT = 10L;
+
+ @Override
+ public void save(OAuth2AuthorizationConsent authorizationConsent) {
+ Assert.notNull(authorizationConsent, "authorizationConsent cannot be null");
+
+ redisTemplate.opsForValue().set(buildKey(authorizationConsent), authorizationConsent, TIMEOUT,
+ TimeUnit.MINUTES);
+
+ }
+
+ @Override
+ public void remove(OAuth2AuthorizationConsent authorizationConsent) {
+ Assert.notNull(authorizationConsent, "authorizationConsent cannot be null");
+ redisTemplate.delete(buildKey(authorizationConsent));
+ }
+
+ @Override
+ public OAuth2AuthorizationConsent findById(String registeredClientId, String principalName) {
+ Assert.hasText(registeredClientId, "registeredClientId cannot be empty");
+ Assert.hasText(principalName, "principalName cannot be empty");
+ return (OAuth2AuthorizationConsent) redisTemplate.opsForValue()
+ .get(buildKey(registeredClientId, principalName));
+ }
+
+ private static String buildKey(String registeredClientId, String principalName) {
+ return "token:consent:" + registeredClientId + ":" + principalName;
+ }
+
+ private static String buildKey(OAuth2AuthorizationConsent authorizationConsent) {
+ return buildKey(authorizationConsent.getRegisteredClientId(), authorizationConsent.getPrincipalName());
+ }
+
+}
diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/JeecgRedisOAuth2AuthorizationService.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/JeecgRedisOAuth2AuthorizationService.java
new file mode 100644
index 0000000000000000000000000000000000000000..bdd4a064e22a9bb28a56982208e45f4fb4964231
--- /dev/null
+++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/JeecgRedisOAuth2AuthorizationService.java
@@ -0,0 +1,192 @@
+package org.jeecg.config.security;
+
+import cn.hutool.core.collection.CollUtil;
+import jakarta.annotation.PostConstruct;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.RedisSerializer;
+import org.springframework.lang.Nullable;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.oauth2.core.OAuth2AccessToken;
+import org.springframework.security.oauth2.core.OAuth2RefreshToken;
+import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
+import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
+import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationCode;
+import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
+import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
+import org.springframework.stereotype.Component;
+import org.springframework.util.Assert;
+
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * spring authorization server自定义redis保存认证信息
+ * @author EightMonth
+ */
+@Component
+public class JeecgRedisOAuth2AuthorizationService implements OAuth2AuthorizationService{
+
+ private final static Long TIMEOUT = 10L;
+
+ private static final String AUTHORIZATION = "token";
+
+ private final RedisTemplate redisTemplate = new RedisTemplate<>();
+
+ @Autowired
+ private RedisConnectionFactory redisConnectionFactory;
+
+ /**
+ * 因为保存sas的认证信息至redis,无法使用jeecg对redisTemplate的某些设置。
+ * 如果在使用时修改redisTemplate属性,会发生线程安全问题,最终容易引起系统无法正常运行。
+ * 所以重新建了一个redis client给到sas操作redis,并且该redis实例不注入spring 容器中
+ */
+ @PostConstruct
+ public void initSasRedis() {
+ redisTemplate.setValueSerializer(RedisSerializer.java());
+ redisTemplate.setConnectionFactory(redisConnectionFactory);
+ redisTemplate.afterPropertiesSet();
+ }
+
+ @Override
+ public void save(OAuth2Authorization authorization) {
+ Assert.notNull(authorization, "authorization cannot be null");
+
+ if (isState(authorization)) {
+ String token = authorization.getAttribute("state");
+ redisTemplate.opsForValue().set(buildKey(OAuth2ParameterNames.STATE, token), authorization, TIMEOUT,
+ TimeUnit.MINUTES);
+ }
+
+ if (isCode(authorization)) {
+ OAuth2Authorization.Token authorizationCode = authorization
+ .getToken(OAuth2AuthorizationCode.class);
+ OAuth2AuthorizationCode authorizationCodeToken = authorizationCode.getToken();
+ long between = ChronoUnit.MINUTES.between(authorizationCodeToken.getIssuedAt(),
+ authorizationCodeToken.getExpiresAt());
+ redisTemplate.opsForValue().set(buildKey(OAuth2ParameterNames.CODE, authorizationCodeToken.getTokenValue()),
+ authorization, between, TimeUnit.MINUTES);
+ }
+
+ if (isRefreshToken(authorization)) {
+ OAuth2RefreshToken refreshToken = authorization.getRefreshToken().getToken();
+ long between = ChronoUnit.SECONDS.between(refreshToken.getIssuedAt(), refreshToken.getExpiresAt());
+ redisTemplate.opsForValue().set(buildKey(OAuth2ParameterNames.REFRESH_TOKEN, refreshToken.getTokenValue()),
+ authorization, between, TimeUnit.SECONDS);
+ }
+
+ if (isAccessToken(authorization)) {
+ OAuth2AccessToken accessToken = authorization.getAccessToken().getToken();
+ long between = ChronoUnit.SECONDS.between(accessToken.getIssuedAt(), accessToken.getExpiresAt());
+ redisTemplate.opsForValue().set(buildKey(OAuth2ParameterNames.ACCESS_TOKEN, accessToken.getTokenValue()),
+ authorization, between, TimeUnit.SECONDS);
+
+ // 扩展记录 access-token 、username 的关系 1::token::username::admin::xxx
+ String tokenUsername = String.format("%s::%s::%s", AUTHORIZATION, authorization.getPrincipalName(), accessToken.getTokenValue());
+ redisTemplate.opsForValue().set(tokenUsername, accessToken.getTokenValue(), between, TimeUnit.SECONDS);
+ }
+ }
+
+ @Override
+ public void remove(OAuth2Authorization authorization) {
+ Assert.notNull(authorization, "authorization cannot be null");
+
+ List keys = new ArrayList<>();
+ if (isState(authorization)) {
+ String token = authorization.getAttribute("state");
+ keys.add(buildKey(OAuth2ParameterNames.STATE, token));
+ }
+
+ if (isCode(authorization)) {
+ OAuth2Authorization.Token authorizationCode = authorization
+ .getToken(OAuth2AuthorizationCode.class);
+ OAuth2AuthorizationCode authorizationCodeToken = authorizationCode.getToken();
+ keys.add(buildKey(OAuth2ParameterNames.CODE, authorizationCodeToken.getTokenValue()));
+ }
+
+ if (isRefreshToken(authorization)) {
+ OAuth2RefreshToken refreshToken = authorization.getRefreshToken().getToken();
+ keys.add(buildKey(OAuth2ParameterNames.REFRESH_TOKEN, refreshToken.getTokenValue()));
+ }
+
+ if (isAccessToken(authorization)) {
+ OAuth2AccessToken accessToken = authorization.getAccessToken().getToken();
+ keys.add(buildKey(OAuth2ParameterNames.ACCESS_TOKEN, accessToken.getTokenValue()));
+
+ // 扩展记录 access-token 、username 的关系 1::token::username::admin::xxx
+ String key = String.format("%s::%s::%s", AUTHORIZATION, authorization.getPrincipalName(), accessToken.getTokenValue());
+ keys.add(key);
+ }
+
+ redisTemplate.delete(keys);
+ }
+
+ @Override
+ @Nullable
+ public OAuth2Authorization findById(String id) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ @Nullable
+ public OAuth2Authorization findByToken(String token, @Nullable OAuth2TokenType tokenType) {
+ Assert.hasText(token, "token cannot be empty");
+ Assert.notNull(tokenType, "tokenType cannot be empty");
+ return (OAuth2Authorization) redisTemplate.opsForValue().get(buildKey(tokenType.getValue(), token));
+ }
+
+ private String buildKey(String type, String id) {
+ return String.format("%s::%s::%s", AUTHORIZATION, type, id);
+ }
+
+ private static boolean isState(OAuth2Authorization authorization) {
+ return Objects.nonNull(authorization.getAttribute("state"));
+ }
+
+ private static boolean isCode(OAuth2Authorization authorization) {
+ OAuth2Authorization.Token authorizationCode = authorization
+ .getToken(OAuth2AuthorizationCode.class);
+ return Objects.nonNull(authorizationCode);
+ }
+
+ private static boolean isRefreshToken(OAuth2Authorization authorization) {
+ return Objects.nonNull(authorization.getRefreshToken());
+ }
+
+ private static boolean isAccessToken(OAuth2Authorization authorization) {
+ return Objects.nonNull(authorization.getAccessToken());
+ }
+
+ /**
+ * 扩展方法根据 username 查询是否存在存储的
+ * @param authentication
+ * @return
+ */
+ public void removeByUsername(Authentication authentication) {
+ // 根据 username查询对应access-token
+ String authenticationName = authentication.getName();
+
+ // 扩展记录 access-token 、username 的关系 1::token::username::admin::xxx
+ String tokenUsernameKey = String.format("%s::%s::*", AUTHORIZATION, authenticationName);
+ Set keys = redisTemplate.keys(tokenUsernameKey);
+ if (CollUtil.isEmpty(keys)) {
+ return;
+ }
+
+ List