diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000..7e585e6 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,6 @@ +# 포맷만 바꾼 커밋 목록. git blame에서 제외해 실제 코드 작성자를 추적할 수 있게 한다. +# GitHub은 이 파일을 자동으로 인식한다. +# 로컬 적용: git config blame.ignoreRevsFile .git-blame-ignore-revs +# +# Squash Merge가 기본이라 브랜치 커밋 해시는 main에 남지 않는다. +# 포맷 PR을 머지한 뒤, main에 생긴 squash 커밋 해시를 여기에 추가한다. diff --git a/api/common-api/src/main/java/kr/ac/kookmin/stream/CursorSliceResponse.java b/api/common-api/src/main/java/kr/ac/kookmin/stream/CursorSliceResponse.java index 21e6277..b56486e 100644 --- a/api/common-api/src/main/java/kr/ac/kookmin/stream/CursorSliceResponse.java +++ b/api/common-api/src/main/java/kr/ac/kookmin/stream/CursorSliceResponse.java @@ -6,10 +6,6 @@ public record CursorSliceResponse(List content, boolean hasNext, Long nextCursor) { public static CursorSliceResponse from(CursorSliceResult result) { - return new CursorSliceResponse<>( - result.content(), - result.hasNext(), - result.nextCursor() - ); + return new CursorSliceResponse<>(result.content(), result.hasNext(), result.nextCursor()); } } diff --git a/bootstrap/src/main/java/kr/ac/kookmin/stream/StreamServerApplication.java b/bootstrap/src/main/java/kr/ac/kookmin/stream/StreamServerApplication.java index 7e19e23..8ba8c2a 100644 --- a/bootstrap/src/main/java/kr/ac/kookmin/stream/StreamServerApplication.java +++ b/bootstrap/src/main/java/kr/ac/kookmin/stream/StreamServerApplication.java @@ -11,5 +11,4 @@ public class StreamServerApplication { public static void main(String[] args) { SpringApplication.run(StreamServerApplication.class, args); } - } diff --git a/bootstrap/src/test/java/kr/ac/kookmin/stream/ModularityTests.java b/bootstrap/src/test/java/kr/ac/kookmin/stream/ModularityTests.java index 4e08dea..a34c99f 100644 --- a/bootstrap/src/test/java/kr/ac/kookmin/stream/ModularityTests.java +++ b/bootstrap/src/test/java/kr/ac/kookmin/stream/ModularityTests.java @@ -17,5 +17,4 @@ void verify() { void writeDocs() { new Documenter(modules).writeDocumentation(); } - } diff --git a/bootstrap/src/test/java/kr/ac/kookmin/stream/StreamServerApplicationTests.java b/bootstrap/src/test/java/kr/ac/kookmin/stream/StreamServerApplicationTests.java index 2935634..e28f8d2 100644 --- a/bootstrap/src/test/java/kr/ac/kookmin/stream/StreamServerApplicationTests.java +++ b/bootstrap/src/test/java/kr/ac/kookmin/stream/StreamServerApplicationTests.java @@ -9,5 +9,4 @@ class StreamServerApplicationTests { @Test void contextLoads() { } - } diff --git a/build.gradle.kts b/build.gradle.kts index 065da77..3b4dc5f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,8 @@ +import com.diffplug.gradle.spotless.SpotlessExtension + plugins { alias(libs.plugins.springBoot) apply false + alias(libs.plugins.spotless) apply false } allprojects { @@ -13,6 +16,7 @@ allprojects { subprojects { apply(plugin = "java") + apply(plugin = "com.diffplug.spotless") // Java 21 (LTS) baseline — docs/conventions/architecture.md §1 extensions.configure { @@ -33,4 +37,13 @@ subprojects { tasks.withType { useJUnitPlatform() } + + extensions.configure { + java { + removeUnusedImports() + importOrder("", "\\#") + // IntelliJ 포맷 결과를 CI에서 그대로 강제하기 위한 Eclipse JDT 프로파일 + eclipse().configFile(rootProject.file("config/eclipse-formatter.xml")) + } + } } diff --git a/config/eclipse-formatter.xml b/config/eclipse-formatter.xml new file mode 100644 index 0000000..ec8ee12 --- /dev/null +++ b/config/eclipse-formatter.xml @@ -0,0 +1,237 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/common/src/main/java/kr/ac/kookmin/stream/common/BusinessException.java b/core/common/src/main/java/kr/ac/kookmin/stream/common/BusinessException.java index fbc78a8..f37734f 100644 --- a/core/common/src/main/java/kr/ac/kookmin/stream/common/BusinessException.java +++ b/core/common/src/main/java/kr/ac/kookmin/stream/common/BusinessException.java @@ -13,7 +13,10 @@ public BusinessException(ErrorCode errorCode) { } public BusinessException(ErrorCode errorCode, Object... formatArgs) { - super(formatArgs.length == 0 ? errorCode.message() : errorCode.message().formatted(formatArgs)); + super( + formatArgs.length == 0 + ? errorCode.message() + : errorCode.message().formatted(formatArgs)); this.errorCode = errorCode; } } diff --git a/core/common/src/main/java/kr/ac/kookmin/stream/common/CouncilDepartment.java b/core/common/src/main/java/kr/ac/kookmin/stream/common/CouncilDepartment.java index 2a61785..dc434ca 100644 --- a/core/common/src/main/java/kr/ac/kookmin/stream/common/CouncilDepartment.java +++ b/core/common/src/main/java/kr/ac/kookmin/stream/common/CouncilDepartment.java @@ -1,15 +1,13 @@ package kr.ac.kookmin.stream.common; -/** - * 학생회 부서. ADMIN에게만 부여되며, member 도메인의 학부(Department)와는 다른 개념이다. - */ +/** 학생회 부서. ADMIN에게만 부여되며, member 도메인의 학부(Department)와는 다른 개념이다. */ public enum CouncilDepartment { - PRESIDENCY, // 회장단 - EXECUTIVE, // 집행부 - GENERAL_AFFAIRS, // 총무부 - PLANNING, // 기획부 - PR, // 홍보부 - MEDIA, // 미디어부 - WELFARE, // 복지부 - COMMUNICATION // 소통부 + PRESIDENCY, // 회장단 + EXECUTIVE, // 집행부 + GENERAL_AFFAIRS, // 총무부 + PLANNING, // 기획부 + PR, // 홍보부 + MEDIA, // 미디어부 + WELFARE, // 복지부 + COMMUNICATION // 소통부 } diff --git a/core/common/src/main/java/kr/ac/kookmin/stream/common/CursorSliceResult.java b/core/common/src/main/java/kr/ac/kookmin/stream/common/CursorSliceResult.java index 4bd0d3d..c7b763f 100644 --- a/core/common/src/main/java/kr/ac/kookmin/stream/common/CursorSliceResult.java +++ b/core/common/src/main/java/kr/ac/kookmin/stream/common/CursorSliceResult.java @@ -2,4 +2,5 @@ import java.util.List; -public record CursorSliceResult(List content, boolean hasNext, Long nextCursor) {} +public record CursorSliceResult(List content, boolean hasNext, Long nextCursor) { +} diff --git a/core/common/src/main/java/kr/ac/kookmin/stream/common/ErrorCode.java b/core/common/src/main/java/kr/ac/kookmin/stream/common/ErrorCode.java index 5daba0c..a20f719 100644 --- a/core/common/src/main/java/kr/ac/kookmin/stream/common/ErrorCode.java +++ b/core/common/src/main/java/kr/ac/kookmin/stream/common/ErrorCode.java @@ -1,7 +1,10 @@ package kr.ac.kookmin.stream.common; public interface ErrorCode { + String name(); + int status(); + String message(); } diff --git a/core/common/src/main/java/kr/ac/kookmin/stream/common/PrincipalProvider.java b/core/common/src/main/java/kr/ac/kookmin/stream/common/PrincipalProvider.java index ae97287..41db181 100644 --- a/core/common/src/main/java/kr/ac/kookmin/stream/common/PrincipalProvider.java +++ b/core/common/src/main/java/kr/ac/kookmin/stream/common/PrincipalProvider.java @@ -3,7 +3,10 @@ import java.util.Set; public interface PrincipalProvider { + Long userId(); + Set roles(); + Set councilDepartments(); } diff --git a/core/domain/member/src/main/java/kr/ac/kookmin/stream/member/Member.java b/core/domain/member/src/main/java/kr/ac/kookmin/stream/member/Member.java index c6d54f9..001a28e 100644 --- a/core/domain/member/src/main/java/kr/ac/kookmin/stream/member/Member.java +++ b/core/domain/member/src/main/java/kr/ac/kookmin/stream/member/Member.java @@ -1,7 +1,4 @@ package kr.ac.kookmin.stream.member; -public record Member( - Long id, - String studentNo, - String name -) {} +public record Member(Long id, String studentNo, String name) { +} diff --git a/core/domain/member/src/main/java/kr/ac/kookmin/stream/member/MemberRepository.java b/core/domain/member/src/main/java/kr/ac/kookmin/stream/member/MemberRepository.java index 3f98642..5ea476b 100644 --- a/core/domain/member/src/main/java/kr/ac/kookmin/stream/member/MemberRepository.java +++ b/core/domain/member/src/main/java/kr/ac/kookmin/stream/member/MemberRepository.java @@ -3,5 +3,6 @@ import java.util.Optional; public interface MemberRepository { + Optional findById(Long id); } diff --git a/core/domain/member/src/main/java/kr/ac/kookmin/stream/member/MemberService.java b/core/domain/member/src/main/java/kr/ac/kookmin/stream/member/MemberService.java index e09a75e..e91bbde 100644 --- a/core/domain/member/src/main/java/kr/ac/kookmin/stream/member/MemberService.java +++ b/core/domain/member/src/main/java/kr/ac/kookmin/stream/member/MemberService.java @@ -1,5 +1,6 @@ package kr.ac.kookmin.stream.member; public interface MemberService { + Member getById(Long id); } diff --git a/core/domain/member/src/main/java/kr/ac/kookmin/stream/member/internal/MemberServiceImpl.java b/core/domain/member/src/main/java/kr/ac/kookmin/stream/member/internal/MemberServiceImpl.java index 21ae572..672c4c9 100644 --- a/core/domain/member/src/main/java/kr/ac/kookmin/stream/member/internal/MemberServiceImpl.java +++ b/core/domain/member/src/main/java/kr/ac/kookmin/stream/member/internal/MemberServiceImpl.java @@ -15,7 +15,8 @@ class MemberServiceImpl implements MemberService { private final MemberRepository memberRepository; public Member getById(Long id) { - return memberRepository.findById(id) - .orElseThrow(() -> new BusinessException(MemberErrorCode.MEMBER_NOT_FOUND)); + return memberRepository + .findById(id) + .orElseThrow(() -> new BusinessException(MemberErrorCode.MEMBER_NOT_FOUND)); } } diff --git a/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/config/PublicEndpoints.java b/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/config/PublicEndpoints.java index cd5a808..62ddfa0 100644 --- a/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/config/PublicEndpoints.java +++ b/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/config/PublicEndpoints.java @@ -8,25 +8,16 @@ import org.springframework.web.util.pattern.PathPattern; import org.springframework.web.util.pattern.PathPatternParser; -/** - * 인증 없이 여는 엔드포인트. SecurityConfig의 permitAll 대상이며 용도별로 묶어 관리한다. - */ +/** 인증 없이 여는 엔드포인트. SecurityConfig의 permitAll 대상이며 용도별로 묶어 관리한다. */ @Getter @Accessors(fluent = true) public enum PublicEndpoints { - HEALTH_CHECK(List.of( - "/actuator/health" - )), - SWAGGER(List.of( - "/swagger-ui/**", - "/swagger-ui.html", - "/v3/api-docs/**" - )); + HEALTH_CHECK(List.of("/actuator/health")), + SWAGGER(List.of("/swagger-ui/**", "/swagger-ui.html", "/v3/api-docs/**")); private static final List ALL_PATH_PATTERNS = Arrays.stream(values()) - .flatMap(endpoints -> endpoints.pathPatterns.stream()) - .toList(); + .flatMap(endpoints -> endpoints.pathPatterns.stream()).toList(); private final List patterns; private final List pathPatterns; @@ -39,13 +30,13 @@ public enum PublicEndpoints { public static String[] allPatterns() { return Arrays.stream(values()) - .flatMap(endpoints -> endpoints.patterns.stream()) - .toArray(String[]::new); + .flatMap(endpoints -> endpoints.patterns.stream()) + .toArray(String[]::new); } public static boolean isPublic(String path) { PathContainer pathContainer = PathContainer.parsePath(path); return ALL_PATH_PATTERNS.stream() - .anyMatch(pathPattern -> pathPattern.matches(pathContainer)); + .anyMatch(pathPattern -> pathPattern.matches(pathContainer)); } } diff --git a/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/config/SecurityConfig.java b/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/config/SecurityConfig.java index e3fa55b..7068307 100644 --- a/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/config/SecurityConfig.java +++ b/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/config/SecurityConfig.java @@ -26,21 +26,26 @@ public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - return http - .csrf(AbstractHttpConfigurer::disable) - .formLogin(AbstractHttpConfigurer::disable) - .httpBasic(AbstractHttpConfigurer::disable) - .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) - .authorizeHttpRequests(request -> request - .requestMatchers(PublicEndpoints.allPatterns()).permitAll() - .requestMatchers("/v1/admin/**").hasAuthority(Role.ADMIN.name()) - .requestMatchers("/v1/app/**").hasAuthority(Role.STUDENT.name()) - .anyRequest().authenticated()) - .exceptionHandling(exception -> exception - .authenticationEntryPoint(authenticationEntryPoint) - .accessDeniedHandler(accessDeniedHandler)) - // ExceptionTranslationFilter 뒤에 두어야 필터가 던진 인증 예외가 EntryPoint로 넘어간다 - .addFilterBefore(JwtAuthFilter.of(jwtProvider), AuthorizationFilter.class) - .build(); + return http.csrf(AbstractHttpConfigurer::disable) + .formLogin(AbstractHttpConfigurer::disable) + .httpBasic(AbstractHttpConfigurer::disable) + .sessionManagement( + session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .authorizeHttpRequests( + request -> request.requestMatchers(PublicEndpoints.allPatterns()) + .permitAll() + .requestMatchers("/v1/admin/**") + .hasAuthority(Role.ADMIN.name()) + .requestMatchers("/v1/app/**") + .hasAuthority(Role.STUDENT.name()) + .anyRequest() + .authenticated()) + .exceptionHandling( + exception -> exception + .authenticationEntryPoint(authenticationEntryPoint) + .accessDeniedHandler(accessDeniedHandler)) + // ExceptionTranslationFilter 뒤에 두어야 필터가 던진 인증 예외가 EntryPoint로 넘어간다 + .addFilterBefore(JwtAuthFilter.of(jwtProvider), AuthorizationFilter.class) + .build(); } } diff --git a/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/handler/RestAccessDeniedHandler.java b/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/handler/RestAccessDeniedHandler.java index baa1876..501d0c9 100644 --- a/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/handler/RestAccessDeniedHandler.java +++ b/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/handler/RestAccessDeniedHandler.java @@ -11,9 +11,7 @@ import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerExceptionResolver; -/** - * 인증은 됐으나 권한이 없는 요청(403)을 공통 에러 응답으로 내보낸다. - */ +/** 인증은 됐으나 권한이 없는 요청(403)을 공통 에러 응답으로 내보낸다. */ @Component @RequiredArgsConstructor public class RestAccessDeniedHandler implements AccessDeniedHandler { @@ -23,11 +21,13 @@ public class RestAccessDeniedHandler implements AccessDeniedHandler { @Override public void handle( - HttpServletRequest request, - HttpServletResponse response, - AccessDeniedException accessDeniedException - ) { + HttpServletRequest request, + HttpServletResponse response, + AccessDeniedException accessDeniedException) { handlerExceptionResolver.resolveException( - request, response, null, new BusinessException(CommonErrorCode.FORBIDDEN)); + request, + response, + null, + new BusinessException(CommonErrorCode.FORBIDDEN)); } } diff --git a/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/handler/RestAuthenticationEntryPoint.java b/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/handler/RestAuthenticationEntryPoint.java index e5181b5..d7e9c1b 100644 --- a/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/handler/RestAuthenticationEntryPoint.java +++ b/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/handler/RestAuthenticationEntryPoint.java @@ -11,9 +11,7 @@ import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerExceptionResolver; -/** - * 인증되지 않은 요청(401). 로그인 페이지로 리다이렉트하는 기본 동작 대신 공통 에러 응답으로 내보낸다. - */ +/** 인증되지 않은 요청(401). 로그인 페이지로 리다이렉트하는 기본 동작 대신 공통 에러 응답으로 내보낸다. */ @Component @RequiredArgsConstructor public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint { @@ -23,11 +21,13 @@ public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint { @Override public void commence( - HttpServletRequest request, - HttpServletResponse response, - AuthenticationException authenticationException - ) { + HttpServletRequest request, + HttpServletResponse response, + AuthenticationException authenticationException) { handlerExceptionResolver.resolveException( - request, response, null, new BusinessException(CommonErrorCode.UNAUTHORIZED)); + request, + response, + null, + new BusinessException(CommonErrorCode.UNAUTHORIZED)); } } diff --git a/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/jwt/InvalidTokenException.java b/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/jwt/InvalidTokenException.java index ec4ab65..f21ba1e 100644 --- a/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/jwt/InvalidTokenException.java +++ b/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/jwt/InvalidTokenException.java @@ -3,8 +3,8 @@ import org.springframework.security.core.AuthenticationException; /** - * 토큰이 만료됐거나 서명·형식이 올바르지 않을 때. ExceptionTranslationFilter가 잡아 - * SecurityConfig에 설정된 AuthenticationEntryPoint로 넘긴다. + * 토큰이 만료됐거나 서명·형식이 올바르지 않을 때. ExceptionTranslationFilter가 잡아 SecurityConfig에 설정된 + * AuthenticationEntryPoint로 넘긴다. */ public class InvalidTokenException extends AuthenticationException { diff --git a/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/jwt/JwtAuthFilter.java b/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/jwt/JwtAuthFilter.java index 8bf8a8b..0a0dd95 100644 --- a/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/jwt/JwtAuthFilter.java +++ b/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/jwt/JwtAuthFilter.java @@ -24,13 +24,14 @@ public static JwtAuthFilter of(JwtProvider jwtProvider) { @Override protected void doFilterInternal( - HttpServletRequest request, - HttpServletResponse response, - FilterChain filterChain - ) throws ServletException, IOException { + HttpServletRequest request, + HttpServletResponse response, + FilterChain filterChain) + throws ServletException, IOException { String token = resolveToken(request); if (token != null) { - SecurityContextHolder.getContext().setAuthentication(UserAuthentication.from(jwtProvider.parse(token))); + SecurityContextHolder.getContext() + .setAuthentication(UserAuthentication.from(jwtProvider.parse(token))); } filterChain.doFilter(request, response); } diff --git a/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/jwt/JwtPayload.java b/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/jwt/JwtPayload.java index 41af91b..9161366 100644 --- a/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/jwt/JwtPayload.java +++ b/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/jwt/JwtPayload.java @@ -4,8 +4,5 @@ import kr.ac.kookmin.stream.common.CouncilDepartment; import kr.ac.kookmin.stream.common.Role; -public record JwtPayload( - Long userId, - Set roles, - Set councilDepartments -) {} +public record JwtPayload(Long userId, Set roles, Set councilDepartments) { +} diff --git a/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/jwt/JwtProperties.java b/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/jwt/JwtProperties.java index 530b678..ef47f7e 100644 --- a/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/jwt/JwtProperties.java +++ b/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/jwt/JwtProperties.java @@ -3,8 +3,5 @@ import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = "jwt") -public record JwtProperties( - String secretKey, - String issuer, - long accessTokenExpiry -) {} +public record JwtProperties(String secretKey, String issuer, long accessTokenExpiry) { +} diff --git a/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/jwt/JwtProvider.java b/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/jwt/JwtProvider.java index a30e0cb..1f76d3b 100644 --- a/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/jwt/JwtProvider.java +++ b/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/jwt/JwtProvider.java @@ -30,35 +30,37 @@ public JwtProvider(JwtProperties jwtProperties) { this.secretKey = Keys.hmacShaKeyFor(jwtProperties.secretKey().getBytes(StandardCharsets.UTF_8)); } - public String generateAccessToken(Long userId, Set roles, Set councilDepartments) { + public String generateAccessToken( + Long userId, + Set roles, + Set councilDepartments) { Date issuedAt = new Date(); Date expiration = new Date(issuedAt.getTime() + jwtProperties.accessTokenExpiry()); return Jwts.builder() - .issuer(jwtProperties.issuer()) - .subject(String.valueOf(userId)) - .issuedAt(issuedAt) - .expiration(expiration) - .claim(ROLES_CLAIM, names(roles)) - .claim(COUNCIL_CLAIM, names(councilDepartments)) - .signWith(secretKey) - .compact(); + .issuer(jwtProperties.issuer()) + .subject(String.valueOf(userId)) + .issuedAt(issuedAt) + .expiration(expiration) + .claim(ROLES_CLAIM, names(roles)) + .claim(COUNCIL_CLAIM, names(councilDepartments)) + .signWith(secretKey) + .compact(); } public JwtPayload parse(String token) { try { Claims claims = Jwts.parser() - .verifyWith(secretKey) - .requireIssuer(jwtProperties.issuer()) - .build() - .parseSignedClaims(token) - .getPayload(); + .verifyWith(secretKey) + .requireIssuer(jwtProperties.issuer()) + .build() + .parseSignedClaims(token) + .getPayload(); return new JwtPayload( - Long.valueOf(claims.getSubject()), - toEnumSet(claims, ROLES_CLAIM, Role.class), - toEnumSet(claims, COUNCIL_CLAIM, CouncilDepartment.class) - ); + Long.valueOf(claims.getSubject()), + toEnumSet(claims, ROLES_CLAIM, Role.class), + toEnumSet(claims, COUNCIL_CLAIM, CouncilDepartment.class)); } catch (JwtException | IllegalArgumentException e) { throw new InvalidTokenException(e); } @@ -74,7 +76,7 @@ private > Set toEnumSet(Claims claims, String claimName, Cl return EnumSet.noneOf(type); } return values.stream() - .map(value -> Enum.valueOf(type, String.valueOf(value))) - .collect(Collectors.toCollection(() -> EnumSet.noneOf(type))); + .map(value -> Enum.valueOf(type, String.valueOf(value))) + .collect(Collectors.toCollection(() -> EnumSet.noneOf(type))); } } diff --git a/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/jwt/UserAuthentication.java b/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/jwt/UserAuthentication.java index 5c631ff..ba4f541 100644 --- a/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/jwt/UserAuthentication.java +++ b/gateway/auth/src/main/java/kr/ac/kookmin/stream/security/jwt/UserAuthentication.java @@ -46,9 +46,9 @@ public Object getCredentials() { private static Collection toAuthorities(JwtPayload payload) { return Stream.concat( payload.roles().stream().map(Role::name), - payload.councilDepartments().stream().map(department -> COUNCIL_AUTHORITY_PREFIX + department.name()) - ) - .map(SimpleGrantedAuthority::new) - .toList(); + payload.councilDepartments().stream() + .map(department -> COUNCIL_AUTHORITY_PREFIX + department.name())) + .map(SimpleGrantedAuthority::new) + .toList(); } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e30ebbe..d9e4564 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,6 +5,7 @@ jjwtVersion = "0.12.6" archunitVersion = "1.3.0" junitBomVersion = "6.0.0" lombokVersion = "1.18.36" +spotlessVersion = "8.10.0" [libraries] # BOMs — import as `platform(...)` in modules that don't apply the Boot plugin directly @@ -53,3 +54,4 @@ junitPlatformLauncher = { module = "org.junit.platform:junit-platform-launcher" [plugins] springBoot = { id = "org.springframework.boot", version.ref = "springBootVersion" } +spotless = { id = "com.diffplug.spotless", version.ref = "spotlessVersion" } diff --git a/infrastructure/db/src/main/java/kr/ac/kookmin/stream/db/member/MemberJpaRepository.java b/infrastructure/db/src/main/java/kr/ac/kookmin/stream/db/member/MemberJpaRepository.java index 13d7092..71a2265 100644 --- a/infrastructure/db/src/main/java/kr/ac/kookmin/stream/db/member/MemberJpaRepository.java +++ b/infrastructure/db/src/main/java/kr/ac/kookmin/stream/db/member/MemberJpaRepository.java @@ -4,5 +4,6 @@ import org.springframework.data.jpa.repository.JpaRepository; public interface MemberJpaRepository extends JpaRepository { + Optional findByIdAndDeletedAtIsNull(Long id); }