Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: 알림나무 갱신 설정 변경, 쿠키설정 변경 #104

Merged
merged 4 commits into from
Jul 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/main/java/com/yapp/betree/BeTreeApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.scheduling.annotation.EnableScheduling;

@EnableScheduling
@EnableJpaAuditing
@SpringBootApplication
public class BeTreeApplication {
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/com/yapp/betree/controller/OAuthController.java
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ private ResponseEntity<Void> buildTokenResponse(HttpServletResponse response, Jw
.maxAge(24 * 60 * 60 * 7)
.path("/")
.secure(true)
.sameSite("None")
// .sameSite("None")
.httpOnly(true)
.build();
response.setHeader(SET_COOKIE_HEADER, cookie.toString());
Expand All @@ -117,7 +117,7 @@ public ResponseEntity<Void> logout(@ApiIgnore @LoginUser LoginUserDto loginUser,
.maxAge(0)
.path("/")
.secure(true)
.sameSite("None")
// .sameSite("None")
.httpOnly(true)
.build();
response.setHeader(SET_COOKIE_HEADER, cookie.toString());
Expand Down
42 changes: 42 additions & 0 deletions src/main/java/com/yapp/betree/interceptor/TokenInterceptor.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@
@Slf4j
public class TokenInterceptor implements HandlerInterceptor {

@Value("dev.email.js")
private String jisu;
@Value("dev.email.sb")
private String subin;
@Value("dev.email.si")
private String sooim;
@Value("dev.email.ym")
private String ym;
@Value("dev.email.yk")
private String yk;

private static final String AUTH_TYPE = "Bearer";
public static final String USER_ATTR_KEY = "user";
Expand Down Expand Up @@ -51,6 +61,19 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons
.orElseThrow(() -> new BetreeException(ErrorCode.USER_TOKEN_ERROR, "헤더에 토큰이 존재하지 않습니다."));


if (!isFrontEndLocal(authHeader) && isInvalidRefreshToken(request.getCookies())) {
log.info("[리프레시토큰 검증] 비어있으면 실패");
if (request.getRequestURI().equals("/api/logout")) {
log.info("[리프레시토큰 검증] 이미로그아웃");
throw new BetreeException(ErrorCode.USER_ALREADY_LOGOUT_TOKEN);
}
log.info("[리프레시토큰 검증] 예외생성");
response.sendError(ErrorCode.USER_REFRESH_ERROR.getStatus(), ErrorCode.USER_REFRESH_ERROR.getMessage());
return false;
}


log.info("[리프레시토큰 검증] 성공");
if (authHeader.startsWith(AUTH_TYPE)) {
authHeader = authHeader.substring(AUTH_TYPE.length()).trim();
}
Expand Down Expand Up @@ -79,6 +102,25 @@ private void printUserLog(HttpServletRequest request) {
log.info("[요청 유저 정보] ip:{}, forward:{}, proto:{}", ip, forward, proto);
}

private boolean isFrontEndLocal(String authHeader) {
// FE, BE 개발자들 로그인할때는 토큰검증 임시로 예외처리->로컬개발 가능하게 하려고
List<String> devEmails = new ArrayList<>();
devEmails.add(jisu);
devEmails.add(subin);
devEmails.add(yk);
devEmails.add(ym);
devEmails.add(sooim);

authHeader = authHeader.substring(AUTH_TYPE.length()).trim();
Claims claims = jwtTokenProvider.parseToken(authHeader);
if (Objects.isNull(claims)) {
throw new BetreeException(ErrorCode.USER_TOKEN_ERROR, "토큰의 payload는 null일 수 없습니다.");
}

log.info("[개발자 로그인] info : {}", claims.get("email"));
return devEmails.contains(claims.get("email"));
}

private boolean isInvalidRefreshToken(Cookie[] cookies) {
if (Objects.isNull(cookies)) {
return true;
Expand Down
22 changes: 20 additions & 2 deletions src/main/java/com/yapp/betree/service/NoticeTreeService.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,14 @@
import com.yapp.betree.util.BetreeUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
Expand All @@ -37,6 +40,7 @@ public class NoticeTreeService {
private final UserService userService;



@Transactional
public NoticeResponseDto getUnreadMessages(Long userId) {
NoticeTree noticeTree = noticeTreeRepository.findByUserId(userId).orElseGet(
Expand Down Expand Up @@ -65,8 +69,10 @@ public NoticeResponseDto getUnreadMessages(Long userId) {
return new NoticeResponseDto(messageRepository.findByUserIdAndAlreadyReadAndDelByReceiver(userId, false, false).size(), messages);
}

@Scheduled(cron = "0 * */1 * * *") // 1시간마다 갱신
@Transactional
public void batchNoticeTree() {
log.info("실행 시간 {}", LocalDateTime.now());
// 전체 유저 조회
List<User> users = userRepository.findAll();
for (User user : users) {
Expand All @@ -87,32 +93,44 @@ public void batchNoticeTree(Long userId) {
* @param userId
*/
private void noticeTree(Long userId) {
log.info("...유저 {} 알림나무 생성중 ...", userId);
// 안읽은 메시지
List<Message> unreadMessages = messageRepository.findByUserIdAndAlreadyReadAndDelByReceiver(userId, false, false);

// 안읽은 메시지 랜덤하게 sorting
Collections.shuffle(unreadMessages);

// 안읽은 메시지 먼저 8개 리스트에 넣음
Set<MessageResponseDto> noticeTreeMessages = new HashSet<>();
for (Message m : unreadMessages) {
SendUserDto sender = userService.findBySenderId(m.getSenderId());
noticeTreeMessages.add(MessageResponseDto.of(m, sender));
}

log.info("...유저 {} 알림나무 생성중 ...안읽은 메시지 {}개 총 {}개 ", userId, unreadMessages.size(), noticeTreeMessages.size());

// 즐겨찾기 메시지
List<Message> favoriteMessages = messageRepository.findAllByUserIdAndFavoriteAndDelByReceiver(userId, true, false);

// 랜덤 셔플
Collections.shuffle(favoriteMessages);
for (Message m : favoriteMessages) {
if (noticeTreeMessages.size() >= 8) {
break; // 8개까지만 담음
}
SendUserDto sender = userService.findBySenderId(m.getSenderId());
noticeTreeMessages.add(MessageResponseDto.of(m, sender));
}
log.info("...유저 {} 알림나무 생성중 ...즐겨찾기 메시지 {}개 총 {}개 ", userId, favoriteMessages.size(), noticeTreeMessages.size());

// 비트리 제공 메시지로 8개까지 다시 채움
long remainCount = 8 - noticeTreeMessages.size();
for (long i = 1; i <= remainCount; i++) {
noticeTreeMessages.add(BetreeUtils.getBetreeMessage(i));
List<Long> betreeMessageNumber = BetreeUtils.getRandomNum(remainCount);
for(Long number : betreeMessageNumber) {
noticeTreeMessages.add(BetreeUtils.getBetreeMessage(number));
}

log.info("...유저 {} 알림나무 생성중 ...비트리 메시지 {}개 총 {}개 ", userId, remainCount, noticeTreeMessages.size());
log.info("[유저 알림나무 갱신 - 리스트 생성] userId = {}, 알림나무 = {}", userId, noticeTreeMessages);
String unreads = noticeTreeMessages
.stream()
Expand Down
30 changes: 21 additions & 9 deletions src/main/java/com/yapp/betree/util/BetreeUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@
import com.yapp.betree.dto.oauth.OAuthUserInfoDto;
import com.yapp.betree.dto.response.MessageResponseDto;

import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.LongStream;

public class BetreeUtils {

Expand All @@ -15,17 +19,25 @@ public class BetreeUtils {
private static final String BASE_IMAGE_URL = "image/v2/user_";
private static final String BASE_IMAGE_SUFFIX = ".svg";
public static final ConcurrentHashMap<Long, String> betreeMessages = new ConcurrentHashMap<Long, String>() {{
put(-1L, "칭찬메시지1");
put(-2L, "칭찬메시지2");
put(-3L, "칭찬메시지3");
put(-4L, "칭찬메시지4");
put(-5L, "칭찬메시지5");
put(-6L, "칭찬메시지6");
put(-7L, "칭찬메시지7");
put(-8L, "칭찬메시지8");

put(-1L, "나무에 물을 주고 나만의 비트리를 길러보세요!");
put(-2L, "조금만 더 힘 내봅시다!!");
put(-3L, "여러분 화이팅");
put(-4L, "넣을만한 좋은 칭찬문구 추천부탁드려요");
put(-5L, "칭찬메시지 5");
put(-6L, "칭찬메시지 6");
put(-7L, "칭찬메시지 7");
put(-8L, "칭찬메시지 8");
put(-9L, "칭찬메시지 9");
put(-10L, "칭찬메시지 10");
put(-11L, "칭찬메시지 11");
}};

public static List<Long> getRandomNum(long size) {
List<Long> candidate = LongStream.range(1, betreeMessages.size()+1).boxed().collect(Collectors.toList());
Collections.shuffle(candidate);
return candidate.subList(0, (int)size);
}

// 접속 URL ""로 리턴 -> 프론트에서 알아서 처리
public static String makeUserAccessUrl(OAuthUserInfoDto user) {
return "";
Expand Down
12 changes: 10 additions & 2 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,13 @@ secrets:
jwt:
token:
secret-key: B1e2t3r4e5e6S7e8c9r0e1t
expiration-time: 86400000
refresh-expiration-time: 86400000
expiration-time: 600000
refresh-expiration-time: 86400000

dev:
email:
js: default
sb: default
si: default
yk: default
ym: default
14 changes: 3 additions & 11 deletions src/test/java/com/yapp/betree/controller/AcceptanceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,9 @@ void createMessagesNoLoginUserTest() throws Exception {
assertThat(sender.getNickname()).isEqualTo("익명");
}


//랜덤설정으로 테스트불가
@Disabled
@Test
@DisplayName("알림나무 읽음처리 테스트")
void noticeTreeTest() throws Exception {
Expand Down Expand Up @@ -361,17 +364,6 @@ void noticeTreeTest() throws Exception {
assertThat(noticeResponseDto2.getMessages()).hasSize(5);
assertThat(noticeResponseDto2.getTotalUnreadMessageCount()).isEqualTo(2);

// 읽은메시지 볼 수 없음
assertThat(
noticeResponseDto2.getMessages().stream()
.filter(messageResponseDto -> messageResponseDto.getId() == message1.getId())
.count()
).isEqualTo(0);
assertThat(
noticeResponseDto2.getMessages().stream()
.filter(messageResponseDto -> messageResponseDto.getId() == message2.getId())
.count()
).isEqualTo(0);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,6 @@ void alreadyLogoutTest() throws Exception {
assertThat(mvcResult.getResponse().getHeader("Authorization")).isNull();
}

// 쿠키 설정 제거로 테스트불가능
@Disabled
@Test
@DisplayName("로그아웃 - 이미 로그아웃된 유저는 예외메시지를 반환한다.")
void deleteRefreshTokenTest() throws Exception {
Expand All @@ -138,8 +136,6 @@ void deleteRefreshTokenTest() throws Exception {
assertThat(mvcResult.getResponse().getHeader("Authorization")).isNull();
}

//쿠키 설정 제거로 테스트 불가능
@Disabled
@Test
@DisplayName("이미 로그아웃되어 헤더(쿠키)에 리프레시 토큰이 존재하지 않을경우 예외가 발생한다.")
void refreshTokenCookieNullTest() throws Exception {
Expand Down