Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/feat(#45)-heachi-domain-redis'…
Browse files Browse the repository at this point in the history
… into feat(#45)-heachi-domain-redis
  • Loading branch information
jusung-c committed Sep 20, 2023
2 parents 3123204 + c9a0b67 commit 0ac592c
Show file tree
Hide file tree
Showing 10 changed files with 339 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@
import com.heachi.auth.api.service.auth.AuthService;
import com.heachi.auth.api.service.auth.request.AuthServiceRegisterRequest;
import com.heachi.auth.api.service.auth.response.AuthServiceLoginResponse;
import com.heachi.auth.api.service.jwt.JwtTokenDTO;
import com.heachi.auth.api.service.oauth.OAuthService;
import com.heachi.auth.api.service.state.LoginStateService;
import com.heachi.mysql.define.user.User;
import com.heachi.mysql.define.user.constant.UserPlatformType;
import com.heachi.mysql.define.user.constant.UserRole;
import io.swagger.v3.oas.annotations.headers.Header;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
Expand Down Expand Up @@ -71,4 +72,12 @@ public JsonResult<UserSimpleInfoResponse> userInfo(@AuthenticationPrincipal User

return JsonResult.successOf(UserSimpleInfoResponse.of(user));
}

@GetMapping("/logout")
public JsonResult<String> logout(@RequestHeader(name = "Authorization") String token) {
String[] tokens = token.split(" ");
authService.logout(tokens[2]);

return JsonResult.successOf("Logout successfully.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,28 @@

import com.heachi.admin.common.exception.ExceptionMessage;
import com.heachi.admin.common.exception.auth.AuthException;
import com.heachi.admin.common.exception.refreshToken.RefreshTokenException;
import com.heachi.auth.api.service.auth.request.AuthServiceRegisterRequest;
import com.heachi.auth.api.service.auth.response.AuthServiceLoginResponse;
import com.heachi.auth.api.service.jwt.JwtService;
import com.heachi.auth.api.service.jwt.JwtTokenDTO;
import com.heachi.auth.api.service.oauth.OAuthService;
import com.heachi.auth.api.service.oauth.response.OAuthResponse;
import com.heachi.auth.api.service.token.RefreshTokenService;
import com.heachi.mysql.define.user.User;
import com.heachi.mysql.define.user.constant.UserPlatformType;
import com.heachi.mysql.define.user.constant.UserRole;
import com.heachi.mysql.define.user.repository.UserRepository;
import com.heachi.redis.config.RedisConfig;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import com.heachi.redis.define.refreshToken.RefreshToken;
import com.heachi.redis.define.refreshToken.repository.RefreshTokenRepository;
import io.jsonwebtoken.Claims;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;


@Slf4j
Expand All @@ -35,15 +35,11 @@ public class AuthService {
private final UserRepository userRepository;
private final OAuthService oAuthService;
private final JwtService jwtService;

// 빈 주입이 안됨
private final RedisTemplate<String, String> redisTemplacte;
private final RefreshTokenService refreshTokenService;

private static final String ROLE_CLAIM = "role";
private static final String NAME_CLAIM = "name";
private static final String PROFILE_IMAGE_CLAIM = "profileImageUrl";
private static final String ACCESS_TOKEN = "accessToken";
private static final String REFRESH_TOKEN = "refreshToken";

@Transactional
public AuthServiceLoginResponse login(UserPlatformType platformType, String code, String state) {
Expand All @@ -68,18 +64,20 @@ public AuthServiceLoginResponse login(UserPlatformType platformType, String code
// 기존 회원의 경우 name, profileImageUrl 변하면 update
findUser.updateProfile(loginResponse.getName(), loginResponse.getProfileImageUrl());

// JWT Access Token 발급
final String accessToken = createJwtToken(findUser).get(ACCESS_TOKEN);
// JWT Access Token, Refresh Token 발급

// JWT Refresh Token 발급
final String refreshToken = createJwtToken(findUser).get(REFRESH_TOKEN);
JwtTokenDTO tokens = createJwtToken(findUser);

return AuthServiceLoginResponse.builder()
.accessToken(accessToken)
.refreshToken(refreshToken)
.accessToken(tokens.getAccessToken())
.refreshToken(tokens.getRefreshToken())
.role(findUser.getRole())
.build();
}
@Transactional
public void logout(String refreshToken) {
refreshTokenService.logout(refreshToken);
}

@Transactional
public AuthServiceLoginResponse register(AuthServiceRegisterRequest request) {
Expand All @@ -96,20 +94,17 @@ public AuthServiceLoginResponse register(AuthServiceRegisterRequest request) {
// 회원가입 정보 DB 반영
findUser.updateRegister(request.getRole(), request.getPhoneNumber());

// JWT Access Token 재발급
final String accessToken = createJwtToken(findUser).get(ACCESS_TOKEN);

// JWT Refresh Token 재발급
final String refreshToken = createJwtToken(findUser).get(REFRESH_TOKEN);
// JWT Access Token, Refresh Token 재발급
JwtTokenDTO tokens = createJwtToken(findUser);

return AuthServiceLoginResponse.builder()
.accessToken(accessToken)
.refreshToken(refreshToken)
.accessToken(tokens.getAccessToken())
.refreshToken(tokens.getRefreshToken())
.role(findUser.getRole())
.build();
}

private Map<String, String> createJwtToken(User user) {
private JwtTokenDTO createJwtToken(User user) {
// JWT 토큰 생성을 위한 claims 생성
HashMap<String, String> claims = new HashMap<>();
claims.put(ROLE_CLAIM, user.getRole().name());
Expand All @@ -121,22 +116,18 @@ private Map<String, String> createJwtToken(User user) {

// Refresh Token 생성
final String refreshToken = jwtService.generateRefreshToken(claims, user);
long rtExpiration = jwtService.extractExpiration(refreshToken).getTime();

// Refresh Token 저장 - REDIS
redisTemplacte.opsForValue().set(
user.getEmail(),
refreshToken,
jwtService.extractExpiration(refreshToken).getTime(),
TimeUnit.MICROSECONDS
);

final Map<String, String> tokens = new HashMap<String, String>();

// Refresh Token 저장
tokens.put(ACCESS_TOKEN, accessToken);
tokens.put(REFRESH_TOKEN, refreshToken);
RefreshToken rt = RefreshToken.builder()
.refreshToken(refreshToken)
.email(user.getEmail())
.build();
refreshTokenService.saveRefreshToken(rt);

// 로그인 반환 객체 생성
return tokens;
return JwtTokenDTO.builder()
.accessToken(accessToken)
.refreshToken(refreshToken)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,6 @@ public class JwtService {
@Value("${jwt.secretKey}")
private String secretKey;

@Value("${jwt.token.access-expiration-time}")
private long accessExpirationTime;

@Value("${jwt.token.refresh-expiration-time}")
private long refreshExpirationTime;

/*
* Token에서 사용자 이름 추출
*/
Expand All @@ -47,11 +41,11 @@ private <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
* AccessToken 생성
*/
public String generateAccessToken(UserDetails userDetails) {
return generateAccessToken(new HashMap<>(), userDetails, new Date(System.currentTimeMillis() + accessExpirationTime));
return generateAccessToken(new HashMap<>(), userDetails, new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24));
}

public String generateAccessToken(Map<String, String> extraClaims, UserDetails userDetails) {
return generateAccessToken(extraClaims, userDetails, new Date(System.currentTimeMillis() + accessExpirationTime));
return generateAccessToken(extraClaims, userDetails, new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24));
}

public String generateAccessToken(Map<String, String> extraClaims, UserDetails userDetails, Date expiredTime) {
Expand All @@ -68,11 +62,11 @@ public String generateAccessToken(Map<String, String> extraClaims, UserDetails u
* RefreshToken 생성
*/
public String generateRefreshToken(UserDetails userDetails) {
return generateAccessToken(new HashMap<>(), userDetails, new Date(System.currentTimeMillis() + refreshExpirationTime));
return generateAccessToken(new HashMap<>(), userDetails, new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 7));
}

public String generateRefreshToken(Map<String, String> extraClaims, UserDetails userDetails) {
return generateRefreshToken(extraClaims, userDetails, new Date(System.currentTimeMillis() + refreshExpirationTime));
return generateRefreshToken(extraClaims, userDetails, new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 7));
}

public String generateRefreshToken(Map<String, String> extraClaims, UserDetails userDetails, Date expiredTime) {
Expand Down Expand Up @@ -108,7 +102,7 @@ public boolean isTokenValid(String token, String username) {
return (claimsSubject.equals(username)) && !isTokenExpired(token);
}

private boolean isTokenExpired(String token) {
public boolean isTokenExpired(String token) {
return extractExpiration(token).before(new Date());
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.heachi.auth.api.service.jwt;

import lombok.Builder;
import lombok.Getter;

@Getter
public class JwtTokenDTO {
private String accessToken;
private String refreshToken;

@Builder
public JwtTokenDTO(String accessToken, String refreshToken) {
this.accessToken = accessToken;
this.refreshToken = refreshToken;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ public void logout(String refreshToken) {
}

public void saveRefreshToken(RefreshToken refreshToken) {
refreshTokenRepository.save(refreshToken);
log.info(">>>> Refresh Token register : {}", refreshToken);
RefreshToken savedToken = refreshTokenRepository.save(refreshToken);
log.info(">>>> Refresh Token register : {}", savedToken);
}

// public String findByKey(String key) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,4 +255,50 @@ void userSimpleInfoTestWhenInvalidToken() throws Exception {
.andExpect(status().isOk())
.andExpect(jsonPath("$.resCode").value(400));
}

@Test
@DisplayName("로그아웃 실패 테스트 - 잘못된 토큰으로 요청시 예외 발생")
void logoutTestWhenInvalidToken() throws Exception {
String accessToken = "strangeToken";
String refreshToken = "strangeToken";

// when
mockMvc.perform(
get("/auth/logout")
.header("Authorization", "Bearer " + accessToken + " " + refreshToken))

// then
.andExpect(status().isOk())
.andExpect(jsonPath("$.resCode").value(400));
}

@Test
@DisplayName("로그아웃 성공 테스트")
void logoutSuccessTest() throws Exception {
// given
User user = User.builder()
.name("김민수")
.role(UserRole.USER)
.email("[email protected]")
.profileImageUrl("https://google.com")
.build();
User savedUser = userRepository.save(user);

HashMap<String, String> map = new HashMap<>();
map.put("role", savedUser.getRole().name());
map.put("name", savedUser.getName());
map.put("profileImageUrl", savedUser.getProfileImageUrl());
String accessToken = jwtService.generateAccessToken(map, savedUser);
String refreshToken = jwtService.generateRefreshToken(map, savedUser);


// when
mockMvc.perform(get("/auth/logout")
.contentType(MediaType.APPLICATION_JSON)
.header("Authorization", "Bearer " + accessToken + " " + refreshToken))
// then
.andExpect(status().isOk())
.andExpect(jsonPath("$.resCode").value(200))
.andExpect(jsonPath("$.resObj").value("Logout successfully."));
}
}
Loading

0 comments on commit 0ac592c

Please sign in to comment.