Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
JoongHyun-Kim committed Sep 24, 2023
2 parents 52207b6 + 86e7b90 commit 7b37d78
Show file tree
Hide file tree
Showing 27 changed files with 354 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import com.ewhatever.qna.answer.entity.Answer;
import com.ewhatever.qna.answer.repository.AnswerRepository;
import com.ewhatever.qna.common.Base.BaseException;
import com.ewhatever.qna.login.CustomUnauthorizedException;
import com.ewhatever.qna.login.JwtIssuer;
import com.ewhatever.qna.login.dto.AuthService;
import com.ewhatever.qna.post.entity.Post;
import com.ewhatever.qna.post.repository.PostRepository;
Expand All @@ -16,7 +18,7 @@

import static com.ewhatever.qna.common.Base.BaseResponseStatus.*;
import static com.ewhatever.qna.common.Constant.Status.ACTIVE;
import static com.ewhatever.qna.common.enums.Role.SINY;
import static com.ewhatever.qna.common.enums.Role.Cyni;

@Service
@RequiredArgsConstructor
Expand All @@ -28,13 +30,16 @@ public class AnswerService {

private final AuthService authService;

private final JwtIssuer jwtIssuer;

@Transactional(rollbackFor = Exception.class)
public void addAnswer(String token, PostAnswerReq postAnswerReq) throws BaseException {
if(!jwtIssuer.validateToken(token)) throw new CustomUnauthorizedException(INVALID_TOKEN.getMessage());
try {
User user = userRepository.findByUserIdxAndStatusEquals(authService.getUserIdx(token), ACTIVE).orElseThrow(() -> new BaseException(INVALID_USER));
Post post = postRepository.findById(postAnswerReq.getPostIdx()).orElseThrow(() -> new BaseException(INVALID_POST_IDX));
Long currentAnswerCount = answerRepository.countByPost(post);
if (user.getRole().equals(SINY)) {
if (user.getRole().equals(Cyni)) {
if (currentAnswerCount < 3) { // 크론잡 주기 사이에 답변이 등록되어 isJuicy 컬럼값에 아직 반영이 안된 경우를 위해 예외처리
Answer answer = Answer.builder()
.content(postAnswerReq.getAnswer())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public BaseResponse<String> addComment(HttpServletRequest request,

@ResponseBody
@PatchMapping("/{commentIdx}")
public BaseResponse<String> updateComment(@PathVariable Long commentIdx, @RequestBody UpdateCommentReq updateCommentReq) throws BaseException {
public BaseResponse<String> updateComment(HttpServletRequest request, @PathVariable Long commentIdx, @RequestBody UpdateCommentReq updateCommentReq) throws BaseException {
commentService.updateComment(commentIdx, updateCommentReq);
return new BaseResponse<>(SUCCESS);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import com.ewhatever.qna.comment.entity.Comment;
import com.ewhatever.qna.comment.repository.CommentRepository;
import com.ewhatever.qna.common.Base.BaseException;
import com.ewhatever.qna.login.CustomUnauthorizedException;
import com.ewhatever.qna.login.JwtIssuer;
import com.ewhatever.qna.login.dto.AuthService;
import com.ewhatever.qna.post.entity.Post;
import com.ewhatever.qna.post.repository.PostRepository;
Expand All @@ -24,8 +26,11 @@ public class CommentService {
private final UserRepository userRepository;
private final CommentRepository commentRepository;
private final AuthService authService;

private final JwtIssuer jwtIssuer;
@Transactional
public void addComment(String token, PostCommentReq postCommentReq) throws BaseException {//TODO : service를 어디서 호출해야할지..
if(!jwtIssuer.validateToken(token)) throw new CustomUnauthorizedException(INVALID_TOKEN.getMessage());
Post post = postRepository.findById(postCommentReq.getPostIdx()).orElseThrow(()-> new BaseException(INVALID_POST_IDX));
Comment comment = Comment.builder().content(postCommentReq.getContent())
.post(post)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ public enum BaseResponseStatus {
NO_SENIOR_ROLE(false, 2400, "시니가 아닙니다."),
INVALID_USER(false, 2401, "존재하지 않는 사용자에 대한 요청입니다."),//TODO : IDX 로 수정하기
NO_JUNIOR_ROLE(false, 2402, "쥬니가 아닙니다."),
NO_VALID_TOKEN(false, 2403, "유효한 토큰이 아닙니다"),
INVALID_TOKEN(false, 2403, "유효한 토큰이 아닙니다"),
UNMATCHED_REFRESH_TOKEN(false, 2404, "리프레시 토큰이 일치하지 않습니다."),

// question(2500-2501)

Expand All @@ -51,6 +52,7 @@ public enum BaseResponseStatus {
//TODO : 4000번대에 가야하는지 생각해보기
NAVER_READ_BODY_FAILED(false, 3400, "네이버 회원 정보 조회 API 응답 바디를 읽는데 실패했습니다."),
NAVER_ACCESS_FAILED(false, 3401, "네이버 회원 프로필 접근에 실패하였습니다."),
NAVER_ACCESS_TOKEN_FAILED(false, 3402, "네이버 접근 토큰 발급에 실패하였습니다."),

// question(3500-3599)
NULL_QUESTION(false, 3500, "질문이 없습니다."),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

import com.ewhatever.qna.common.Base.BaseException;
import com.ewhatever.qna.common.Base.BaseResponse;
import com.ewhatever.qna.login.CustomUnauthorizedException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

Expand All @@ -13,4 +16,9 @@ protected BaseResponse<?> handleBaseException(BaseException e) {
return new BaseResponse<>(e.getStatus());
}

@ExceptionHandler(CustomUnauthorizedException.class)
protected ResponseEntity<?> handleNoSuchElementFoundException(CustomUnauthorizedException e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(e.getMessage());
}

}
2 changes: 1 addition & 1 deletion src/main/java/com/ewhatever/qna/common/enums/Role.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.ewhatever.qna.common.enums;

public enum Role {
SINY, JUNY;
Cyni, Juni;
private static final String PREFIX = "ROLE_";
public String getAuthority(){
return PREFIX + this.name();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.ewhatever.qna.login;

public class CustomUnauthorizedException extends RuntimeException {
public CustomUnauthorizedException() {
super("Unauthorized");
}

public CustomUnauthorizedException(String message) {
super(message);
}
}
16 changes: 8 additions & 8 deletions src/main/java/com/ewhatever/qna/login/JwtAuthProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.ewhatever.qna.common.Base.BaseException;
import com.ewhatever.qna.login.dto.JwtTokenDto;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
Expand All @@ -14,7 +15,10 @@ public class JwtAuthProvider {
//private final UserDetailsService userDetailsService;
private final JwtIssuer jwtIssuer;



// 인증용
/*
public boolean validateToken(String token) throws BaseException {
if (!StringUtils.hasText(token)) {
return false;
Expand All @@ -24,13 +28,11 @@ public boolean validateToken(String token) throws BaseException {
return false;
}
/*
* 추가 검증 로직
*/

//추가 검증 로직
return true;
}
// 재발급용
public boolean validateToken(JwtTokenDto jwtDto) throws BaseException {
if (!StringUtils.hasText(jwtDto.getAccessToken())
Expand All @@ -41,13 +43,11 @@ public boolean validateToken(JwtTokenDto jwtDto) throws BaseException {
Claims accessClaims = jwtIssuer.getClaims(jwtDto.getAccessToken());
Claims refreshClaims = jwtIssuer.getClaims(jwtDto.getRefreshToken());
/*
* 추가 검증 로직
*/
//추가 검증 로직
return accessClaims != null && refreshClaims != null
&& jwtIssuer.getSubject(accessClaims).equals(jwtIssuer.getSubject(refreshClaims));
}
}*/


//TODO : userIdxStr 수정
Expand Down
69 changes: 47 additions & 22 deletions src/main/java/com/ewhatever/qna/login/JwtIssuer.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,56 +2,65 @@

import com.ewhatever.qna.common.Base.BaseException;
import com.ewhatever.qna.common.Base.BaseResponseStatus;
import com.ewhatever.qna.login.dto.GetRefreshedAccessTokenRes;
import com.ewhatever.qna.login.dto.JwtTokenDto;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.*;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.Base64;
import java.util.Date;

@Component
@RequiredArgsConstructor
@Slf4j
public class JwtIssuer {

private static String SECRET_KEY = "secretKeyForJsonWebTokenTutorial";
public static final long EXPIRE_TIME = 1000 * 60 * 5;
public static final long REFRESH_EXPIRE_TIME = 1000 * 60 * 15;
public static final long EXPIRE_TIME = 1000 * 60 * 2;//5분 // TODO : 만료시간
public static final long REFRESH_EXPIRE_TIME = 1000 * 60 * 15;//15분 //TODO : 만료시간 재설정
public static final String ROLE = "role";

@PostConstruct
void init(){
SECRET_KEY = Base64.getEncoder().encodeToString(SECRET_KEY.getBytes());
}

public JwtTokenDto createToken(Long userIdx, String role) {
public JwtTokenDto createTokens(Long userIdx, String role) throws BaseException {

String accessToken = this.createToken(userIdx, role, EXPIRE_TIME);

String refreshToken = this.createToken(userIdx, role, REFRESH_EXPIRE_TIME);

return JwtTokenDto.builder()
.accessToken(accessToken)
.refreshToken(refreshToken)
.exp(this.getClaims(accessToken).get("exp", Date.class).getTime()/1000)
.build();
}
public GetRefreshedAccessTokenRes createRefreshedAccessToken(Long userIdx, String role) throws BaseException {
String accessToken = this.createToken(userIdx, role, EXPIRE_TIME);

return GetRefreshedAccessTokenRes.builder()
.accessToken(accessToken)
.exp(this.getClaims(accessToken).get("exp", Date.class).getTime()/1000)
.build();
}

public String createToken(Long userIdx, String role, long exp) {
Claims claims = Jwts.claims().setSubject(String.valueOf(userIdx));
claims.put(ROLE, role);

Date now = new Date();

String accessToken = Jwts.builder()
return Jwts.builder()
.setClaims(claims)
.setIssuedAt(now)
.setExpiration(new Date(now.getTime() + EXPIRE_TIME))
.setExpiration(new Date(now.getTime() + exp))
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();

String refreshToken = Jwts.builder()
.setClaims(claims)
.setIssuedAt(now)
.setExpiration(new Date(now.getTime() + REFRESH_EXPIRE_TIME))
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();

return JwtTokenDto.builder()
.accessToken(accessToken)
.refreshToken(refreshToken)
.build();
}

public String getSubject(Claims claims) {
Expand All @@ -66,9 +75,25 @@ public Claims getClaims(String token) throws BaseException {
claims = e.getClaims();
} catch (Exception e) {
//throw new BadCredentialsException("유효한 토큰이 아닙니다.");
throw new BaseException(BaseResponseStatus.NO_VALID_TOKEN);
throw new BaseException(BaseResponseStatus.INVALID_TOKEN);
}
return claims;
}


public boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token);
return true;
} catch (io.jsonwebtoken.SignatureException | MalformedJwtException e) {
throw new CustomUnauthorizedException("잘못된 JWT 서명입니다.");
} catch (ExpiredJwtException e) {
throw new CustomUnauthorizedException("만료된 JWT 토큰입니다.");
} catch (UnsupportedJwtException e) {
throw new CustomUnauthorizedException("지원되지 않는 JWT 토큰입니다.");
} catch (IllegalArgumentException e) {
throw new CustomUnauthorizedException("JWT 토큰이 잘못되었습니다.");
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import com.ewhatever.qna.common.Base.BaseException;
import com.ewhatever.qna.common.Base.BaseResponse;
import com.ewhatever.qna.common.Base.BaseResponseStatus;
import com.ewhatever.qna.login.dto.GetRefreshedAccessTokenRes;
import com.ewhatever.qna.login.dto.LoginRes;
import com.ewhatever.qna.login.service.LoginService;
import com.fasterxml.jackson.core.JsonProcessingException;
Expand All @@ -12,6 +12,8 @@
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import static com.ewhatever.qna.common.Base.BaseResponseStatus.SUCCESS;

@Controller
@Slf4j
@RequiredArgsConstructor
Expand All @@ -25,10 +27,35 @@ public BaseResponse<LoginRes> callBack(@RequestParam("code") String code,
return new BaseResponse<>(loginService.callback(code, state));
}

@GetMapping(value="/tmp/login/naver", produces = "application/json; charset=UTF8")
@ResponseBody
public BaseResponse<LoginRes> callBack(HttpServletRequest request) throws BaseException, JsonProcessingException {
return new BaseResponse<>(loginService.callback(request.getParameter("code"), request.getParameter("state")));
}
@GetMapping("/login")
public String login() {
return "naver_login";
}

@PostMapping("/logout")
@ResponseBody
public BaseResponse<String> logout(HttpServletRequest request) throws Exception {
loginService.logout(request.getHeader("Authorization"));
return new BaseResponse<>(SUCCESS);
}

@GetMapping("/test")
@ResponseBody
public BaseResponse<String> tmp() {
return new BaseResponse<>(BaseResponseStatus.SUCCESS);
public BaseResponse<String> test() {
return new BaseResponse<>(SUCCESS);
}

@GetMapping("/login/token/refresh")
@ResponseBody
public BaseResponse<GetRefreshedAccessTokenRes> refresh(HttpServletRequest request) throws BaseException {
return new BaseResponse<>(loginService.refresh(request.getHeader("Authorization")));
}



}
30 changes: 30 additions & 0 deletions src/main/java/com/ewhatever/qna/login/dto/DeleteNaverTokenRes.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.ewhatever.qna.login.dto;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import lombok.*;

@NoArgsConstructor
@AllArgsConstructor
@ToString
@Setter
@Getter
@JsonPropertyOrder({"access_token", "result", "expires_in", "error", "error_description"})
public class DeleteNaverTokenRes {

@JsonProperty("access_token")
private String accessToken;//삭제 처리된 접근 토큰 값

@JsonProperty("result")
private String result;//처리 결과가 성공이면 'success'가 리턴

@JsonProperty("expires_in")
private int expiredIn;//접근 토큰의 유효 기간(초 단위)

@JsonProperty("error")
private String error;//에러 코드

@JsonProperty("error_description")
private String errorDescription;//에러 메시지

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.ewhatever.qna.login.dto;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.*;

@Builder
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class GetRefreshedAccessTokenRes {

@JsonProperty("access_token")
private String accessToken;

private Long exp;
}
Loading

0 comments on commit 7b37d78

Please sign in to comment.