Skip to content

Commit a762d67

Browse files
authored
merge: pull request #19 from feat/token/1
[Feat]token/1 pull request
2 parents 2e57c6a + d3f5e72 commit a762d67

File tree

7 files changed

+128
-21
lines changed

7 files changed

+128
-21
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package org.dfbf.soundlink.domain.user.controller;
2+
3+
import io.swagger.v3.oas.annotations.Operation;
4+
import io.swagger.v3.oas.annotations.tags.Tag;
5+
import jakarta.servlet.http.HttpServletRequest;
6+
import jakarta.servlet.http.HttpServletResponse;
7+
import lombok.AllArgsConstructor;
8+
import org.dfbf.soundlink.domain.user.dto.request.LoginReqDto;
9+
import org.dfbf.soundlink.domain.user.service.UserService;
10+
import org.dfbf.soundlink.global.exception.ResponseResult;
11+
import org.springframework.web.bind.annotation.PostMapping;
12+
import org.springframework.web.bind.annotation.RequestBody;
13+
import org.springframework.web.bind.annotation.RequestMapping;
14+
import org.springframework.web.bind.annotation.RestController;
15+
16+
@RestController
17+
@AllArgsConstructor
18+
@Tag(name = "Auth API", description = "인증 관련 API")
19+
@RequestMapping("/api/auth")
20+
public class AuthController {
21+
22+
private final UserService userService;
23+
24+
@PostMapping("/login")
25+
@Operation(summary = "로그인", description = "로그인 API")
26+
public ResponseResult login(@RequestBody LoginReqDto loginReqDto, HttpServletResponse response) {
27+
return userService.login(loginReqDto, response);
28+
}
29+
30+
@PostMapping("/logout")
31+
@Operation(summary = "로그아웃", description = "로그아웃 API")
32+
public ResponseResult logout(HttpServletResponse response, HttpServletRequest request) {
33+
return userService.logout(response, request);
34+
}
35+
36+
@PostMapping("/token")
37+
@Operation(summary = "토큰 재발급", description="AC 토큰 재발급 API")
38+
public ResponseResult reissueToken(HttpServletRequest request, HttpServletResponse response){
39+
return userService.reissueToken(request, response);
40+
}
41+
}

src/main/java/org/dfbf/soundlink/domain/user/controller/UserController.java

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -50,15 +50,5 @@ public ResponseResult updateUser(@AuthenticationPrincipal Long id,@RequestBody U
5050
@Operation(summary = "마이 페이지", description = "마이 페이지 조회 API")
5151
public ResponseResult getMyPage(@AuthenticationPrincipal Long id) { return userService.getMyPage(id); }
5252

53-
@PostMapping("/login")
54-
@Operation(summary = "로그인", description = "로그인 API")
55-
public ResponseResult login(@RequestBody LoginReqDto loginReqDto, HttpServletResponse response) {
56-
return userService.login(loginReqDto, response);
57-
}
5853

59-
@PostMapping("/logout")
60-
@Operation(summary = "로그아웃", description = "로그아웃 API")
61-
public ResponseResult logout(HttpServletResponse response) {
62-
return userService.logout(response);
63-
}
6454
}

