Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
442fb47
merge 성공
SungMinju Jan 12, 2026
8220906
merge 성공
SungMinju Jan 12, 2026
5d487f8
[feature]: user 엔티티 설계
SungMinju Jan 12, 2026
f31624d
merge 오류 수정
SungMinju Jan 17, 2026
af53b10
[Feature] JWT 생성
SungMinju Jan 19, 2026
f84b769
[FIX] 오류 수정
SungMinju Jan 19, 2026
37be66f
[FIX] User 엔티티 설정 수정
SungMinju Jan 19, 2026
8307545
[Feature] User 컨버터 구현
SungMinju Jan 19, 2026
c89395e
[FIX] 오류 수정
SungMinju Jan 19, 2026
e271230
[FIX] 설정값 수정
SungMinju Jan 19, 2026
16e29ee
[Refactor] 코드 구조 변경
SungMinju Jan 19, 2026
dcc3412
[Refactor] 코드 구조 변경
SungMinju Jan 19, 2026
107a30c
[Fix] 오류 응답 구조 변경으로 인한 오류 수정
SungMinju Jan 19, 2026
9943956
[Feat] 약관동의 추가
SungMinju Jan 29, 2026
32482b8
[Fix] 약관동의 dto 추가
SungMinju Jan 29, 2026
8d19b22
[Fix] 약관동의 dto 추가
SungMinju Jan 29, 2026
c849010
[Fix] refresh token dto 삭제
SungMinju Jan 29, 2026
4acbc09
[Fix] 유저 컨버터 코드 추가
SungMinju Jan 29, 2026
af9b5a4
[Refactor] Refresh Token 전달 방식 변경
SungMinju Jan 29, 2026
0239089
[Feat] Auth 로직 구현
SungMinju Jan 29, 2026
dfc0983
[Feat] Refresh Token 쿠키 저장 로직 구현
SungMinju Jan 29, 2026
d3d6f19
[Style] import 구문 수정
SungMinju Feb 3, 2026
ab72c3a
[Feat] 코드 추가
SungMinju Feb 3, 2026
af75e6c
[Feat] 오류메세지 추가
SungMinju Feb 3, 2026
9578c2d
[Feat] 회원가입/로그인 코드 구현
SungMinju Feb 3, 2026
a0410ad
Merge 성공
SungMinju Feb 3, 2026
379112a
Merge 과정 중 코드 생략 오류 수정
SungMinju Feb 3, 2026
6e49016
[Chore] BaseEntity import 경로 수정
SungMinju Feb 3, 2026
e4604fa
[Fix] cors 코드 수정
SungMinju Feb 3, 2026
09e7154
[Fix] cors 코드 수정
SungMinju Feb 3, 2026
2b5ff86
[Feat] 에러상태 코드 추가
SungMinju Feb 3, 2026
4316265
[Feat] S3 이미지 설정 코드 추가
SungMinju Feb 3, 2026
f360a57
[Fix] 프로필 이미지 변수 제거
SungMinju Feb 3, 2026
6d46ef5
[Fix] 회원 정보 수정 API 분리
SungMinju Feb 3, 2026
5a4ff0c
[Fix] 코드래빗 리뷰 수정사항 반영
SungMinju Feb 4, 2026
cb5902f
[Feat] 회원가입 시 Term(약관) 엔티티 저장 로직 추가
SungMinju Feb 4, 2026
dcbac96
[Refactor] @PasswordMatch 검증기 범용성 확대
SungMinju Feb 4, 2026
ac07dbc
[Refactor] @PasswordMatch 검증기 범용성 확대
SungMinju Feb 4, 2026
22a93d2
[Feat] 회원가입 시 Term(약관) 엔티티 저장 로직 추가
SungMinju Feb 4, 2026
931b6ac
[Fix] 프로필 업데이트 롤백 시 S3 파일 정리 로직 추가
SungMinju Feb 4, 2026
6f6b2a5
[Fix] 로그아웃 시 리프레시 토큰 쿠키 삭제 처리
SungMinju Feb 4, 2026
592b436
[Refactor] @PasswordMatch 검증기 범용성 확대
SungMinju Feb 4, 2026
dc56c8c
[Refactor] @PasswordMatch 검증기 범용성 확대
SungMinju Feb 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,15 @@ dependencies {
testImplementation 'com.h2database:h2'

//security
//implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-security'

// JWT
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'

// oauth
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
Comment on lines 35 to +44
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

io.jsonwebtoken jjwt latest version 2025 2026

💡 Result:

As of February 3, 2026, the latest JJWT (io.jsonwebtoken) release is 0.13.0. [1][2]

  • Latest artifacts (all at 0.13.0): jjwt-api, jjwt-impl, jjwt-jackson, jjwt-gson, etc. [2]
  • Latest BOM: io.jsonwebtoken:jjwt-bom:0.13.0 (Aug 20, 2025). [3]

Gradle (recommended via BOM):

dependencies {
  implementation platform("io.jsonwebtoken:jjwt-bom:0.13.0")
  implementation "io.jsonwebtoken:jjwt-api"
  runtimeOnly  "io.jsonwebtoken:jjwt-impl"
  runtimeOnly  "io.jsonwebtoken:jjwt-jackson" // or jjwt-gson
}

Maven:

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>io.jsonwebtoken</groupId>
      <artifactId>jjwt-bom</artifactId>
      <version>0.13.0</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

Sources

[1] GitHub Releases – jwtk/jjwt (shows latest release 0.13.0)
[2] javadoc.io – io.jsonwebtoken artifacts (shows latest version 0.13.0)
[3] mvnrepository – io.jsonwebtoken:jjwt-bom (shows latest 0.13.0, dated Aug 20, 2025)


🌐 Web query:

CVE io.jsonwebtoken jjwt 0.11.5 security vulnerabilities

💡 Result:

For io.jsonwebtoken (jjwt) 0.11.5, the main publicly tracked issue you’ll see in scanners is:

  • CVE-2024-31033 (aka GHSA-r65j-6h5f-4f92)“ignored characters” in key handling may cause users to overestimate signing-key strength (reported as affecting JJWT “through 0.12.5,” i.e., it includes 0.11.5). Status: disputed by the JJWT vendor in the NVD record. [1]
    • GitHub’s advisory entry for this was withdrawn as disputed, but OSV still lists 0.11.5 among affected versions in the withdrawn GHSA record, which is why many tools still flag it. [2]
    • The JJWT project discusses this CVE report (and dispute context) in issue #930. [3]
    • Some advisory databases mark this as a false positive for io.jsonwebtoken:jjwt-impl. [4]

Are there other CVEs for jjwt 0.11.5?
From the sources above, CVE-2024-31033 is the one commonly associated with jjwt 0.11.5; other “JWT CVEs” you might encounter are typically for different libraries/products with similar names.

Practical guidance

  • If you’re being blocked purely by policy/scanner findings: document that CVE-2024-31033 is disputed/withdrawn and reference the vendor discussion. [1][2][3]
  • If you still want to reduce risk/noise: consider upgrading—Snyk currently reports jjwt-impl latest as 0.13.0 and “latest non vulnerable version” as 0.13.0. [5]

Sources:
[1] NVD – CVE-2024-31033 https://nvd.nist.gov/vuln/detail/CVE-2024-31033
[2] OSV – GHSA-r65j-6h5f-4f92 (withdrawn) https://osv.dev/vulnerability/GHSA-r65j-6h5f-4f92
[3] jwtk/jjwt Issue #930 jwtk/jjwt#930
[4] GitLab Advisory DB (marked false positive) https://advisories.gitlab.com/pkg/maven/io.jsonwebtoken/jjwt-impl/CVE-2024-31033/
[5] Snyk package page for jjwt-impl@0.11.5 https://security.snyk.io/package/maven/io.jsonwebtoken%3Ajjwt-impl/0.11.5


🌐 Web query:

jjwt library maintenance status current version

💡 Result:

  • Current version (latest release): 0.13.0, released Aug 20, 2025. [1][2][3]
  • Maintenance status: The project is not archived and is described by maintainers as “supported and maintained by a community of contributors.” [4] The latest release notes also indicate ongoing development is intended (future work planned for 0.14.0, with 0.13.x reserved for emergency fixes). [1]

Sources: [1] GitHub releases, [2] Javadoc.io latest versions, [3] MVNRepository release info, [4] GitHub README.


Upgrade JJWT to 0.13.0 and use the official Bill of Materials approach.

Version 0.11.5 is outdated (0.13.0 released Aug 20, 2025) and has a CVE entry (CVE-2024-31033, though disputed by the vendor). Use the official BOM pattern for proper version alignment across JJWT modules:

Recommended: use jjwt-bom for version management
     implementation 'org.springframework.boot:spring-boot-starter-security'
 
+    // JJWT (via BOM)
+    implementation platform('io.jsonwebtoken:jjwt-bom:0.13.0')
     implementation 'io.jsonwebtoken:jjwt-api'
     runtimeOnly 'io.jsonwebtoken:jjwt-impl'
     runtimeOnly 'io.jsonwebtoken:jjwt-jackson'
-
-    // oauth
🤖 Prompt for AI Agents
In `@build.gradle` around lines 35 - 44, Replace the hardcoded jjwt module
versions in build.gradle with the official BOM: add a platform import for
io.jsonwebtoken:jjwt-bom:0.13.0 and remove versions from the jjwt dependencies
(jjwt-api, jjwt-impl, jjwt-jackson) so they are resolved by the BOM; keep
jjwt-api as implementation and jjwt-impl/jjwt-jackson as runtimeOnly but do not
specify versions, and ensure the BOM line appears before those dependency
declarations.


//swagger
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.1'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
import com.eatsfine.eatsfine.domain.store.entity.Store;
import com.eatsfine.eatsfine.domain.storetable.entity.StoreTable;
import com.eatsfine.eatsfine.domain.user.entity.User;
import com.eatsfine.eatsfine.global.entity.BaseEntity;

import com.eatsfine.eatsfine.global.common.BaseEntity;
import jakarta.persistence.*;
import lombok.*;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.eatsfine.eatsfine.domain.businesshours.entity;

import com.eatsfine.eatsfine.domain.store.entity.Store;
import com.eatsfine.eatsfine.global.entity.BaseEntity;
import com.eatsfine.eatsfine.global.common.BaseEntity;
import jakarta.persistence.*;
import lombok.*;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public enum ImageErrorStatus implements BaseErrorCode {
_IMAGE_NOT_FOUND(HttpStatus.NOT_FOUND, "IMAGE404", "해당하는 이미지가 존재하지 않습니다."),
_INVALID_IMAGE_KEY(HttpStatus.BAD_REQUEST, "IMAGE4003", "유효하지 않은 이미지 키입니다."),
_INVALID_S3_DIRECTORY(HttpStatus.BAD_REQUEST, "IMAGE4004", "유효하지 않은 S3 디렉토리입니다."),
FILE_TOO_LARGE(HttpStatus.BAD_REQUEST, "IMAGE4005", "첨부하는 이미지의 크기가 너무 큽니다.")
;


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

import com.eatsfine.eatsfine.domain.menu.enums.MenuCategory;
import com.eatsfine.eatsfine.domain.store.entity.Store;
import com.eatsfine.eatsfine.global.entity.BaseEntity;

import com.eatsfine.eatsfine.global.common.BaseEntity;
import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.SQLDelete;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import com.eatsfine.eatsfine.domain.payment.enums.PaymentProvider;
import com.eatsfine.eatsfine.domain.payment.enums.PaymentStatus;
import com.eatsfine.eatsfine.domain.payment.enums.PaymentType;
import com.eatsfine.eatsfine.global.entity.BaseEntity;
import com.eatsfine.eatsfine.global.common.BaseEntity;
import jakarta.persistence.*;
import lombok.*;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
import com.eatsfine.eatsfine.domain.user.entity.User;
import com.eatsfine.eatsfine.global.apiPayload.code.status.ErrorStatus;
import com.eatsfine.eatsfine.global.apiPayload.exception.GeneralException;
import com.eatsfine.eatsfine.global.entity.BaseEntity;

import com.eatsfine.eatsfine.global.common.BaseEntity;
import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.BatchSize;
Expand Down Expand Up @@ -191,4 +192,4 @@ public void updateBasicInfo(StoreReqDto.StoreUpdateDto dto) {
}
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

import com.eatsfine.eatsfine.domain.storetable.enums.SeatsType;
import com.eatsfine.eatsfine.domain.table_layout.entity.TableLayout;
import com.eatsfine.eatsfine.global.entity.BaseEntity;

import com.eatsfine.eatsfine.global.common.BaseEntity;
import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.SQLDelete;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import com.eatsfine.eatsfine.domain.store.entity.Store;
import com.eatsfine.eatsfine.domain.storetable.entity.StoreTable;
import com.eatsfine.eatsfine.global.entity.BaseEntity;
import com.eatsfine.eatsfine.global.common.BaseEntity;
import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.SQLDelete;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.eatsfine.eatsfine.domain.tableblock.entity;

import com.eatsfine.eatsfine.domain.storetable.entity.StoreTable;
import com.eatsfine.eatsfine.global.entity.BaseEntity;
import com.eatsfine.eatsfine.global.common.BaseEntity;
import jakarta.persistence.*;
import lombok.*;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.eatsfine.eatsfine.domain.tableimage.entity;

import com.eatsfine.eatsfine.domain.store.entity.Store;
import com.eatsfine.eatsfine.global.entity.BaseEntity;
import com.eatsfine.eatsfine.global.common.BaseEntity;
import jakarta.persistence.*;
import lombok.*;

Expand Down
35 changes: 35 additions & 0 deletions src/main/java/com/eatsfine/eatsfine/domain/term/entity/Term.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.eatsfine.eatsfine.domain.term.entity;

import com.eatsfine.eatsfine.domain.user.entity.User;
import com.eatsfine.eatsfine.global.common.BaseEntity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "term")
public class Term extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.REMOVE, orphanRemoval = true)
@JoinColumn(name = "user_id", nullable = false, unique = true)
private User user;

