Skip to content

Commit 3fc875d

Browse files
iamseojinLEEDONGH00NCopilot
authored
[feat] 로그인 완료 후 회원 정보 조회시 Access · RefreshToken 반환 (#98)
* [fix] build.gradle jjwt 0.9.1과 0.11.x 의존성 혼용 문제 해결 및 의존성 정리 * [feat] 로그인 회원 조회시 토큰 정보 함께 조회 * [feat] 보안성 강화를 위한 HttpOnly Cookie로 RefreshToken 전달 * [fix] 클라이언트와의 호환을 위한 로그인 직후 refreshToken 응답 바디로 전달 * Setter 제거 Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Setter 제거 Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update src/main/java/com/arom/with_travel/domain/member/dto/MemberSignupTokenResponse.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: LEEDONGHOON <123933574+LEEDONGH00N@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 4db41cb commit 3fc875d

6 files changed

Lines changed: 80 additions & 24 deletions

File tree

build.gradle

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,11 @@ dependencies {
5151
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
5252
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
5353

54-
//redis
54+
// Redis / WebSocket / Mongo
5555
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
5656
implementation 'org.springframework.boot:spring-boot-starter-websocket'
5757
implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'
5858

59-
6059
/* Swagger / OpenAPI */
6160
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0'
6261

@@ -81,24 +80,14 @@ dependencies {
8180
annotationProcessor 'org.projectlombok:lombok'
8281

8382
/* === 테스트 전용 === */
84-
testImplementation 'org.springframework.boot:spring-boot-starter-test' // JUnit5 + Mockito(core) + AssertJ 포함
85-
testImplementation 'org.springframework.security:spring-security-test' // SecurityMockMvcRequestPostProcessors 등
86-
testImplementation 'org.mockito:mockito-junit-jupiter' // MockitoExtension
87-
testImplementation 'org.assertj:assertj-core' // 최신 AssertJ
88-
89-
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
90-
91-
// security
92-
implementation 'org.springframework.boot:spring-boot-starter-security'
93-
94-
// OAuth2
95-
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
96-
97-
// jwt
98-
implementation 'io.jsonwebtoken:jjwt:0.9.1'
99-
implementation 'javax.xml.bind:jaxb-api:2.3.1'
83+
testImplementation 'org.springframework.boot:spring-boot-starter-test'
84+
testImplementation 'org.springframework.security:spring-security-test'
85+
testImplementation 'org.mockito:mockito-junit-jupiter'
86+
testImplementation 'org.assertj:assertj-core'
87+
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
10088
}
10189

90+
10291
tasks.named('test') {
10392
useJUnitPlatform()
10493

src/main/java/com/arom/with_travel/domain/member/controller/MemberSignupController.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
package com.arom.with_travel.domain.member.controller;
22

3+
import com.arom.with_travel.domain.member.dto.MemberSignupTokenResponse;
34
import com.arom.with_travel.domain.member.dto.MemberSignupRequestDto;
45
import com.arom.with_travel.domain.member.dto.MemberSignupResponseDto;
56
import com.arom.with_travel.domain.member.service.MemberService;
67
import com.arom.with_travel.domain.member.service.MemberSignupService;
8+
import com.arom.with_travel.global.jwt.dto.response.AuthTokenResponse;
9+
import com.arom.with_travel.global.jwt.service.TokenService;
710
import com.arom.with_travel.global.oauth2.dto.CustomOAuth2User;
811
import io.swagger.v3.oas.annotations.tags.Tag;
12+
import jakarta.servlet.http.HttpServletResponse;
913
import jakarta.validation.Valid;
1014
import lombok.RequiredArgsConstructor;
15+
import org.springframework.http.MediaType;
1116
import org.springframework.http.ResponseEntity;
1217
import org.springframework.security.core.annotation.AuthenticationPrincipal;
1318
import org.springframework.web.bind.annotation.*;
@@ -19,6 +24,7 @@ public class MemberSignupController {
1924

2025
private final MemberService memberService;
2126
private final MemberSignupService memberSignupService;
27+
private final TokenService tokenService;
2228

2329
// 추가 정보 등록
2430
@PostMapping("/signup/register")
@@ -32,12 +38,16 @@ public ResponseEntity<MemberSignupResponseDto> fillExtraInfo(
3238
}
3339

3440
// 현재 로그인 사용자 정보 조회
35-
@GetMapping("/signup/register")
36-
public ResponseEntity<MemberSignupResponseDto> getMyInfo(
37-
@AuthenticationPrincipal CustomOAuth2User user) {
41+
@GetMapping(value = "/signup/register",
42+
produces = MediaType.APPLICATION_JSON_VALUE)
43+
public ResponseEntity<MemberSignupTokenResponse> getMyInfo(
44+
@AuthenticationPrincipal CustomOAuth2User user,
45+
HttpServletResponse response) {
3846

3947
MemberSignupResponseDto dto = memberSignupService
4048
.getSignupInfo(user.getEmail());
41-
return ResponseEntity.ok(dto);
49+
AuthTokenResponse tokenDto = tokenService.issueTokenPair(user.getEmail(), response);
50+
MemberSignupTokenResponse signupDto = new MemberSignupTokenResponse(dto, tokenDto);
51+
return ResponseEntity.ok(signupDto);
4252
}
4353
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.arom.with_travel.domain.member.dto;
2+
3+
import com.arom.with_travel.global.jwt.dto.response.AuthTokenResponse;
4+
import lombok.AllArgsConstructor;
5+
import lombok.Getter;
6+
7+
@Getter
8+
@AllArgsConstructor
9+
public class MemberSignupTokenResponse {
10+
private MemberSignupResponseDto memberSignupDto;
11+
private AuthTokenResponse tokenDto;
12+
}

src/main/java/com/arom/with_travel/global/jwt/config/JwtProperties.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,7 @@
1212
public class JwtProperties {
1313
private String issuer;
1414
private String secretKey;
15+
private int accessTokenExpireHours;
16+
private int refreshTokenExpireDays;
17+
private String refreshCookieName;
1518
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.arom.with_travel.global.jwt.dto.response;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Getter;
5+
6+
7+
@Getter
8+
@AllArgsConstructor
9+
public class AuthTokenResponse {
10+
private String accessToken;
11+
private String refreshToken;
12+
}

src/main/java/com/arom/with_travel/global/jwt/service/TokenService.java

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,18 @@
22

33
import com.arom.with_travel.domain.member.Member;
44
import com.arom.with_travel.domain.member.service.MemberService;
5-
import com.arom.with_travel.domain.member.service.MemberSignupService;
65
import com.arom.with_travel.global.exception.BaseException;
76
import com.arom.with_travel.global.exception.error.ErrorCode;
7+
import com.arom.with_travel.global.jwt.config.JwtProperties;
88
import com.arom.with_travel.global.jwt.domain.RefreshToken;
9+
import com.arom.with_travel.global.jwt.dto.response.AuthTokenResponse;
910
import com.arom.with_travel.global.jwt.repository.RefreshTokenRepository;
11+
import jakarta.servlet.http.HttpServletResponse;
1012
import jakarta.transaction.Transactional;
1113
import lombok.RequiredArgsConstructor;
1214
import lombok.extern.slf4j.Slf4j;
15+
import org.springframework.http.HttpHeaders;
16+
import org.springframework.http.ResponseCookie;
1317
import org.springframework.stereotype.Service;
1418

1519
import java.time.Duration;
@@ -24,7 +28,7 @@ public class TokenService {
2428
private final RefreshTokenService refreshTokenService;
2529
private final MemberService memberService;
2630
private final RefreshTokenRepository refreshTokenRepository;
27-
private final MemberSignupService memberSignupService;
31+
private final JwtProperties jwtProperties;
2832

2933
// 새로운 액세스 토큰 생성
3034
public String createNewAccessToken(String refreshToken) {
@@ -51,4 +55,30 @@ public void validateRefreshTokenOrElseThrow(String refreshToken) {
5155
throw BaseException.from(ErrorCode.INVALID_TOKEN);
5256
}
5357
}
58+
59+
public AuthTokenResponse issueTokenPair(String loginEmail, HttpServletResponse response) {
60+
61+
Member member = memberService.getUserByLoginEmailOrElseThrow(loginEmail);
62+
63+
String accessToken = tokenProvider.generateToken(
64+
member,
65+
Duration.ofHours(jwtProperties.getAccessTokenExpireHours())
66+
);
67+
68+
String refreshToken = tokenProvider.generateToken(
69+
member,
70+
Duration.ofDays(jwtProperties.getRefreshTokenExpireDays())
71+
);
72+
73+
// HttpOnly 쿠키 설정
74+
ResponseCookie cookie = ResponseCookie.from(jwtProperties.getRefreshCookieName(), refreshToken)
75+
.httpOnly(true)
76+
.secure(true)
77+
.path("/")
78+
.maxAge(Duration.ofDays(jwtProperties.getRefreshTokenExpireDays()))
79+
.build();
80+
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
81+
82+
return new AuthTokenResponse(accessToken, refreshToken);
83+
}
5484
}

0 commit comments

Comments
 (0)