src/main/java/org/dfbf/soundlink/domain/user/repository/UserRepository.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,6 @@ public interface UserRepository extends JpaRepository<User, Long> {
3333
Optional<User> findByLoginId(String loginId);
3434

3535
@Query("Select u.password from User u where u.loginId =:loginId ")
36-
String findByPassword(@Param("loginId")String loginId);
37-
38-
39-
36+
String findPasswordByLoginId(@Param("loginId")String loginId); //비밀번호만 조회
4037

4138
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package org.dfbf.soundlink.domain.user.service;
2+
3+
import org.springframework.beans.factory.annotation.Autowired;
4+
import org.springframework.data.redis.core.StringRedisTemplate;
5+
import org.springframework.stereotype.Service;
6+
7+
@Service
8+
public class TokenService {
9+
@Autowired
10+
private StringRedisTemplate redisTemplate;
11+
12+
//가져오기
13+
public String getRefreshToken(Long userId) {
14+
String tokenKey = "refreshToken:" + userId;
15+
return redisTemplate.opsForValue().get(tokenKey);
16+
}
17+
18+
//저장하기
19+
public void saveRedisTemplate(Long userId, String refreshToken) {
20+
String tokenKey = "refreshToken:" + userId;
21+
redisTemplate.opsForValue().set(tokenKey, refreshToken);
22+
}
23+
24+
//삭제하기
25+
public void deleteRefreshToken(Long userId){
26+
String tokenKey = "refreshToken:" + userId;
27+
redisTemplate.delete(tokenKey);
28+
}
29+
}

src/main/java/org/dfbf/soundlink/domain/user/service/UserService.java

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package org.dfbf.soundlink.domain.user.service;
22

33
import jakarta.mail.MessagingException;
4+
import jakarta.servlet.http.Cookie;
5+
import jakarta.servlet.http.HttpServletRequest;
46
import jakarta.servlet.http.HttpServletResponse;
57
import jakarta.transaction.Transactional;
68
import lombok.AllArgsConstructor;
@@ -22,7 +24,6 @@
2224
import org.dfbf.soundlink.global.auth.TokenProperties;
2325
import org.dfbf.soundlink.global.exception.ErrorCode;
2426
import org.dfbf.soundlink.global.exception.ResponseResult;
25-
import org.springframework.data.redis.core.RedisTemplate;
2627
import org.springframework.http.ResponseCookie;
2728
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
2829
import org.springframework.stereotype.Service;
@@ -43,9 +44,10 @@ public class UserService {
4344
private final BCryptPasswordEncoder passwordEncoder;
4445
private final MailService mailService;
4546
private final RedisService redisService;
47+
4648
private final JwtProvider jwtProvider;
4749
private final TokenProperties tokenProperties;
48-
private RedisTemplate<String, String> redisTemplate;
50+
private final TokenService tokenService;
4951

5052

5153
// 회원가입
@@ -194,7 +196,7 @@ public ResponseResult login(LoginReqDto loginReqDto, HttpServletResponse respons
194196
return new ResponseResult(ErrorCode.FAIL_TO_FIND_USER, "계정을 찾을 수 없습니다.");
195197
}
196198
// 비밀번호 검증(암호화 된 비밀번호 비교)
197-
if(!passwordEncoder.matches(loginReqDto.password(), userRepository.findByPassword(loginReqDto.loginId()))){
199+
if(!passwordEncoder.matches(loginReqDto.password(), userRepository.findPasswordByLoginId(loginReqDto.loginId()))){
198200
return new ResponseResult( ErrorCode.NOT_EQUALS_PASSWORD,"잘못된 비밀번호 입니다.");
199201
}
200202

@@ -220,7 +222,7 @@ public ResponseResult login(LoginReqDto loginReqDto, HttpServletResponse respons
220222
}
221223

222224
//로그아웃
223-
public ResponseResult logout(HttpServletResponse response) {
225+
public ResponseResult logout(HttpServletResponse response, HttpServletRequest request) {
224226
try {
225227
//클라이언트 - 토큰 삭제
226228
ResponseCookie refreshCookie = ResponseCookie
@@ -232,13 +234,60 @@ public ResponseResult logout(HttpServletResponse response) {
232234
.build();
233235
response.setHeader("Set-Cookie", refreshCookie.toString());//쿠키 삭제 요청
234236

235-
return new ResponseResult(ErrorCode.SUCCESS);
237+
String accessToken = jwtProvider.resolveAccessToken(request); // 요청에서 액세스 토큰 추출
238+
Long userId = jwtProvider.getUserId(accessToken); // 액세스 토큰을 넘겨서 userId 추출
239+
240+
tokenService.deleteRefreshToken(userId);
241+
242+
return new ResponseResult(ErrorCode.SUCCESS,"로그아웃 되었습니다.");
236243

237244
} catch (Exception e) {
238245
return new ResponseResult(ErrorCode. INTERNAL_SERVER_ERROR,"로그아웃 중 오류가 발생했습니다.");
239246
}
240247
}
241248

249+
//토큰 재발급
250+
public ResponseResult reissueToken(HttpServletRequest request, HttpServletResponse response) {
251+
String accessToken = jwtProvider.resolveAccessToken(request);
252+
String refreshToken = jwtProvider.resolveRefreshToken(request);
253+
254+
// System.out.println("AccessToken: " + accessToken);
255+
// System.out.println("RefreshToken from Cookie: " + refreshToken);
256+
257+
// AccessToken과 RefreshToken이 모두 없는 경우
258+
if (accessToken == null || refreshToken == null) {
259+
logout(response,request);
260+
return new ResponseResult(ErrorCode.TOKEN_INVALID, "토큰이 존재하지 않거나 만료되었습니다.");
261+
}
262+
263+
// AccessToken 유효성 확인
264+
if (jwtProvider.validateToken(accessToken)) {
265+
return new ResponseResult(ErrorCode.TOKEN_NOT_EXPIRED);// 유효한 액세스 토큰: 재발급 x
266+
}
267+
268+
// RefreshToken 유효성 확인
269+
if (jwtProvider.validateToken(refreshToken)) {
270+
Long userId = jwtProvider.getUserId(refreshToken);
271+
272+
// Redis에서 리프레시 토큰 가져오기
273+
String redisRefreshToken = tokenService.getRefreshToken(userId);
274+
275+
// Redis에서 리프레시 토큰을 확인하고, 일치하면 새 액세스 토큰 발급
276+
if (redisRefreshToken != null && redisRefreshToken.equals(refreshToken)) {
277+
String newAccessToken = jwtProvider.createAccessToken(userId);
278+
279+
// System.out.println("New AccessToken: " + newAccessToken);
280+
281+
Map<String, String> responseBody = new HashMap<>();
282+
responseBody.put("accessToken", newAccessToken);
242283

284+
return new ResponseResult(ErrorCode.SUCCESS, responseBody);
285+
} else {
286+
return new ResponseResult(ErrorCode.TOKEN_INVALID, "리프레시 토큰이 일치하지 않습니다.");
287+
}
288+
} else {
289+
return new ResponseResult(ErrorCode.TOKEN_INVALID, "리프레시 토큰이 유효하지 않습니다.");
290+
}
291+
}
243292

244293
}

src/main/java/org/dfbf/soundlink/global/auth/JwtProvider.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,10 @@ public String createRefreshToken(long userId) {
5555
.signWith(SECRET_KEY, SignatureAlgorithm.HS256)
5656
.compact();
5757
try {
58-
redisTemplate.opsForValue().set("refreshToken: "+userId, refreshToken,REFRESH_EXPIRATION_TIME, TimeUnit.MILLISECONDS);
58+
redisTemplate.opsForValue().set("refreshToken:"+userId, refreshToken,REFRESH_EXPIRATION_TIME, TimeUnit.MILLISECONDS);
5959
return refreshToken;
6060
} catch (Exception e) {
61-
System.out.println("[Redis] RefreshToken save failed: " + e.getMessage());
61+
System.out.println("[Redis] RefreshToken save failed:" + e.getMessage());
6262
return null;
6363
}
6464
}

src/main/java/org/dfbf/soundlink/global/exception/ErrorCode.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ public enum ErrorCode {
4343
TOKEN_NOT_EXPIRED(HttpStatus.OK, "토큰 정상"),
4444
TOKEN_EXPIRED(HttpStatus.BAD_REQUEST, "토큰 만료됨"),
4545
TOKEN_TAMPERED(HttpStatus.BAD_REQUEST, "토큰 변조됨"),
46+
TOKEN_INVALID(HttpStatus.BAD_REQUEST,"유효하지 않은 토큰"),
4647

4748
// 카카오페이 결제 에러
4849
KAKAOPAY_READY_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "카카오페이 결제 준비 에러"),

0 commit comments

Comments
 (0)