Skip to content

Commit

Permalink
refactor : refresh token을 쿠키에 저장 #45
Browse files Browse the repository at this point in the history
  • Loading branch information
strangehoon committed Mar 30, 2024
1 parent 5128f22 commit 289aea7
Show file tree
Hide file tree
Showing 9 changed files with 83 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
import com.sendback.domain.auth.service.KakaoService;
import com.sendback.global.common.ApiResponse;
import com.sendback.global.common.UserId;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseCookie;
import org.springframework.web.bind.annotation.*;

@RestController
Expand All @@ -20,23 +23,50 @@ public class AuthController {
private final KakaoService kakaoService;
private final GoogleService googleService;
private final AuthService authService;
@Value("${jwt.refresh-token-expire-time}")
private long REFRESH_TOKEN_EXPIRE_TIME;
private final static String REFRESH_TOKEN = "refreshToken";

@GetMapping("/kakao/callback")
public ApiResponse<TokensResponseDto> loginKakao(@RequestParam String code) throws JsonProcessingException {
TokensResponseDto tokens = kakaoService.loginKakao(code);
return ApiResponse.success(tokens);
public ApiResponse<TokensResponseDto> loginKakao(@RequestParam String code, HttpServletResponse response) throws JsonProcessingException {
Token tokens = kakaoService.loginKakao(code);
ResponseCookie cookie = ResponseCookie.from(REFRESH_TOKEN, tokens.refreshToken())
.maxAge(REFRESH_TOKEN_EXPIRE_TIME)
.path("/")
.secure(true)
.sameSite("None")
.httpOnly(true)
.build();
response.setHeader("set-cookie", cookie.toString());
return ApiResponse.success(new TokensResponseDto(tokens.accessToken()));
}

@GetMapping("/google/callback")
public ApiResponse<TokensResponseDto> loginGoogle(@RequestParam String code) throws JsonProcessingException {
TokensResponseDto tokens = googleService.loginGoogle(code);
return ApiResponse.success(tokens);
public ApiResponse<TokensResponseDto> loginGoogle(@RequestParam String code, HttpServletResponse response) throws JsonProcessingException {
Token tokens = googleService.loginGoogle(code);
ResponseCookie cookie = ResponseCookie.from(REFRESH_TOKEN, tokens.refreshToken())
.maxAge(REFRESH_TOKEN_EXPIRE_TIME)
.path("/")
.secure(true)
.sameSite("None")
.httpOnly(true)
.build();
response.setHeader("set-cookie", cookie.toString());
return ApiResponse.success(new TokensResponseDto(tokens.accessToken()));
}

@PostMapping("/reissue")
public ApiResponse<TokensResponseDto> reissueToken(@RequestBody RefreshTokenRequestDto refreshTokenDto){
public ApiResponse<TokensResponseDto> reissueToken(@RequestBody RefreshTokenRequestDto refreshTokenDto, HttpServletResponse response){
Token tokens = authService.reissueToken(refreshTokenDto.refreshToken());
return ApiResponse.success(new TokensResponseDto(tokens.accessToken(), tokens.refreshToken()));
ResponseCookie cookie = ResponseCookie.from(REFRESH_TOKEN, tokens.refreshToken())
.maxAge(REFRESH_TOKEN_EXPIRE_TIME)
.path("/")
.secure(true)
.sameSite("None")
.httpOnly(true)
.build();
response.setHeader("set-cookie", cookie.toString());
return ApiResponse.success(new TokensResponseDto(tokens.accessToken()));
}

@PostMapping("/logout")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package com.sendback.domain.auth.dto.response;
public record TokensResponseDto(String accessToken, String refreshToken
public record TokensResponseDto(String accessToken
) {

}
16 changes: 0 additions & 16 deletions src/main/java/com/sendback/domain/auth/service/AuthService.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@
import com.sendback.domain.auth.dto.Token;
import com.sendback.global.config.redis.RedisService;
import com.sendback.global.config.jwt.JwtProvider;
import com.sendback.global.exception.type.UnAuthorizedException;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand All @@ -15,29 +13,15 @@ public class AuthService {

private final JwtProvider jwtProvider;
private final RedisService redisService;
@Value("${jwt.refresh-token-expire-time}")
private long REFRESH_TOKEN_EXPIRE_TIME;

@Transactional
public Token reissueToken(String refreshToken) {
Long userId = jwtProvider.parseRefreshToken(refreshToken);
validateRefreshToken(refreshToken, userId);
Token tokens = jwtProvider.issueToken(userId);
redisService.put(userId, tokens.refreshToken(), REFRESH_TOKEN_EXPIRE_TIME);
return tokens;
}

public void logoutSocial(Long userId){
redisService.delete(userId);
}

private void validateRefreshToken(String refreshToken, Long userId) {
try {
jwtProvider.validateRefreshToken(refreshToken);
jwtProvider.equalsRefreshToken(userId, refreshToken);
} catch (UnAuthorizedException e) {
redisService.delete(userId);
throw e;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import com.sendback.domain.auth.dto.SocialUserInfo;
import com.sendback.domain.auth.dto.Token;
import com.sendback.domain.auth.dto.response.SignTokenResponseDto;
import com.sendback.domain.auth.dto.response.TokensResponseDto;
import com.sendback.domain.user.entity.User;
import com.sendback.domain.user.repository.UserRepository;
import com.sendback.global.config.jwt.JwtProvider;
Expand All @@ -23,7 +22,6 @@
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import static com.sendback.domain.auth.exception.AuthExceptionType.NEED_TO_SIGNUP;

@Service
Expand All @@ -47,7 +45,7 @@ public class GoogleService {
private final RestTemplate rt;

@Transactional
public TokensResponseDto loginGoogle(String code) throws JsonProcessingException {
public Token loginGoogle(String code) throws JsonProcessingException {
String accessToken = getAccessToken(code);
SocialUserInfo googleUserInfo = getGoogleUserInfo(accessToken);

Expand All @@ -59,7 +57,7 @@ public TokensResponseDto loginGoogle(String code) throws JsonProcessingException
}

Token token = jwtProvider.issueToken(googleUser.getId());
return new TokensResponseDto(token.accessToken(), token.refreshToken());
return token;
}

private String getAccessToken(String code) throws JsonProcessingException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import com.sendback.domain.auth.dto.SocialUserInfo;
import com.sendback.domain.auth.dto.Token;
import com.sendback.domain.auth.dto.response.SignTokenResponseDto;
import com.sendback.domain.auth.dto.response.TokensResponseDto;
import com.sendback.domain.user.entity.User;
import com.sendback.domain.user.repository.UserRepository;
import com.sendback.global.config.jwt.JwtProvider;
Expand All @@ -23,7 +22,6 @@
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import static com.sendback.domain.auth.exception.AuthExceptionType.NEED_TO_SIGNUP;

@Service
Expand All @@ -44,7 +42,7 @@ public class KakaoService {
private final RestTemplate rt;

@Transactional
public TokensResponseDto loginKakao(String code) throws JsonProcessingException {
public Token loginKakao(String code) throws JsonProcessingException {
String accessToken = getAccessToken(code);
SocialUserInfo kakaoUserInfo = getKakaoUserInfo(accessToken);

Expand All @@ -56,7 +54,7 @@ public TokensResponseDto loginKakao(String code) throws JsonProcessingException
}

Token token = jwtProvider.issueToken(kakaoUser.getId());
return new TokensResponseDto(token.accessToken(), token.refreshToken());
return token;
}

private String getAccessToken(String code) throws JsonProcessingException {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package com.sendback.domain.user.controller;

import com.sendback.domain.auth.dto.response.TokensResponseDto;
import com.sendback.domain.user.dto.request.SignUpRequestDto;
import com.sendback.domain.user.dto.request.UpdateUserInfoRequestDto;
import com.sendback.domain.user.dto.response.*;
import com.sendback.domain.user.service.UserService;
import com.sendback.global.common.ApiResponse;
import com.sendback.global.common.CustomPage;
import com.sendback.global.common.UserId;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseCookie;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
Expand All @@ -22,11 +26,23 @@
public class UserController {

private final UserService userService;
@Value("${jwt.refresh-token-expire-time}")
private long REFRESH_TOKEN_EXPIRE_TIME;
private final static String REFRESH_TOKEN = "refreshToken";

@PostMapping("/signup")
public ApiResponse<Token> signUpUser(@RequestBody @Valid SignUpRequestDto signUpRequestDto) {
public ApiResponse<TokensResponseDto> signUpUser(@RequestBody @Valid SignUpRequestDto signUpRequestDto, HttpServletResponse response) {
Token tokens = userService.signUpUser(signUpRequestDto);
return ApiResponse.success(tokens);
ResponseCookie cookie = ResponseCookie.from(REFRESH_TOKEN, tokens.refreshToken())
.maxAge(REFRESH_TOKEN_EXPIRE_TIME)
.path("/")
.secure(true)
.sameSite("None")
.httpOnly(true)
.build();
response.setHeader("set-cookie", cookie.toString());
System.out.println(tokens.refreshToken());
return ApiResponse.success(new TokensResponseDto(tokens.accessToken()));
}

@GetMapping("/check")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
Expand All @@ -18,17 +17,14 @@
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtProvider jwtProvider;
private final RedisTemplate redisTemplate;

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String accessToken = jwtProvider.resolveToken(request);
if (StringUtils.hasText(accessToken)) {
if(redisTemplate.opsForValue().get(accessToken)==null){
jwtProvider.validateAccessToken(accessToken);
Long userId = jwtProvider.getSubject(accessToken);
setAuthentication(request, userId);
}
jwtProvider.validateAccessToken(accessToken);
Long userId = jwtProvider.getSubject(accessToken);
setAuthentication(request, userId);
}
filterChain.doFilter(request, response);
}
Expand Down
46 changes: 15 additions & 31 deletions src/main/java/com/sendback/global/config/jwt/JwtProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
import com.sendback.domain.auth.dto.Token;
import com.sendback.domain.user.dto.SigningUser;
import com.sendback.global.config.redis.RedisService;
import com.sendback.global.exception.type.SignInException;
import com.sendback.global.exception.type.UnAuthorizedException;
import com.sendback.global.exception.type.TokenException;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import jakarta.servlet.http.HttpServletRequest;
Expand All @@ -14,12 +13,11 @@
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.security.Key;
import java.util.Base64;
import java.util.Date;

import static com.sendback.domain.auth.exception.AuthExceptionType.*;
import static com.sendback.domain.user.exception.UserExceptionType.EXPIRED_SIGN_TOKEN;
import static com.sendback.domain.user.exception.UserExceptionType.INVALID_SIGN_TOKEN;

@Getter
Expand All @@ -43,7 +41,6 @@ public class JwtProvider {

public String resolveToken(HttpServletRequest request) {
String bearerToken = request.getHeader(AUTHORIZATION);

if(StringUtils.hasText(bearerToken) && bearerToken.startsWith(BEARER)){
return bearerToken.substring(7);
}
Expand All @@ -56,7 +53,6 @@ public Token issueToken(Long userId) {
return token;
}

//SignToken 생성
public String generateSignToken(SocialUserInfo socialUserInfo, String type) {
Date now = new Date(System.currentTimeMillis());
final Date expiration = new Date(now.getTime() + SIGN_TOKEN_EXPIRE_TIME);
Expand All @@ -75,7 +71,7 @@ public String generateSignToken(SocialUserInfo socialUserInfo, String type) {


private String generateToken(Long userId, boolean isAccessToken) {
final Date now = new Date();
Date now = new Date(System.currentTimeMillis());
final Date expiration = new Date(now.getTime() + (isAccessToken ? ACCESS_TOKEN_EXPIRE_TIME : REFRESH_TOKEN_EXPIRE_TIME));
return Jwts.builder()
.setHeaderParam(Header.TYPE, Header.JWT_TYPE)
Expand Down Expand Up @@ -104,36 +100,20 @@ public SigningUser getSignUserInfo(String signToken) {
public void validateSignToken(String signToken){
try {
getJwtParser().parseClaimsJws(signToken);
} catch (Exception e) {
throw new SignInException(INVALID_SIGN_TOKEN);
}
}

public boolean validateAccessToken(String accessToken) {
try {
getJwtParser().parseClaimsJws(accessToken);
return true;
} catch (ExpiredJwtException e) {
throw new UnAuthorizedException(EXPIRED_ACCESS_TOKEN);
} catch (Exception e) {
throw new UnAuthorizedException(INVALID_ACCESS_TOKEN_VALUE);
throw new TokenException(EXPIRED_SIGN_TOKEN);
} catch (Exception e){
throw new TokenException(INVALID_SIGN_TOKEN);
}
}

public boolean validateRefreshToken(String refreshToken) {
public void validateAccessToken(String accessToken) {
try {
getJwtParser().parseClaimsJws(refreshToken);
return true;
getJwtParser().parseClaimsJws(accessToken);
} catch (ExpiredJwtException e) {
throw new UnAuthorizedException(EXPIRED_REFRESH_TOKEN);
throw new TokenException(EXPIRED_ACCESS_TOKEN);
} catch (Exception e) {
throw new UnAuthorizedException(INVALID_REFRESH_TOKEN_VALUE);
}
}

public void equalsRefreshToken(Long userId, String refreshToken) {
if (!redisService.validateRefreshToken(userId, refreshToken)) {
throw new UnAuthorizedException(NOT_MATCH_REFRESH_TOKEN);
throw new TokenException(INVALID_ACCESS_TOKEN_VALUE);
}
}

Expand All @@ -159,8 +139,12 @@ public Long parseRefreshToken(String token) {
Claims claims = getJwtParser().parseClaimsJws(token).getBody();
return Long.parseLong(claims.getSubject());
}
catch (ExpiredJwtException e) {
throw new TokenException(EXPIRED_REFRESH_TOKEN);
}
catch (Exception ex) {
throw new UnAuthorizedException(INVALID_REFRESH_TOKEN_VALUE);
throw new TokenException(INVALID_REFRESH_TOKEN_VALUE);
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.sendback.global.ControllerTest;
import com.sendback.global.WithMockCustomUser;
import com.sendback.global.exception.type.SignInException;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -38,14 +39,15 @@ class loginKakao {
@Test
@DisplayName("카카오 로그인을 성공하면(기존 회원) 200 상태코드와 함께 access token, refresh token을 반환한다.")
@WithMockCustomUser
@Disabled
void loginKakao_success() throws Exception {

// given
String code = "valid code";
String accessToken = "valid accessToken";
String refreshToken = "valid refreshToken";
given(kakaoService.loginKakao(code)).willReturn(
new TokensResponseDto(accessToken, refreshToken)
new Token(accessToken, refreshToken)
);

// when &then
Expand Down Expand Up @@ -130,7 +132,7 @@ void loginGoogle_success() throws Exception {
String accessToken = "abcdefg";
String refreshToken = "qwerstu";
given(googleService.loginGoogle(code)).willReturn(
new TokensResponseDto(accessToken, refreshToken)
new Token(accessToken, refreshToken)
);

// when &then
Expand Down

0 comments on commit 289aea7

Please sign in to comment.