Merge pull request '메뉴 권한 수정,shp 파일생성 baseurl 설정 추가, 추론데이터 테이블 수정' (#126) from feat/dev_251201 into develop

Reviewed-on: https://kamco.gitea.gs.dabeeo.com/dabeeo/kamco-dabeeo-backoffice/pulls/126
This commit is contained in:
2025-12-30 15:13:04 +09:00
9 changed files with 86 additions and 13 deletions

View File

@@ -1,13 +1,12 @@
package com.kamco.cd.kamcoback.auth; package com.kamco.cd.kamcoback.auth;
import com.kamco.cd.kamcoback.common.enums.RoleType;
import com.kamco.cd.kamcoback.postgres.entity.MenuEntity; import com.kamco.cd.kamcoback.postgres.entity.MenuEntity;
import com.kamco.cd.kamcoback.postgres.repository.menu.MenuRepository; import com.kamco.cd.kamcoback.postgres.repository.menu.MenuRepository;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import java.util.List; import java.util.List;
import java.util.function.Supplier; import java.util.function.Supplier;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager; import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
@@ -24,8 +23,6 @@ import org.springframework.stereotype.Component;
@RequiredArgsConstructor @RequiredArgsConstructor
public class MenuAuthorizationManager implements AuthorizationManager<RequestAuthorizationContext> { public class MenuAuthorizationManager implements AuthorizationManager<RequestAuthorizationContext> {
private static final Logger log = LogManager.getLogger(MenuAuthorizationManager.class);
private final MenuRepository menuAuthQueryRepository; private final MenuRepository menuAuthQueryRepository;
@Override @Override
@@ -45,19 +42,50 @@ public class MenuAuthorizationManager implements AuthorizationManager<RequestAut
return new AuthorizationDecision(false); return new AuthorizationDecision(false);
} }
// DB에서 role에 허용된 메뉴 조회 boolean isAdmin = RoleType.ADMIN.getId().equalsIgnoreCase(role);
List<MenuEntity> allowedMenus = menuAuthQueryRepository.findAllowedMenuUrlsByRole(role);
// URL별 권한 조회
List<MenuEntity> matchedMenus = menuAuthQueryRepository.findMenusByRequestPath(requestPath);
boolean isProtectedUrl = matchedMenus != null && !matchedMenus.isEmpty();
// URL별 권한에 라벨러, 검수자 권한이 있으면 , ADMIN도 false
if (isProtectedUrl) {
List<MenuEntity> allowedMenus = menuAuthQueryRepository.findAllowedMenuUrlsByRole(role);
if (allowedMenus == null || allowedMenus.isEmpty()) { if (allowedMenus == null || allowedMenus.isEmpty()) {
return new AuthorizationDecision(false); return new AuthorizationDecision(false);
} }
// menu_url(prefix) 기반 접근 허용 판단
for (MenuEntity menu : allowedMenus) { for (MenuEntity menu : allowedMenus) {
String baseUri = menu.getMenuUrl(); String baseUri = menu.getMenuUrl();
if (baseUri == null || baseUri.isBlank()) { if (baseUri == null || baseUri.isBlank()) {
continue; continue;
} }
if (matchUri(baseUri, requestPath)) {
return new AuthorizationDecision(true);
}
}
return new AuthorizationDecision(false);
}
// ADMIN은 전부 허용
if (isAdmin) {
return new AuthorizationDecision(true);
}
// 일반 role은 기존대로 매핑 기반
List<MenuEntity> allowedMenus = menuAuthQueryRepository.findAllowedMenuUrlsByRole(role);
if (allowedMenus == null || allowedMenus.isEmpty()) {
return new AuthorizationDecision(false);
}
for (MenuEntity menu : allowedMenus) {
String baseUri = menu.getMenuUrl();
if (baseUri == null || baseUri.isBlank()) {
continue;
}
if (matchUri(baseUri, requestPath)) { if (matchUri(baseUri, requestPath)) {
return new AuthorizationDecision(true); return new AuthorizationDecision(true);
} }

View File

@@ -83,9 +83,10 @@ public class SecurityConfig {
.requestMatchers("/api/user/**") .requestMatchers("/api/user/**")
.authenticated() .authenticated()
.anyRequest() .anyRequest()
// .access(redisAuthorizationManager) .access(menuAuthorizationManager)
.authenticated()) // .authenticated()
)
.addFilterBefore( .addFilterBefore(
jwtAuthenticationFilter, jwtAuthenticationFilter,
UsernamePasswordAuthenticationFilter UsernamePasswordAuthenticationFilter

View File

@@ -5,6 +5,7 @@ import com.kamco.cd.kamcoback.inference.dto.WriteCnt;
import com.kamco.cd.kamcoback.postgres.core.InferenceResultShpCoreService; import com.kamco.cd.kamcoback.postgres.core.InferenceResultShpCoreService;
import java.util.List; import java.util.List;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -16,6 +17,9 @@ public class InferenceResultShpService {
private final InferenceResultShpCoreService coreService; private final InferenceResultShpCoreService coreService;
private final ShpWriter shpWriter; private final ShpWriter shpWriter;
@Value("${mapsheet.shp.baseurl}")
private String baseDir;
/** inference_results 테이블을 기준으로 분석 결과 테이블과 도형 테이블을 최신 상태로 반영한다. */ /** inference_results 테이블을 기준으로 분석 결과 테이블과 도형 테이블을 최신 상태로 반영한다. */
@Transactional @Transactional
public InferenceResultShpDto.InferenceCntDto saveInferenceResultData() { public InferenceResultShpDto.InferenceCntDto saveInferenceResultData() {
@@ -33,9 +37,6 @@ public class InferenceResultShpService {
@Transactional @Transactional
public InferenceResultShpDto.FileCntDto createShpFile() { public InferenceResultShpDto.FileCntDto createShpFile() {
// TODO 파일 경로는 정해지면 수정, properties 사용
String baseDir = "/app/detect/result";
// TODO 배치 실행으로 변경 필요 // TODO 배치 실행으로 변경 필요
int batchSize = 100; int batchSize = 100;
int geomLimit = 500_000; int geomLimit = 500_000;

View File

@@ -146,4 +146,10 @@ public class MapSheetAnalDataInferenceGeomEntity {
@Column(name = "file_created_dttm") @Column(name = "file_created_dttm")
private ZonedDateTime fileCreatedDttm; private ZonedDateTime fileCreatedDttm;
@Column(name = "pass_yn")
private Boolean passYn;
@Column(name = "pass_yn_dttm")
private ZonedDateTime passYnDttm;
} }

View File

@@ -22,4 +22,12 @@ public interface MenuRepositoryCustom {
* @return * @return
*/ */
List<MenuEntity> findAllowedMenuUrlsByRole(String role); List<MenuEntity> findAllowedMenuUrlsByRole(String role);
/**
* url별 역할
*
* @param requestPath
* @return
*/
List<MenuEntity> findMenusByRequestPath(String requestPath);
} }

View File

@@ -79,4 +79,21 @@ public class MenuRepositoryImpl implements MenuRepositoryCustom {
.orderBy(menuEntity.menuOrder.asc().nullsLast()) .orderBy(menuEntity.menuOrder.asc().nullsLast())
.fetch(); .fetch();
} }
@Override
public List<MenuEntity> findMenusByRequestPath(String requestPath) {
return queryFactory
.selectDistinct(menuEntity)
.from(menuMappEntity)
.join(menuMappEntity.menuUid, menuEntity)
.where(
menuMappEntity.deleted.isFalse(),
menuEntity.deleted.isFalse(),
menuEntity.isUse.isTrue(),
menuEntity.menuUrl.isNotNull(),
menuEntity.menuUrl.isNotEmpty(),
menuEntity.menuUrl.eq(requestPath))
.orderBy(menuEntity.menuOrder.asc().nullsLast())
.fetch();
}
} }

View File

@@ -73,4 +73,6 @@ logging:
mapsheet: mapsheet:
upload: upload:
skipGdalValidation: true skipGdalValidation: true
shp:
baseurl: /app/detect/result

View File

@@ -53,3 +53,9 @@ token:
springdoc: springdoc:
swagger-ui: swagger-ui:
persist-authorization: true # 스웨거 새로고침해도 토큰 유지, 로컬스토리지에 저장 persist-authorization: true # 스웨거 새로고침해도 토큰 유지, 로컬스토리지에 저장
mapsheet:
upload:
skipGdalValidation: true
shp:
baseurl: /Users/bokmin/detect/result

View File

@@ -30,5 +30,9 @@ token:
refresh-cookie-name: kamco # 개발용 쿠키 이름 refresh-cookie-name: kamco # 개발용 쿠키 이름
refresh-cookie-secure: true # 로컬 http 테스트면 false refresh-cookie-secure: true # 로컬 http 테스트면 false
mapsheet:
upload:
skipGdalValidation: true
shp:
baseurl: /app/detect/result