Merge branch 'feat/dev_251201' of https://kamco.gitea.gs.dabeeo.com/dabeeo/kamco-dabeeo-backoffice into feat/dev_251201
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
package com.kamco.cd.kamcoback.config;
|
||||
package com.kamco.cd.kamcoback.auth;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Base64;
|
||||
@@ -39,6 +39,15 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
|
||||
String path = request.getServletPath();
|
||||
|
||||
// 여기에 JWT 필터를 타지 않게 할 URL 패턴들 작성
|
||||
return path.startsWith("/api/auth/signin") || path.startsWith("/api/auth/refresh");
|
||||
// 필요하면 "/api/auth/logout" 도 추가
|
||||
}
|
||||
|
||||
private String resolveToken(HttpServletRequest request) {
|
||||
String bearer = request.getHeader("Authorization");
|
||||
if (bearer != null && bearer.startsWith("Bearer ")) {
|
||||
|
||||
@@ -3,6 +3,8 @@ package com.kamco.cd.kamcoback.config;
|
||||
import io.swagger.v3.oas.models.Components;
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import io.swagger.v3.oas.models.info.Info;
|
||||
import io.swagger.v3.oas.models.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.models.security.SecurityScheme;
|
||||
import io.swagger.v3.oas.models.servers.Server;
|
||||
import java.util.List;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -13,6 +15,21 @@ public class OpenApiConfig {
|
||||
|
||||
@Bean
|
||||
public OpenAPI kamcoOpenAPI() {
|
||||
// 🔹 1) SecurityScheme 정의 (Bearer JWT)
|
||||
SecurityScheme bearerAuth =
|
||||
new SecurityScheme()
|
||||
.type(SecurityScheme.Type.HTTP)
|
||||
.scheme("bearer")
|
||||
.bearerFormat("JWT")
|
||||
.in(SecurityScheme.In.HEADER)
|
||||
.name("Authorization");
|
||||
|
||||
// 🔹 2) SecurityRequirement (기본으로 BearerAuth 사용)
|
||||
SecurityRequirement securityRequirement = new SecurityRequirement().addList("BearerAuth");
|
||||
|
||||
// 🔹 3) Components 에 SecurityScheme 등록
|
||||
Components components = new Components().addSecuritySchemes("BearerAuth", bearerAuth);
|
||||
|
||||
return new OpenAPI()
|
||||
.info(
|
||||
new Info()
|
||||
@@ -32,6 +49,8 @@ public class OpenApiConfig {
|
||||
new Server().url("https://kamco.dev-api.gs.dabeeo.com").description("개발 서버")
|
||||
// , new Server().url("https://api.kamco.com").description("운영 서버")
|
||||
))
|
||||
.components(new Components());
|
||||
.components(new Components())
|
||||
// 🔥 여기 한 줄이 "모든 API 기본적으로 BearerAuth 요구" 의미
|
||||
.addSecurityItem(securityRequirement);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.kamco.cd.kamcoback.config;
|
||||
|
||||
import com.kamco.cd.kamcoback.auth.CustomAuthenticationProvider;
|
||||
import com.kamco.cd.kamcoback.auth.JwtAuthenticationFilter;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -11,6 +12,9 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.CorsConfigurationSource;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@@ -22,22 +26,23 @@ public class SecurityConfig {
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
http.csrf(csrf -> csrf.disable()) // CSRF 보안 기능 비활성화
|
||||
.sessionManagement(
|
||||
sm ->
|
||||
sm.sessionCreationPolicy(
|
||||
SessionCreationPolicy.STATELESS)) // 서버 세션 만들지 않음 요청은 JWT 인증
|
||||
.formLogin(form -> form.disable()) // react에서 로그인 요청 관리
|
||||
.httpBasic(basic -> basic.disable()) // 기본 basic 인증 비활성화 JWT 인증사용
|
||||
.logout(logout -> logout.disable()) // 기본 로그아웃 비활성화 JWT는 서버 상태가 없으므로 로그아웃 처리 필요 없음
|
||||
.authenticationProvider(
|
||||
customAuthenticationProvider) // 로그인 패스워드 비교방식 스프링 기본 Provider 사용안함 커스텀 사용
|
||||
.authorizeHttpRequests(auth -> auth.anyRequest().permitAll());
|
||||
// http.csrf(csrf -> csrf.disable()) // CSRF 보안 기능 비활성화
|
||||
// .sessionManagement(
|
||||
// sm ->
|
||||
// sm.sessionCreationPolicy(
|
||||
// SessionCreationPolicy.STATELESS)) // 서버 세션 만들지 않음 요청은 JWT 인증
|
||||
// .formLogin(form -> form.disable()) // react에서 로그인 요청 관리
|
||||
// .httpBasic(basic -> basic.disable()) // 기본 basic 인증 비활성화 JWT 인증사용
|
||||
// .logout(logout -> logout.disable()) // 기본 로그아웃 비활성화 JWT는 서버 상태가 없으므로 로그아웃 처리 필요 없음
|
||||
// .authenticationProvider(
|
||||
// customAuthenticationProvider) // 로그인 패스워드 비교방식 스프링 기본 Provider 사용안함 커스텀 사용
|
||||
// .authorizeHttpRequests(
|
||||
// auth ->
|
||||
// auth.requestMatchers(
|
||||
// "/api/auth/signin",
|
||||
// "/api/auth/refresh",
|
||||
// "/swagger-ui/**",
|
||||
// "/v3/api-docs/**") // 로그인 없이 접근가능 url
|
||||
// "/v3/api-docs/**")
|
||||
// .permitAll()
|
||||
// .anyRequest()
|
||||
// .authenticated())
|
||||
@@ -45,6 +50,15 @@ public class SecurityConfig {
|
||||
// jwtAuthenticationFilter,
|
||||
// UsernamePasswordAuthenticationFilter
|
||||
// .class) // 요청 들어오면 먼저 JWT 토큰 검사 후 security context 에 사용자 정보 저장.
|
||||
http.csrf(csrf -> csrf.disable())
|
||||
.sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||||
.formLogin(form -> form.disable())
|
||||
.httpBasic(basic -> basic.disable())
|
||||
.logout(logout -> logout.disable())
|
||||
.authenticationProvider(customAuthenticationProvider)
|
||||
.authorizeHttpRequests(
|
||||
auth -> auth.anyRequest().permitAll() // 🔥 인증 필요 없음
|
||||
);
|
||||
;
|
||||
|
||||
return http.build();
|
||||
@@ -55,4 +69,18 @@ public class SecurityConfig {
|
||||
throws Exception {
|
||||
return configuration.getAuthenticationManager();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CorsConfigurationSource corsConfigurationSource() {
|
||||
CorsConfiguration config = new CorsConfiguration(); // CORS 객체 생성
|
||||
config.setAllowedOriginPatterns(List.of("*")); // 도메인 허용
|
||||
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
|
||||
config.setAllowedHeaders(List.of("*")); // 헤더요청 Authorization, Content-Type, X-Custom-Header
|
||||
config.setAllowCredentials(true); // 쿠키, Authorization 헤더, Bearer Token 등 자격증명 포함 요청을 허용할지 설정
|
||||
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
/** "/**" → 모든 API 경로에 대해 이 CORS 규칙을 적용 /api/** 같이 특정 경로만 지정 가능. */
|
||||
source.registerCorsConfiguration("/**", config); // CORS 정책을 등록
|
||||
return source;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.kamco.cd.kamcoback.config;
|
||||
|
||||
// @Configuration
|
||||
// @SecurityScheme(
|
||||
// name = "BearerAuth",
|
||||
// type = SecuritySchemeType.HTTP,
|
||||
// scheme = "bearer",
|
||||
// bearerFormat = "JWT"
|
||||
// )
|
||||
public class SwaggerConfig {}
|
||||
@@ -10,22 +10,11 @@ import org.locationtech.jts.geom.Polygon;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
public class WebConfig implements WebMvcConfigurer {
|
||||
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry
|
||||
.addMapping("/**") // 모든 URL 허용
|
||||
.allowedOriginPatterns("*")
|
||||
.allowedMethods("*")
|
||||
.allowedHeaders("*")
|
||||
.allowCredentials(true);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ObjectMapper objectMapper() {
|
||||
SimpleModule module = new SimpleModule();
|
||||
|
||||
@@ -9,6 +9,7 @@ import java.util.stream.Collectors;
|
||||
import org.springframework.web.util.ContentCachingRequestWrapper;
|
||||
|
||||
public class ApiLogFunction {
|
||||
|
||||
// 클라이언트 IP 추출
|
||||
public static String getClientIp(HttpServletRequest request) {
|
||||
String[] headers = {
|
||||
@@ -34,6 +35,7 @@ public class ApiLogFunction {
|
||||
// 사용자 ID 추출 예시 (Spring Security 기준)
|
||||
public static String getUserId(HttpServletRequest request) {
|
||||
try {
|
||||
Object userId = request.getUserPrincipal();
|
||||
return request.getUserPrincipal() != null ? request.getUserPrincipal().getName() : null;
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
@@ -45,8 +47,12 @@ public class ApiLogFunction {
|
||||
String uri = request.getRequestURI().toLowerCase();
|
||||
|
||||
// URL 기반 DOWNLOAD/PRINT 분류
|
||||
if (uri.contains("/download") || uri.contains("/export")) return EventType.DOWNLOAD;
|
||||
if (uri.contains("/print")) return EventType.PRINT;
|
||||
if (uri.contains("/download") || uri.contains("/export")) {
|
||||
return EventType.DOWNLOAD;
|
||||
}
|
||||
if (uri.contains("/print")) {
|
||||
return EventType.PRINT;
|
||||
}
|
||||
|
||||
// 일반 CRUD
|
||||
return switch (method) {
|
||||
@@ -97,7 +103,9 @@ public class ApiLogFunction {
|
||||
// JSON Body 읽기
|
||||
public static String getBodyData(ContentCachingRequestWrapper request) {
|
||||
byte[] buf = request.getContentAsByteArray();
|
||||
if (buf.length == 0) return null;
|
||||
if (buf.length == 0) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return new String(buf, request.getCharacterEncoding());
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
|
||||
@@ -50,7 +50,6 @@ public class MapSheetMngApiController {
|
||||
return ApiResponseDto.createOK(mapSheetMngService.getFolderAll(srchDto));
|
||||
}
|
||||
|
||||
|
||||
@Operation(summary = "지정폴더내 파일목록 조회", description = "지정폴더내 파일목록 조회")
|
||||
@ApiResponses(
|
||||
value = {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.kamco.cd.kamcoback.mapsheet.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonView;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
@@ -43,7 +42,6 @@ public class FileDto {
|
||||
@Schema(description = "파일종료위치", example = "100")
|
||||
@NotNull
|
||||
private Integer endPos;
|
||||
|
||||
}
|
||||
|
||||
@Getter
|
||||
|
||||
@@ -183,7 +183,6 @@ public class MapSheetMngService {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return new FilesDto(dirPath, fileTotCnt, fileTotSize, files);
|
||||
}
|
||||
|
||||
@@ -207,7 +206,6 @@ public class MapSheetMngService {
|
||||
int fileTotCnt = 0;
|
||||
long fileTotSize = 0;
|
||||
|
||||
|
||||
try (Stream<Path> stream = Files.walk(startPath, maxDepth)) {
|
||||
|
||||
fileDtoList =
|
||||
@@ -244,8 +242,6 @@ public class MapSheetMngService {
|
||||
fileTotCnt = fileDtoList.size();
|
||||
fileTotSize = fileDtoList.stream().mapToLong(FileDto.Basic::getFileSize).sum();
|
||||
|
||||
|
||||
|
||||
} catch (IOException e) {
|
||||
System.err.println("파일 I/O 오류 발생: " + e.getMessage());
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.kamco.cd.kamcoback.members.service;
|
||||
|
||||
import com.kamco.cd.kamcoback.config.BCryptSaltGenerator;
|
||||
import com.kamco.cd.kamcoback.auth.BCryptSaltGenerator;
|
||||
import com.kamco.cd.kamcoback.members.dto.MembersDto;
|
||||
import com.kamco.cd.kamcoback.postgres.core.MembersCoreService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.kamco.cd.kamcoback.members.service;
|
||||
import com.kamco.cd.kamcoback.auth.CustomUserDetails;
|
||||
import com.kamco.cd.kamcoback.postgres.entity.MemberEntity;
|
||||
import com.kamco.cd.kamcoback.postgres.repository.members.MembersRepository;
|
||||
import java.util.UUID;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
@@ -17,9 +18,10 @@ public class MemberDetailsService implements UserDetailsService {
|
||||
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
UUID uuid = UUID.fromString(username);
|
||||
MemberEntity member =
|
||||
membersRepository
|
||||
.findByEmployeeNo(username)
|
||||
.findByUUID(uuid)
|
||||
.orElseThrow(() -> new UsernameNotFoundException("USER NOT FOUND"));
|
||||
|
||||
return new CustomUserDetails(member);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.kamco.cd.kamcoback.members.service;
|
||||
|
||||
import com.kamco.cd.kamcoback.auth.BCryptSaltGenerator;
|
||||
import com.kamco.cd.kamcoback.common.exception.CustomApiException;
|
||||
import com.kamco.cd.kamcoback.config.BCryptSaltGenerator;
|
||||
import com.kamco.cd.kamcoback.members.dto.MembersDto;
|
||||
import com.kamco.cd.kamcoback.members.dto.MembersDto.Basic;
|
||||
import com.kamco.cd.kamcoback.postgres.core.MembersCoreService;
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package com.kamco.cd.kamcoback.postgres.core;
|
||||
|
||||
import com.kamco.cd.kamcoback.config.BCryptSaltGenerator;
|
||||
import com.kamco.cd.kamcoback.auth.BCryptSaltGenerator;
|
||||
import com.kamco.cd.kamcoback.members.dto.MembersDto;
|
||||
import com.kamco.cd.kamcoback.members.exception.MemberException;
|
||||
import com.kamco.cd.kamcoback.members.exception.MemberException.MemberNotFoundException;
|
||||
import com.kamco.cd.kamcoback.postgres.entity.MemberArchivedEntity;
|
||||
import com.kamco.cd.kamcoback.postgres.entity.MemberArchivedEntityId;
|
||||
import com.kamco.cd.kamcoback.postgres.entity.MemberEntity;
|
||||
import com.kamco.cd.kamcoback.postgres.entity.MemberRoleEntity;
|
||||
import com.kamco.cd.kamcoback.postgres.entity.MemberRoleEntityId;
|
||||
@@ -161,9 +162,12 @@ public class MembersCoreService {
|
||||
.findByUUID(statusDto.getUuid())
|
||||
.orElseThrow(() -> new MemberNotFoundException());
|
||||
|
||||
MemberArchivedEntityId memberArchivedEntityId = new MemberArchivedEntityId();
|
||||
memberArchivedEntityId.setUserId(memberEntity.getId());
|
||||
memberArchivedEntityId.setUuid(memberEntity.getUuid());
|
||||
|
||||
MemberArchivedEntity memberArchivedEntity = new MemberArchivedEntity();
|
||||
memberArchivedEntity.setUuid(memberEntity.getUuid());
|
||||
memberArchivedEntity.setId(memberEntity.getId());
|
||||
memberArchivedEntity.setId(memberArchivedEntityId);
|
||||
memberArchivedEntity.setEmployeeNo(memberEntity.getEmployeeNo());
|
||||
memberArchivedEntity.setName(memberEntity.getName());
|
||||
memberArchivedEntity.setPassword(memberEntity.getPassword());
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
package com.kamco.cd.kamcoback.postgres.entity;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.EmbeddedId;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.UUID;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.hibernate.annotations.ColumnDefault;
|
||||
@@ -18,12 +17,7 @@ import org.hibernate.annotations.ColumnDefault;
|
||||
@Table(name = "tb_member_archived")
|
||||
public class MemberArchivedEntity {
|
||||
|
||||
@Id
|
||||
@Column(name = "uuid", nullable = false)
|
||||
private UUID uuid;
|
||||
|
||||
@Column(name = "id", nullable = false)
|
||||
private Long id;
|
||||
@EmbeddedId private MemberArchivedEntityId id;
|
||||
|
||||
@Size(max = 50)
|
||||
@Column(name = "employee_no", length = 50)
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.kamco.cd.kamcoback.postgres.entity;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.hibernate.Hibernate;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Embeddable
|
||||
public class MemberArchivedEntityId implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -7102800377481389036L;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "user_id", nullable = false)
|
||||
private Long userId;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "uuid", nullable = false)
|
||||
private UUID uuid;
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o)) {
|
||||
return false;
|
||||
}
|
||||
MemberArchivedEntityId entity = (MemberArchivedEntityId) o;
|
||||
return Objects.equals(this.userId, entity.userId) && Objects.equals(this.uuid, entity.uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(userId, uuid);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user