Skip to content

Commit 284dc12

Browse files
authored
Merge pull request #134 from prgrms-web-devcourse-final-project/refactor#126
[refactor] 리프레시 토큰 수정(프론트 요구사항) #126
2 parents 0151595 + 20d4c12 commit 284dc12

File tree

10 files changed

+101
-73
lines changed

10 files changed

+101
-73
lines changed

Dockerfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ COPY .env .
1717
COPY src src
1818

1919
# 애플리케이션 빌드
20-
RUN gradle build --no-daemon
20+
#RUN gradle build --no-daemon
21+
RUN gradle build -x test --no-daemon
2122

2223
# 두 번째 스테이지: 실행 스테이지
2324
FROM container-registry.oracle.com/graalvm/jdk:21

src/main/java/com/back/domain/user/controller/UserAuthController.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.back.domain.user.controller;
22

3+
import com.back.domain.user.dto.RefreshTokenResDto;
34
import com.back.domain.user.service.UserAuthService;
45
import com.back.global.rsData.RsData;
56
import io.swagger.v3.oas.annotations.Operation;
@@ -17,7 +18,7 @@
1718
@Tag(name = "UserAuth", description = "사용자 인증 API")
1819
@Slf4j
1920
@RestController
20-
@RequestMapping("/api/user/auth")
21+
@RequestMapping("/user/auth")
2122
@RequiredArgsConstructor
2223
public class UserAuthController {
2324

@@ -32,11 +33,11 @@ public class UserAuthController {
3233
@ApiResponse(responseCode = "401", description = "토큰이 유효하지 않거나 만료됨")
3334
})
3435
@PostMapping("/refresh")
35-
public RsData<Void> refreshToken(HttpServletRequest request, HttpServletResponse response) {
36-
boolean success = userAuthService.refreshTokens(request, response);
36+
public RsData<RefreshTokenResDto> refreshToken(HttpServletRequest request, HttpServletResponse response) {
37+
RefreshTokenResDto refreshToken = userAuthService.refreshTokens(request, response);
3738

38-
if (success) {
39-
return RsData.of(200, "토큰이 성공적으로 갱신되었습니다.");
39+
if (refreshToken != null) {
40+
return RsData.of(200, "토큰이 갱신 성공.", refreshToken);
4041
} else {
4142
return RsData.of(401, "토큰 갱신에 실패했습니다. 다시 로그인해주세요.");
4243
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.back.domain.user.dto;
2+
3+
import lombok.Builder;
4+
import lombok.Getter;
5+
6+
@Getter
7+
@Builder
8+
public class RefreshTokenResDto {
9+
private final String accessToken;
10+
private final UserInfoDto user;
11+
12+
@Getter
13+
@Builder
14+
public static class UserInfoDto {
15+
private final String id;
16+
private final String nickname;
17+
private final Boolean isFirstLogin;
18+
private final Double abvDegree;
19+
20+
}
21+
}

src/main/java/com/back/domain/user/entity/User.java

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,18 @@
11
package com.back.domain.user.entity;
22

33
import com.back.domain.post.post.entity.PostLike;
4-
import jakarta.persistence.CascadeType;
5-
import jakarta.persistence.Column;
6-
import jakarta.persistence.Entity;
7-
import jakarta.persistence.GeneratedValue;
8-
import jakarta.persistence.GenerationType;
9-
import jakarta.persistence.Id;
10-
import jakarta.persistence.OneToMany;
11-
import jakarta.persistence.Table;
12-
import java.time.LocalDateTime;
13-
import java.util.ArrayList;
14-
import java.util.Collection;
15-
import java.util.List;
16-
import lombok.AllArgsConstructor;
17-
import lombok.Builder;
18-
import lombok.Getter;
19-
import lombok.NoArgsConstructor;
20-
import lombok.Setter;
4+
import jakarta.persistence.*;
5+
import lombok.*;
216
import org.springframework.data.annotation.CreatedDate;
227
import org.springframework.data.annotation.LastModifiedDate;
238
import org.springframework.security.core.GrantedAuthority;
249
import org.springframework.security.core.authority.SimpleGrantedAuthority;
2510

11+
import java.time.LocalDateTime;
12+
import java.util.ArrayList;
13+
import java.util.Collection;
14+
import java.util.List;
15+
2616
@Entity
2717
@Table(name = "users") // 예약어 충돌 방지를 위해 "users" 권장
2818
@Getter

src/main/java/com/back/domain/user/service/UserAuthService.java

Lines changed: 43 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.back.domain.user.service;
22

3+
import com.back.domain.user.dto.RefreshTokenResDto;
34
import com.back.domain.user.entity.User;
45
import com.back.domain.user.repository.UserRepository;
56
import com.back.global.exception.ServiceException;
@@ -24,25 +25,25 @@
2425
@RequiredArgsConstructor
2526
public class UserAuthService {
2627

27-
static Set<String> param1 = Set.of("두둑한", "날씬한", "만취한", "알딸딸한", "얼큰한", "시트러스한", "도수높은", "톡쏘는", "거품가득한", "하이볼한",
28-
"앙증맞은", "쓸쓸한", "거만한", "산만한", "귀찮은", "삐딱한", "맛이간", "저세상급", "시궁창스러운", "기묘한",
29-
"졸린", "센치한", "철학적인", "무중력의", "뽀송한", "전투적인", "배부른", "대충한", "쩌는", "철지난",
30-
"절규하는", "맞춤형", "다급한", "찌뿌둥한", "구수한", "문어발적인", "자포자기한", "터무니없는", "귀여운척하는",
31-
"심드렁한", "무심한", "번쩍이는", "붉그레한", "밤새는", "좌절한", "의기양양한", "비굴한", "터프한", "흘러내린",
32-
"공허한", "허무한", "헛기침하는", "뿜어대는", "질척한", "기어다니는", "헤매는", "삐죽한", "악에받친", "격렬한",
33-
"삐까번쩍한", "오지랖넓은", "쪼르르거리는", "꿀꺽거리는", "머쓱한", "휘청대는", "추접스러운", "천방지축인", "어리둥절한", "질주하는",
34-
"겸연쩍은", "뿌연", "썩은", "짠내나는", "철썩같은", "흥건한", "안간힘쓰는", "뜨끈한", "꾸덕한", "동공지진난",
35-
"덕지덕지한", "비밀스러운", "개운한", "심란한", "음울한", "터질듯한", "달달한", "사악한", "기괴한", "용맹한",
36-
"껄끄러운", "헐떡이는", "허둥대는", "분란스러운", "애매한", "찐득한", "허기진", "쩔어버린", "몽롱한", "허세떠는",
37-
"황당한", "거대하고작은", "대차게구린", "어이없는", "두통약", "지갑", "이쑤시개", "돌침대", "고무장갑", "손수건",
38-
"바람개비", "지하철표", "송진가루", "철가방", "머리끈", "양말한짝", "라이터", "숟가락", "스티커", "드럼통",
39-
"열쇠꾸러미", "벼락", "대걸레", "파리채", "앙금빵", "선풍기날개", "스티로폼", "건전지", "껌종이", "소화전",
40-
"비닐우산", "고드름", "전등갓", "양초", "지우개가루", "국자", "밥솥", "연필심", "비둘기깃털", "찜질팩",
41-
"청테이프", "김밥말이", "곰팡이", "청소기", "밤송이", "옥수수수염", "철창살", "휴지심", "선반", "곽티슈",
42-
"스프링노트", "고향집된장", "머드팩", "장독대", "뒤꿈치각질", "어묵꼬치", "환풍기", "군고구마", "카세트테이프",
43-
"빨래건조대", "박카스병", "우체통", "주차권", "털실뭉치", "지하수", "깃털베개", "추리닝", "이불각", "육포",
44-
"빨대", "지렁이분양소", "김칫국", "오징어채", "전기장판", "꽃병", "도시락통", "구급상자", "양배추잎", "고무줄",
45-
"망치", "유통기한", "알람시계", "방범창", "신발깔창");
28+
static Set<String> param1 = Set.of("두둑한", "날씬한", "만취한", "알딸딸", "얼큰한", "시트러스", "도수높은", "톡쏘는", "거품가득", "하이볼한",
29+
"앙증맞은", "쓸쓸한", "거만한", "산만한", "귀찮은", "삐딱한", "맛이간", "저세상급", "시궁창", "기묘한",
30+
"졸린", "센치한", "철학적인", "무중력", "뽀송한", "전투적인", "배부른", "대충한", "쩌는", "철지난",
31+
"절규하는", "맞춤형", "다급한", "찌뿌둥한", "구수한", "문어발", "자포자기", "터무니", "귀척", "심드렁한",
32+
"무심한", "번쩍이는", "붉그레한", "밤새는", "좌절한", "의기양양", "비굴한", "터프한", "흘러내린", "공허한",
33+
"허무한", "헛기침", "뿜어대는", "질척한", "기어다님", "헤매는", "삐죽한", "악에받친", "격렬한", "삐까번쩍",
34+
"오지랖", "쪼르르", "꿀꺽", "머쓱한", "휘청대는", "추접", "천방지축", "어리둥절", "질주하는", "겸연쩍은",
35+
"뿌연", "썩은", "짠내나는", "철썩", "흥건한", "안간힘", "뜨끈한", "꾸덕한", "동공지진", "덕지덕지",
36+
"비밀", "개운한", "심란한", "음울한", "터질듯한", "달달한", "사악한", "기괴한", "용맹한", "껄끄러운",
37+
"헐떡이는", "허둥대는", "분란", "애매한", "찐득한", "허기진", "쩔어버린", "몽롱한", "허세", "황당한",
38+
"거대작음", "대차게구림", "어이없음", "두통약", "지갑", "이쑤시개", "돌침대", "고무장갑", "손수건", "바람개비",
39+
"지하철표", "송진가루", "철가방", "머리끈", "양말한짝", "라이터", "숟가락", "스티커", "드럼통", "열쇠",
40+
"벼락", "대걸레", "파리채", "앙금빵", "날개", "스티로폼", "건전지", "껌종이", "소화전", "비닐우산",
41+
"고드름", "전등갓", "양초", "지우개", "국자", "밥솥", "연필심", "깃털", "찜질팩", "청테이프",
42+
"김밥말이", "곰팡이", "청소기", "밤송이", "옥수수", "철창살", "휴지심", "선반", "곽티슈", "스프링",
43+
"고향된장", "머드팩", "장독대", "각질", "어묵꼬치", "환풍기", "군고구마", "카세트", "건조대", "박카스병",
44+
"우체통", "주차권", "털실뭉치", "지하수", "추리닝", "이불각", "육포", "빨대", "지렁이", "김칫국",
45+
"오징어채", "전기장판", "꽃병", "도시락통", "구급상자", "양배추잎", "고무줄", "망치", "유통기한", "알람시계",
46+
"방범창", "깔창", "만취육포", "날씬국자", "터프각질", "음울밥솥", "사악김치", "허세숟갈", "삐딱곰팡");
4647

4748
static Set<String> param2 = Set.of("도토리딱개구리", "아프리카들개", "강남성인군자", "술고래", "알코올러버", "겨자잎", "청개구리", "산수유",
4849
"맥주문어", "칵테일앵무새", "보드카수달", "진토닉거북이", "테킬라코요테", "럼펭귄", "사케고양이", "막걸리두꺼비",
@@ -134,46 +135,56 @@ public String generateNickname(String baseNickname) {
134135

135136
public void issueTokens(HttpServletResponse response, Long userId, String email, String nickname) {
136137
String accessToken = jwtUtil.generateAccessToken(userId, email, nickname);
137-
String refreshToken = refreshTokenService.generateRefreshToken(userId, email);
138+
String refreshToken = refreshTokenService.generateRefreshToken(userId);
138139

139140
jwtUtil.addAccessTokenToCookie(response, accessToken);
140141
jwtUtil.addRefreshTokenToCookie(response, refreshToken);
141142
}
142143

143-
public boolean refreshTokens(HttpServletRequest request, HttpServletResponse response) {
144+
public RefreshTokenResDto refreshTokens(HttpServletRequest request, HttpServletResponse response) {
144145
try {
145146
String oldRefreshToken = jwtUtil.getRefreshTokenFromCookie(request);
146147

147148
if (oldRefreshToken == null || !refreshTokenService.validateToken(oldRefreshToken)) {
148-
return false;
149+
return null;
149150
}
150151

151152
Optional<RefreshToken> tokenData = refreshTokenRepository.findByToken(oldRefreshToken);
152153
if (tokenData.isEmpty()) {
153-
return false;
154+
return null;
154155
}
155156

156157
RefreshToken refreshTokenEntity = tokenData.get();
157158
Long userId = refreshTokenEntity.getUserId();
158-
String email = refreshTokenEntity.getEmail();
159159

160-
// DB에서 현재 nickname 조회
161-
Optional<User> user = userRepository.findById(userId);
162-
if (user.isEmpty()) {
163-
return false;
160+
// DB에서 사용자 정보 조회
161+
Optional<User> userOpt = userRepository.findById(userId);
162+
if (userOpt.isEmpty()) {
163+
return null;
164164
}
165-
String nickname = user.get().getNickname();
165+
166+
User user = userOpt.get();
166167

167168
String newRefreshToken = refreshTokenService.rotateToken(oldRefreshToken);
168-
String newAccessToken = jwtUtil.generateAccessToken(userId, email, nickname);
169+
String newAccessToken = jwtUtil.generateAccessToken(userId, user.getEmail(), user.getNickname());
169170

170171
jwtUtil.addAccessTokenToCookie(response, newAccessToken);
171172
jwtUtil.addRefreshTokenToCookie(response, newRefreshToken);
172173

173-
return true;
174+
return RefreshTokenResDto.builder()
175+
.accessToken(newAccessToken)
176+
.user(
177+
RefreshTokenResDto.UserInfoDto.builder()
178+
.id(user.getId().toString())
179+
.nickname(user.getNickname())
180+
.isFirstLogin(user.isFirstLogin())
181+
.abvDegree(user.getAbvDegree())
182+
.build()
183+
)
184+
.build();
174185
} catch (Exception e) {
175186
log.error("토큰 갱신 중 오류 발생: {}", e.getMessage());
176-
return false;
187+
return null;
177188
}
178189
}
179190

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,23 @@
11
package com.back.global.controller;
22

3+
import org.springframework.beans.factory.annotation.Value;
34
import org.springframework.stereotype.Controller;
45
import org.springframework.web.bind.annotation.GetMapping;
56

67
@Controller
78
public class HomeController {
89

10+
@Value("${custom.site.frontUrl}")
11+
private String frontUrl;
12+
13+
@Value("${spring.profiles.active}")
14+
private String activeProfile;
15+
916
@GetMapping("/")
10-
public String redirectToSwagger() {
17+
public String redirect() {
18+
if("prod".equals(activeProfile)){
19+
return "redirect:" + frontUrl;
20+
}
1121
return "redirect:/swagger-ui/index.html";
1222
}
1323
}

src/main/java/com/back/global/jwt/JwtUtil.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ public String generateAccessToken(Long userId, String email, String nickname) {
4242
.compact();
4343
}
4444

45+
4546
public void addAccessTokenToCookie(HttpServletResponse response, String accessToken) {
4647
Cookie cookie = new Cookie(ACCESS_TOKEN_COOKIE_NAME, accessToken);
4748
cookie.setHttpOnly(true);

src/main/java/com/back/global/jwt/refreshToken/entity/RefreshToken.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,18 @@ public class RefreshToken {
2020
@Column(nullable = false)
2121
private Long userId;
2222

23-
@Column(nullable = false)
24-
private String email;
25-
2623
@Column(nullable = false)
2724
private LocalDateTime createdAt;
2825

2926
@Column(nullable = false)
3027
private LocalDateTime expiresAt;
3128

32-
public static RefreshToken create(String token, Long userId, String email, long ttlSeconds) {
29+
30+
public static RefreshToken create(String token, Long userId, long ttlSeconds) {
3331
LocalDateTime now = LocalDateTime.now();
3432
return RefreshToken.builder()
3533
.token(token)
3634
.userId(userId)
37-
.email(email)
3835
.createdAt(now)
3936
.expiresAt(now.plusSeconds(ttlSeconds))
4037
.build();

src/main/java/com/back/global/jwt/refreshToken/service/RefreshTokenService.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
import com.back.global.jwt.refreshToken.repository.RefreshTokenRepository;
66
import lombok.RequiredArgsConstructor;
77
import org.springframework.beans.factory.annotation.Value;
8+
import org.springframework.scheduling.annotation.Scheduled;
89
import org.springframework.stereotype.Service;
10+
import org.springframework.transaction.annotation.Transactional;
911

1012
import java.security.SecureRandom;
1113
import java.time.LocalDateTime;
1214
import java.util.Base64;
1315
import java.util.Optional;
14-
import org.springframework.scheduling.annotation.Scheduled;
15-
import org.springframework.transaction.annotation.Transactional;
1616

1717
@Service
1818
@RequiredArgsConstructor
@@ -26,12 +26,12 @@ public class RefreshTokenService {
2626

2727
// 기존 리프레시 토큰 삭제하고 생성
2828
@Transactional
29-
public String generateRefreshToken(Long userId, String email) {
29+
public String generateRefreshToken(Long userId) {
3030
// 기존 토큰 삭제
3131
refreshTokenRepository.deleteByUserId(userId);
3232

3333
String token = generateSecureToken();
34-
RefreshToken refreshToken = RefreshToken.create(token, userId, email, refreshTokenExpiration);
34+
RefreshToken refreshToken = RefreshToken.create(token, userId, refreshTokenExpiration);
3535
refreshTokenRepository.save(refreshToken);
3636

3737
return token;
@@ -65,7 +65,7 @@ public String rotateToken(String oldToken) {
6565
RefreshToken tokenData = oldRefreshToken.get();
6666
revokeToken(oldToken);
6767

68-
return generateRefreshToken(tokenData.getUserId(), tokenData.getEmail());
68+
return generateRefreshToken(tokenData.getUserId());
6969
}
7070

7171
//삭제

src/main/java/com/back/global/security/CustomOAuth2LoginSuccessHandler.java

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,15 @@ public class CustomOAuth2LoginSuccessHandler implements AuthenticationSuccessHan
2424
@Override
2525
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
2626
SecurityUser securityUser = (SecurityUser) authentication.getPrincipal();
27+
2728
// Access Token과 Refresh Token 발급
2829
userAuthService.issueTokens(response, securityUser.getId(), securityUser.getEmail(), securityUser.getNickname());
2930

30-
// 첫 로그인 여부에 따라 리다이렉트 분기
31-
String redirectUrl;
32-
3331
if (securityUser.isFirstLogin()) {
34-
redirectUrl = frontendUrl + "/oauth/success/welcome";
3532
userAuthService.setFirstLoginFalse(securityUser.getId());
33+
response.sendRedirect(frontendUrl + "/login/first-user");
3634
} else {
37-
redirectUrl = frontendUrl + "/oauth/success";
35+
response.sendRedirect(frontendUrl + "/login/success");
3836
}
39-
40-
response.sendRedirect(redirectUrl);
4137
}
4238
}

0 commit comments

Comments
 (0)