queryWrapper = QueryGenerator.initQueryWrapper(object, request.getParameterMap());
- LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+ LoginUser sysUser = SecureUtil.currentUser();
// Step.2 计算分页sheet数据
double total = service.count();
int count = (int)Math.ceil(total/pageNum);
diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/util/JwtUtil.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/util/JwtUtil.java
index 7365ece60..da97d9573 100644
--- a/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/util/JwtUtil.java
+++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/util/JwtUtil.java
@@ -1,5 +1,7 @@
package org.jeecg.common.system.util;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson2.JSONObject;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
@@ -17,7 +19,6 @@ import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import lombok.extern.slf4j.Slf4j;
-import org.apache.shiro.SecurityUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.DataBaseConstant;
@@ -29,6 +30,8 @@ import org.jeecg.common.system.vo.SysUserCacheInfo;
import org.jeecg.common.util.DateUtils;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.oConvertUtils;
+import org.jeecg.config.security.utils.SecureUtil;
+import org.springframework.security.core.context.SecurityContextHolder;
/**
* @Author Scott
@@ -95,7 +98,8 @@ public class JwtUtil {
public static String getUsername(String token) {
try {
DecodedJWT jwt = JWT.decode(token);
- return jwt.getClaim("username").asString();
+ LoginUser loginUser = SecureUtil.currentUser();
+ return loginUser.getUsername();
} catch (JWTDecodeException e) {
return null;
}
@@ -177,7 +181,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-base-core/src/main/java/org/jeecg/common/system/vo/LoginUser.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/LoginUser.java
index 44656a7f8..1c63fe232 100644
--- a/jeecg-boot-base-core/src/main/java/org/jeecg/common/system/vo/LoginUser.java
+++ b/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
*/
@@ -127,4 +134,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-base-core/src/main/java/org/jeecg/config/firewall/interceptor/LowCodeModeInterceptor.java b/jeecg-boot-base-core/src/main/java/org/jeecg/config/firewall/interceptor/LowCodeModeInterceptor.java
index 215351d29..071d14068 100644
--- a/jeecg-boot-base-core/src/main/java/org/jeecg/config/firewall/interceptor/LowCodeModeInterceptor.java
+++ b/jeecg-boot-base-core/src/main/java/org/jeecg/config/firewall/interceptor/LowCodeModeInterceptor.java
@@ -6,15 +6,12 @@ import org.apache.shiro.SecurityUtils;
import org.jeecg.common.api.CommonAPI;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
-import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.system.util.JwtUtil;
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.firewall.interceptor.enums.LowCodeUrlsEnum;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.util.AntPathMatcher;
import org.springframework.web.servlet.HandlerInterceptor;
import jakarta.annotation.Resource;
diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/JeecgPermissionService.java b/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/JeecgPermissionService.java
new file mode 100644
index 000000000..69e102530
--- /dev/null
+++ b/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/JeecgPermissionService.java
@@ -0,0 +1,53 @@
+package org.jeecg.config.security;
+
+import cn.hutool.core.util.ArrayUtil;
+import lombok.AllArgsConstructor;
+import org.jeecg.common.api.CommonAPI;
+import org.jeecg.common.system.vo.LoginUser;
+import org.jeecg.config.security.utils.SecureUtil;
+import org.springframework.stereotype.Service;
+import org.springframework.util.PatternMatchUtils;
+import org.springframework.util.StringUtils;
+
+import java.util.Set;
+
+/**
+ * @author EightMonth
+ * @date 2024/1/10 17:00
+ */
+@Service("jps")
+@AllArgsConstructor
+public class JeecgPermissionService {
+
+ private final CommonAPI commonAPI;
+
+ /**
+ * 判断接口是否有任意xxx,xxx权限
+ * @param permissions 权限
+ * @return {boolean}
+ */
+ public boolean requiresPermissions(String... permissions) {
+ if (ArrayUtil.isEmpty(permissions)) {
+ return false;
+ }
+ LoginUser loginUser = SecureUtil.currentUser();
+ Set permissionList = commonAPI.queryUserAuths(loginUser.getUsername());
+ return permissionList.stream().filter(StringUtils::hasText)
+ .anyMatch(x -> PatternMatchUtils.simpleMatch(permissions, x));
+ }
+
+ /**
+ * 判断接口是否有任意xxx,xxx角色
+ * @param roles 角色
+ * @return {boolean}
+ */
+ public boolean requiresRoles(String... roles) {
+ if (ArrayUtil.isEmpty(roles)) {
+ return false;
+ }
+ LoginUser loginUser = SecureUtil.currentUser();
+ Set roleList = commonAPI.queryUserRoles(loginUser.getUsername());
+ return roleList.stream().filter(StringUtils::hasText)
+ .anyMatch(x -> PatternMatchUtils.simpleMatch(roles, x));
+ }
+}
diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/JeecgRedisOAuth2AuthorizationConsentService.java b/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/JeecgRedisOAuth2AuthorizationConsentService.java
new file mode 100644
index 000000000..5ca2113ae
--- /dev/null
+++ b/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/JeecgRedisOAuth2AuthorizationConsentService.java
@@ -0,0 +1,51 @@
+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;
+
+@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-base-core/src/main/java/org/jeecg/config/security/JeecgRedisOAuth2AuthorizationService.java b/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/JeecgRedisOAuth2AuthorizationService.java
new file mode 100644
index 000000000..cdbb7dc53
--- /dev/null
+++ b/jeecg-boot-base-core/src/main/java/org/jeecg/config/security/JeecgRedisOAuth2AuthorizationService.java
@@ -0,0 +1,180 @@
+package org.jeecg.config.security;
+
+import cn.hutool.core.collection.CollUtil;
+import lombok.RequiredArgsConstructor;
+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;
+
+/**
+ * @author EightMonth
+ */
+@Component
+@RequiredArgsConstructor
+public class JeecgRedisOAuth2AuthorizationService implements OAuth2AuthorizationService {
+
+ private final static Long TIMEOUT = 10L;
+
+ private static final String AUTHORIZATION = "token";
+
+ private final RedisTemplate redisTemplate;
+
+ @Override
+ public void save(OAuth2Authorization authorization) {
+ Assert.notNull(authorization, "authorization cannot be null");
+
+ if (isState(authorization)) {
+ String token = authorization.getAttribute("state");
+ redisTemplate.setValueSerializer(RedisSerializer.java());
+ 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.setValueSerializer(RedisSerializer.java());
+ 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.setValueSerializer(RedisSerializer.java());
+ 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.setValueSerializer(RedisSerializer.java());
+ 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");
+ redisTemplate.setValueSerializer(RedisSerializer.java());
+ 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