From 529c26e8cc0f52180e6a0aac40fd687d152cdd7d Mon Sep 17 00:00:00 2001 From: lihongjie0209 Date: Sat, 13 Jan 2024 16:45:00 +0800 Subject: [PATCH] =?UTF-8?q?=E6=80=A7=E8=83=BD=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/lihongjie/coal/common/Constants.java | 4 + .../lihongjie/coal/common/RequestUtils.java | 10 ++ .../cn/lihongjie/coal/filter/AuthFilter.java | 132 +++++++++--------- .../lihongjie/coal/filter/ResourceFilter.java | 57 ++++++++ .../repository/PermissionRepository.java | 7 + .../permission/service/PermissionService.java | 22 +++ .../coal/resource/dto/ResourceDto.java | 16 +++ .../repository/ResourceRepository.java | 7 + .../resource/service/ResourceService.java | 13 ++ .../coal/user/repository/UserRepository.java | 4 + .../coal/user/service/UserService.java | 5 + 11 files changed, 209 insertions(+), 68 deletions(-) create mode 100644 src/main/java/cn/lihongjie/coal/filter/ResourceFilter.java diff --git a/src/main/java/cn/lihongjie/coal/common/Constants.java b/src/main/java/cn/lihongjie/coal/common/Constants.java index 4012ff16..8b9d2b8c 100644 --- a/src/main/java/cn/lihongjie/coal/common/Constants.java +++ b/src/main/java/cn/lihongjie/coal/common/Constants.java @@ -36,6 +36,10 @@ public class Constants { public static final String CACHE_CLIENT_RANDOM_PREFIX = "clientRandom::"; public static final String HTTP_HEADER_RESPONSE_TIME = "X-Response-Time"; public static final String SYSCONFIG_REQUEST_SIGN_IGNORE_URLS = "request_sign_ignore_urls"; + public static final String HTTP_ATTR_RESOURCE = "http_attr_resource"; + public static final String CACHE_RESOURCE_BY_URL_2 = "resourceByUrl2"; + public static final String CACHE_IS_ANONYMOUS_BY_RESOURCE_ID = "isAnonymousByResourceId"; + public static final String CACHE_ORG_ADMIN_HAS_PERMISSION = "orgAdminHasPermission"; public static String SYSCONFIG_ENABLE_CAPTCHA = "enable_captcha"; public static String SYSCONFIG_ENABLE_REQUEST_SIGN = "enable_request_sign"; public static String SYSCONFIG_SESSION_TIMEOUT = "session_timeout"; diff --git a/src/main/java/cn/lihongjie/coal/common/RequestUtils.java b/src/main/java/cn/lihongjie/coal/common/RequestUtils.java index 3a9c1dde..a29ca178 100644 --- a/src/main/java/cn/lihongjie/coal/common/RequestUtils.java +++ b/src/main/java/cn/lihongjie/coal/common/RequestUtils.java @@ -85,4 +85,14 @@ public class RequestUtils { response.getOutputStream().write(Ctx.getContext().getBean(ObjectMapper.class).writeValueAsBytes(fail)); response.getOutputStream().flush(); } + + public static String getRequestURI(HttpServletRequest request, String contextPath1) { + if (StringUtils.equalsIgnoreCase(contextPath1, "/")) { + + return request.getRequestURI(); + } else { + + return request.getRequestURI().replace(contextPath1, "/"); + } + } } diff --git a/src/main/java/cn/lihongjie/coal/filter/AuthFilter.java b/src/main/java/cn/lihongjie/coal/filter/AuthFilter.java index cf9eb032..e9429be1 100644 --- a/src/main/java/cn/lihongjie/coal/filter/AuthFilter.java +++ b/src/main/java/cn/lihongjie/coal/filter/AuthFilter.java @@ -4,7 +4,6 @@ import cn.lihongjie.coal.common.Constants; import cn.lihongjie.coal.common.Ctx; import cn.lihongjie.coal.common.RequestUtils; import cn.lihongjie.coal.exception.BizException; -import cn.lihongjie.coal.permission.dto.PermissionDto; import cn.lihongjie.coal.permission.service.PermissionService; import cn.lihongjie.coal.resource.dto.ResourceDto; import cn.lihongjie.coal.resource.service.ResourceService; @@ -16,16 +15,13 @@ import cn.lihongjie.coal.user.service.UserService; import com.fasterxml.jackson.databind.ObjectMapper; -import io.vavr.collection.Stream; - import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.lang3.BooleanUtils; -import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.Nullable; import org.slf4j.MDC; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -33,15 +29,11 @@ import org.springframework.core.annotation.Order; import org.springframework.http.server.PathContainer; import org.springframework.stereotype.Component; import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.TransactionStatus; +import org.springframework.util.StopWatch; import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.util.pattern.PathPatternParser; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; import java.util.Optional; -import java.util.function.Consumer; @Component @Order(100) @@ -63,23 +55,22 @@ public class AuthFilter extends OncePerRequestFilter { @Autowired PermissionService permissionService; - @Override - public void doFilterInternal( - HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) - throws ServletException, IOException { - - try { - - getTransactionStatusConsumer(request, response, filterChain).accept(null); - } catch (Exception e) { - - logger.info("系统异常", e); - RequestUtils.writeResponse(new BizException("系统异常"), response); - } - } - @Autowired UserService userService; + @Nullable + private static Optional getResourceDto( + HttpServletRequest request, HttpServletResponse response) { + Optional resource = + Optional.ofNullable( + (ResourceDto) request.getAttribute(Constants.HTTP_ATTR_RESOURCE)); + + if (resource.isEmpty()) { + RequestUtils.writeResponse(new BizException("invalidUrl", "资源未找到"), response); + return null; + } + return resource; + } + private static void doFilter( HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) { try { @@ -89,10 +80,13 @@ public class AuthFilter extends OncePerRequestFilter { } } - private Consumer getTransactionStatusConsumer( + @Override + public void doFilterInternal( HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) { + StopWatch stopWatch = new StopWatch(); + + try { - return tx -> { MDC.remove("user"); if (isMatches(request)) { @@ -100,34 +94,24 @@ public class AuthFilter extends OncePerRequestFilter { return; } - String sessionId = StringUtils.defaultIfEmpty(request.getHeader(Constants.HTTP_HEADER_TOKEN), request.getParameter(Constants.HTTP_HEADER_TOKEN)); + stopWatch.start("get resource"); + String sessionId = + StringUtils.defaultIfEmpty( + request.getHeader(Constants.HTTP_HEADER_TOKEN), + request.getParameter(Constants.HTTP_HEADER_TOKEN)); - Optional resource = - resourceService.findUrlFromCache(getRequestURI(request)); + Optional resource = getResourceDto(request, response); - if (resource.isEmpty()) { - RequestUtils.writeResponse(new BizException("invalidUrl", "资源未找到"), response); - return; - } + stopWatch.stop(); // 未登录用户访问 if (StringUtils.isEmpty(sessionId)) { - // 找到匿名权限 - List permissions = permissionService.getByTypeFromCache("0"); + stopWatch.start("找到匿名权限"); + // 找到匿名权限 boolean isAnonymous = - permissions.stream() - .flatMap( - x -> - ObjectUtils.defaultIfNull( - x.getResources(), - new ArrayList()) - .stream()) - .anyMatch( - x -> - StringUtils.equals( - x.getId(), resource.get().getId())) - || BooleanUtils.isTrue(resource.get().getAnonymous()); + permissionService.isAnonymousByResourceId(resource.get().getId()); + stopWatch.stop(); if (isAnonymous) { sessionService.anonymousSession(); @@ -146,8 +130,11 @@ public class AuthFilter extends OncePerRequestFilter { // try { + stopWatch.start("重建session"); sessionService.rebuildSession(sessionId); + stopWatch.stop(); + } catch (BizException ex) { RequestUtils.writeResponse(ex, response); @@ -158,20 +145,38 @@ public class AuthFilter extends OncePerRequestFilter { var user = Ctx.currentUser(); MDC.put("user", user.getUsername()); - Optional userResource = - Stream.ofAll(userService.resources(user.getId())) - .filter(x -> StringUtils.equals(x.getId(), resource.get().getId())) - .headOption() - .toJavaOptional(); + stopWatch.start("检查权限"); - if (userResource.isEmpty() && BooleanUtils.isFalse(user.getSysAdmin())) { + boolean hasPermission = false; + if (BooleanUtils.isTrue(user.getSysAdmin())) { + hasPermission = true; + } else if (BooleanUtils.isTrue(user.getOrgAdmin())) { - RequestUtils.writeResponse(new BizException("invalidAccess", "当前资源未授权,请联系机构管理员处理。"), response); + hasPermission = permissionService.orgAdminHasPermission(resource.get().getId()); + } else { + + hasPermission = userService.hasResource(user.getId(), resource.get().getId()); + } + + stopWatch.stop(); + if (!hasPermission) { + + RequestUtils.writeResponse( + new BizException("invalidAccess", "当前资源未授权,请联系机构管理员处理。"), response); } else { doFilter(request, response, filterChain); } - }; + } catch (Exception e) { + + logger.info("系统异常", e); + RequestUtils.writeResponse(new BizException("系统异常"), response); + } finally { + if (stopWatch.isRunning()) { + stopWatch.stop(); + } + logger.info(stopWatch.prettyPrint()); + } } private boolean isMatches(HttpServletRequest request) { @@ -181,22 +186,13 @@ public class AuthFilter extends OncePerRequestFilter { matches = PathPatternParser.defaultInstance .parse(url) - .matches(PathContainer.parsePath(getRequestURI(request))); + .matches( + PathContainer.parsePath( + RequestUtils.getRequestURI(request, contextPath))); if (matches) { break; } } return matches; } - - private String getRequestURI(HttpServletRequest request) { - if (StringUtils.equalsIgnoreCase(contextPath, "/")) { - - return request.getRequestURI(); - } else { - - return request.getRequestURI().replace(contextPath, "/"); - } - } - } diff --git a/src/main/java/cn/lihongjie/coal/filter/ResourceFilter.java b/src/main/java/cn/lihongjie/coal/filter/ResourceFilter.java new file mode 100644 index 00000000..60d19a68 --- /dev/null +++ b/src/main/java/cn/lihongjie/coal/filter/ResourceFilter.java @@ -0,0 +1,57 @@ +package cn.lihongjie.coal.filter; + + +import cn.lihongjie.coal.common.Constants; +import cn.lihongjie.coal.common.RequestUtils; +import cn.lihongjie.coal.exception.BizException; +import cn.lihongjie.coal.resource.dto.ResourceDto; +import cn.lihongjie.coal.resource.service.ResourceService; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import lombok.extern.slf4j.Slf4j; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; +import java.util.Optional; + +/** +* +*/ +@Component +@Order(-1) +@Slf4j +public class ResourceFilter extends OncePerRequestFilter { + + @Autowired ResourceService resourceService; + + @Value("${server.servlet.context-path}") + private String contextPath; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + + + Optional resource = + resourceService.findUrlFromCache2(RequestUtils.getRequestURI(request, contextPath)); + + + if (resource.isEmpty()) { + RequestUtils.writeResponse(new BizException("invalidUrl", "资源未找到"), response); + return; + } + + request.setAttribute(Constants.HTTP_ATTR_RESOURCE, resource.get()); + + doFilter(request, response, filterChain); + + } +} diff --git a/src/main/java/cn/lihongjie/coal/permission/repository/PermissionRepository.java b/src/main/java/cn/lihongjie/coal/permission/repository/PermissionRepository.java index 9264e26f..1e7bdc98 100644 --- a/src/main/java/cn/lihongjie/coal/permission/repository/PermissionRepository.java +++ b/src/main/java/cn/lihongjie/coal/permission/repository/PermissionRepository.java @@ -3,6 +3,7 @@ package cn.lihongjie.coal.permission.repository; import cn.lihongjie.coal.base.dao.BaseRepository; import cn.lihongjie.coal.permission.entity.PermissionEntity; +import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; import java.util.List; @@ -12,4 +13,10 @@ public interface PermissionRepository extends BaseRepository { List findAllByPermissionType(String type); List findAllByPermissionTypeIn(String[] types); + + @Query("select count(p) > 0 from PermissionEntity p inner join p.resources r on r.id = ?1 where p.permissionType = '0' ") + boolean isAnonymousByResourceId(String resourceId); + + @Query("select count(p) > 0 from PermissionEntity p inner join p.resources r on r.id = ?1 where p.permissionType in ?2 ") + boolean hasPermission( String resourceId, List permissionType); } diff --git a/src/main/java/cn/lihongjie/coal/permission/service/PermissionService.java b/src/main/java/cn/lihongjie/coal/permission/service/PermissionService.java index 05eee24a..2e8851cf 100644 --- a/src/main/java/cn/lihongjie/coal/permission/service/PermissionService.java +++ b/src/main/java/cn/lihongjie/coal/permission/service/PermissionService.java @@ -36,6 +36,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -230,5 +231,26 @@ public class PermissionService extends BaseService { // @EntityGraph(attributePaths = {"children"}) List findAllByTypeAndParentIsNullOrderBySortKey(String type); + + ResourceEntity findByUrlAndType(String url, String type); + + @Query("select new cn.lihongjie.coal.resource.dto.ResourceDto(r.id, r.code, r.name, r.type, r.anonymous, r.orgAdmin, r.sysAdmin) from ResourceEntity r where r.code = ?1 and r.type = ?2 ") + ResourceDto findByCodeAndType(String code, String type); } diff --git a/src/main/java/cn/lihongjie/coal/resource/service/ResourceService.java b/src/main/java/cn/lihongjie/coal/resource/service/ResourceService.java index 970954f6..be531590 100644 --- a/src/main/java/cn/lihongjie/coal/resource/service/ResourceService.java +++ b/src/main/java/cn/lihongjie/coal/resource/service/ResourceService.java @@ -240,10 +240,23 @@ public class ResourceService extends BaseService findUrlFromCache2(String url) { + + + + return Optional.ofNullable(repository.findByCodeAndType(url, "3")); + + + } + + public void clearCache() { cacheManager.getCache(Constants.CACHE_RESOURCE).clear(); cacheManager.getCache(Constants.CACHE_RESOURCE_BY_URL).clear(); + cacheManager.getCache(Constants.CACHE_RESOURCE_BY_URL_2).clear(); cacheManager.getCache(Constants.CACHE_RESOURCE_MENU_TREE).clear(); cacheManager.getCache(Constants.CACHE_RESOURCE_API_TREE).clear(); permissionService.clearCache(); diff --git a/src/main/java/cn/lihongjie/coal/user/repository/UserRepository.java b/src/main/java/cn/lihongjie/coal/user/repository/UserRepository.java index ccddcf78..c3296cf8 100644 --- a/src/main/java/cn/lihongjie/coal/user/repository/UserRepository.java +++ b/src/main/java/cn/lihongjie/coal/user/repository/UserRepository.java @@ -15,4 +15,8 @@ public interface UserRepository extends BaseRepository { @Query("select count(u) from UserEntity u where u.organizationId = ?1") Integer countByOrganizationId(String organizationId); + + @Query("select count(u) > 0 from UserEntity u join u.roles r join r.permissions p join p.resources pr where u.id = ?1 and pr.id = ?2") + boolean hasResource(String userId, String resourceId); + } diff --git a/src/main/java/cn/lihongjie/coal/user/service/UserService.java b/src/main/java/cn/lihongjie/coal/user/service/UserService.java index 9a8ac818..0097eae2 100644 --- a/src/main/java/cn/lihongjie/coal/user/service/UserService.java +++ b/src/main/java/cn/lihongjie/coal/user/service/UserService.java @@ -390,4 +390,9 @@ public class UserService extends BaseService { UserEntity user = users.get(0); return user; } + + public boolean hasResource(String userId, String resourceId) { + + return this.repository.hasResource(userId, resourceId); + } }