@Column(name = "tos_consent", nullable = false)
private Boolean tosConsent;

@Column(name = "privacy_consent", nullable = false)
private Boolean privacyConsent;

@Column(name = "marketing_consent", nullable = false)
private Boolean marketingConsent;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.eatsfine.eatsfine.domain.term.repository;

import com.eatsfine.eatsfine.domain.term.entity.Term;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface TermRepository extends JpaRepository<Term, Long> {

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,134 @@
package com.eatsfine.eatsfine.domain.user.controller;


import com.eatsfine.eatsfine.domain.user.dto.request.UserRequestDto;
import com.eatsfine.eatsfine.domain.user.dto.response.UserResponseDto;
import com.eatsfine.eatsfine.domain.user.exception.UserException;
import com.eatsfine.eatsfine.domain.user.service.UserService;
import com.eatsfine.eatsfine.domain.user.status.UserErrorStatus;
import com.eatsfine.eatsfine.global.apiPayload.ApiResponse;
import com.eatsfine.eatsfine.global.auth.AuthCookieProvider;
import com.eatsfine.eatsfine.global.config.jwt.JwtTokenProvider;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestBody;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseCookie;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;


@Tag(name = "User", description = "회원 관리 API")
@Slf4j
@RestController
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
private final JwtTokenProvider jwtTokenProvider;
private final AuthCookieProvider authCookieProvider;

@PostMapping("/api/auth/signup")
@Operation(summary = "회원가입 API", description = "회원가입을 처리하는 API입니다.")
public ResponseEntity<UserResponseDto.JoinResultDto> signup(@RequestBody @Valid UserRequestDto.JoinDto joinDto) {
UserResponseDto.JoinResultDto result = userService.signup(joinDto);
return ResponseEntity.ok(result);
}

@PostMapping("/api/auth/login")
@Operation(summary = "로그인 API", description = "사용자 로그인을 처리하는 API입니다.")
public ResponseEntity<ApiResponse<UserResponseDto.LoginResponseDto>> login(@RequestBody UserRequestDto.LoginDto loginDto) {
UserResponseDto.LoginResponseDto loginResult = userService.login(loginDto);

if (loginResult.getRefreshToken() == null || loginResult.getRefreshToken().isBlank()) {
throw new UserException(UserErrorStatus.REFRESH_TOKEN_NOT_ISSUED);
}

ResponseCookie refreshCookie = authCookieProvider.refreshTokenCookie(loginResult.getRefreshToken());

UserResponseDto.LoginResponseDto body = UserResponseDto.LoginResponseDto.builder()
.id(loginResult.getId())
.accessToken(loginResult.getAccessToken())
.refreshToken(null)
.build();

return ResponseEntity.ok()
.header(HttpHeaders.SET_COOKIE, refreshCookie.toString())
.body(ApiResponse.onSuccess(body));
}
Comment on lines +45 to +65
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Missing @Valid annotation on login request body.

The LoginDto has @NotBlank and @Email validation constraints, but @Valid is not applied to the parameter. Validation will not be triggered.

🛡️ Proposed fix
-    public ResponseEntity<ApiResponse<UserResponseDto.LoginResponseDto>> login(`@RequestBody` UserRequestDto.LoginDto loginDto) {
+    public ResponseEntity<ApiResponse<UserResponseDto.LoginResponseDto>> login(`@RequestBody` `@Valid` UserRequestDto.LoginDto loginDto) {
🤖 Prompt for AI Agents
In
`@src/main/java/com/eatsfine/eatsfine/domain/user/controller/UserController.java`
around lines 45 - 65, The login controller is not triggering bean validation
because the `@Valid` annotation is missing on the request body parameter; update
the login(...) method signature in UserController to annotate the
UserRequestDto.LoginDto parameter with `@Valid` (i.e., change the parameter from
`@RequestBody` UserRequestDto.LoginDto loginDto to `@RequestBody` `@Valid`
UserRequestDto.LoginDto loginDto) and ensure the proper
javax/jakarta.validation.Valid import is added so the `@NotBlank/`@Email
constraints on LoginDto are enforced.


@GetMapping("/api/v1/member/info")
@Operation(
summary = "유저 내 정보 조회 API - 인증 필요",
description = "유저가 내 정보를 조회하는 API입니다.",
security = {@SecurityRequirement(name = "JWT")}
)
public ApiResponse<UserResponseDto.UserInfoDto> getMyInfo(HttpServletRequest request) {
return ApiResponse.onSuccess(userService.getMemberInfo(request));
}


@PatchMapping(value = "/api/v1/member/info")
@Operation(
summary = "닉네임/전화번호 수정 API - 인증 필요",
description = "닉네임/전화번호만 수정합니다. (JSON)",
security = {@SecurityRequirement(name = "JWT")}
)
public ResponseEntity<ApiResponse<String>> updateMyInfoText(
@RequestBody @Valid UserRequestDto.UpdateDto updateDto, HttpServletRequest request
) {
String result = userService.updateMemberInfo(updateDto, null, request);
return ResponseEntity.ok(ApiResponse.onSuccess(result));
}


@PutMapping(
value = "/api/v1/member/profile-image", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation(
summary = "프로필 이미지 수정 API - 인증 필요",
description = "프로필 이미지만 수정합니다. (multipart/form-data)",
security = {@SecurityRequirement(name = "JWT")}
)
public ResponseEntity<ApiResponse<String>> updateProfileImage(
@RequestPart(value = "profileImage") MultipartFile profileImage,
HttpServletRequest request
) {
String result = userService.updateMemberInfo(null, profileImage, request);
return ResponseEntity.ok(ApiResponse.onSuccess(result));
}


@DeleteMapping("/api/auth/withdraw")
@Operation(
summary = "회원 탈퇴 API - 인증 필요",
description = "회원 탈퇴 기능 API입니다.",
security = {@SecurityRequirement(name = "JWT")}
)
public ResponseEntity<?> withdraw(HttpServletRequest request) {
userService.withdraw(request);
return ResponseEntity.ok(ApiResponse.onSuccess("회원 탈퇴가 완료되었습니다."));
}


@DeleteMapping("/api/auth/logout")
@Operation(
summary = "회원 로그아웃 API - 인증 필요",
description = "회원 로그아웃 기능 API입니다.",
security = {@SecurityRequirement(name = "JWT")}
)
public ResponseEntity<ApiResponse<String>> logout(HttpServletRequest request) {
userService.logout(request);
ResponseCookie clearCookie = authCookieProvider.clearRefreshTokenCookie();
return ResponseEntity.ok()
.header(HttpHeaders.SET_COOKIE, clearCookie.toString())
.body(ApiResponse.onSuccess("로그아웃이 되었습니다."));
}

}
Loading
Loading