mirror of
https://codeup.aliyun.com/64f7d6b8ce01efaafef1e678/coal/coal.git
synced 2026-01-25 07:46:40 +08:00
性能优化
This commit is contained in:
@@ -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";
|
||||
|
||||
@@ -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, "/");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<ResourceDto> getResourceDto(
|
||||
HttpServletRequest request, HttpServletResponse response) {
|
||||
Optional<ResourceDto> 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<TransactionStatus> 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<ResourceDto> resource =
|
||||
resourceService.findUrlFromCache(getRequestURI(request));
|
||||
Optional<ResourceDto> resource = getResourceDto(request, response);
|
||||
|
||||
if (resource.isEmpty()) {
|
||||
RequestUtils.writeResponse(new BizException("invalidUrl", "资源未找到"), response);
|
||||
return;
|
||||
}
|
||||
stopWatch.stop();
|
||||
|
||||
// 未登录用户访问
|
||||
if (StringUtils.isEmpty(sessionId)) {
|
||||
// 找到匿名权限
|
||||
List<PermissionDto> permissions = permissionService.getByTypeFromCache("0");
|
||||
|
||||
stopWatch.start("找到匿名权限");
|
||||
// 找到匿名权限
|
||||
boolean isAnonymous =
|
||||
permissions.stream()
|
||||
.flatMap(
|
||||
x ->
|
||||
ObjectUtils.defaultIfNull(
|
||||
x.getResources(),
|
||||
new ArrayList<PermissionDto>())
|
||||
.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<ResourceDto> 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, "/");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
57
src/main/java/cn/lihongjie/coal/filter/ResourceFilter.java
Normal file
57
src/main/java/cn/lihongjie/coal/filter/ResourceFilter.java
Normal file
@@ -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<ResourceDto> 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);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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<PermissionEntity> {
|
||||
List<PermissionEntity> findAllByPermissionType(String type);
|
||||
|
||||
List<PermissionEntity> 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<String> permissionType);
|
||||
}
|
||||
|
||||
@@ -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<PermissionEntity, PermissionR
|
||||
public void clearCache() {
|
||||
cacheManager.getCache(Constants.CACHE_PERMISSION).clear();
|
||||
cacheManager.getCache(Constants.CACHE_PERMISSION_BY_TYPE).clear();
|
||||
cacheManager.getCache(Constants.CACHE_IS_ANONYMOUS_BY_RESOURCE_ID).clear();
|
||||
cacheManager.getCache(Constants.CACHE_ORG_ADMIN_HAS_PERMISSION).clear();
|
||||
}
|
||||
|
||||
@Cacheable(cacheNames = Constants.CACHE_IS_ANONYMOUS_BY_RESOURCE_ID, key = "#resourceId")
|
||||
|
||||
public boolean isAnonymousByResourceId(String resourceId) {
|
||||
|
||||
|
||||
return this.repository.isAnonymousByResourceId(resourceId);
|
||||
|
||||
}
|
||||
|
||||
@Cacheable(cacheNames = Constants.CACHE_ORG_ADMIN_HAS_PERMISSION, key = "#resourceId")
|
||||
|
||||
public boolean orgAdminHasPermission( String resourceId) {
|
||||
|
||||
|
||||
return this.repository.hasPermission(resourceId, Arrays.asList("0", "1", "2"));
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,4 +36,20 @@ public class ResourceDto extends CommonDto {
|
||||
private String metadata;
|
||||
|
||||
private String parent;
|
||||
|
||||
public ResourceDto() {
|
||||
}
|
||||
|
||||
public ResourceDto(String id, String code, String name, String type, Boolean anonymous, Boolean orgAdmin, Boolean sysAdmin) {
|
||||
|
||||
this.setId(id);
|
||||
this.setCode(code);
|
||||
this.setName(name);
|
||||
|
||||
this.type = type;
|
||||
this.orgAdmin = orgAdmin;
|
||||
this.sysAdmin = sysAdmin;
|
||||
this.anonymous = anonymous;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package cn.lihongjie.coal.resource.repository;
|
||||
|
||||
import cn.lihongjie.coal.base.dao.BaseRepository;
|
||||
import cn.lihongjie.coal.resource.dto.ResourceDto;
|
||||
import cn.lihongjie.coal.resource.entity.ResourceEntity;
|
||||
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
@@ -12,4 +14,9 @@ public interface ResourceRepository extends BaseRepository<ResourceEntity> {
|
||||
|
||||
// @EntityGraph(attributePaths = {"children"})
|
||||
List<ResourceEntity> 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);
|
||||
}
|
||||
|
||||
@@ -240,10 +240,23 @@ public class ResourceService extends BaseService<ResourceEntity, ResourceReposit
|
||||
.findAny();
|
||||
}
|
||||
|
||||
|
||||
@Cacheable(cacheNames = Constants.CACHE_RESOURCE_BY_URL_2, key = "#url")
|
||||
public Optional<ResourceDto> 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();
|
||||
|
||||
@@ -15,4 +15,8 @@ public interface UserRepository extends BaseRepository<UserEntity> {
|
||||
|
||||
@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);
|
||||
|
||||
}
|
||||
|
||||
@@ -390,4 +390,9 @@ public class UserService extends BaseService<UserEntity, UserRepository> {
|
||||
UserEntity user = users.get(0);
|
||||
return user;
|
||||
}
|
||||
|
||||
public boolean hasResource(String userId, String resourceId) {
|
||||
|
||||
return this.repository.hasResource(userId, resourceId);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user