Skip to content

Commit

Permalink
#5 feat: 네이버 소셜 로그인 API 추가
Browse files Browse the repository at this point in the history
  • Loading branch information
Haeun-Y committed Sep 21, 2023
1 parent 411b0ef commit b13ca84
Show file tree
Hide file tree
Showing 27 changed files with 694 additions and 49 deletions.
16 changes: 16 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,22 @@ dependencies {
testImplementation 'org.springframework.boot:spring-boot-starter-test'

implementation 'mysql:mysql-connector-java:8.0.33'

implementation 'org.apache.tomcat.embed:tomcat-embed-jasper'
implementation 'org.springframework.boot:spring-boot-starter-webflux'

//implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'io.jsonwebtoken:jjwt:0.9.1'

// com.sun.xml.bind
implementation 'com.sun.xml.bind:jaxb-impl:4.0.1'
implementation 'com.sun.xml.bind:jaxb-core:4.0.1'
// javax.xml.bind
implementation 'javax.xml.bind:jaxb-api:2.4.0-b180830.0359'
implementation 'net.pwall.json:jsonutil:5.0'

implementation 'com.googlecode.json-simple:json-simple:1.1.1'

}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.ewhatever.qna.answer.service.AnswerService;
import com.ewhatever.qna.common.Base.BaseException;
import com.ewhatever.qna.common.Base.BaseResponse;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

Expand All @@ -20,9 +21,10 @@ public class AnswerController {
*/
@ResponseBody
@PostMapping("")
public BaseResponse<String> addAnswer(@RequestBody PostAnswerReq postAnswerReq, Long userIdx) {
public BaseResponse<String> addAnswer(HttpServletRequest request,
@RequestBody PostAnswerReq postAnswerReq) {
try {
answerService.addAnswer(postAnswerReq); //TODO: authService.getUserIdx()로 수정
answerService.addAnswer(request.getHeader("Authorization"), postAnswerReq);
return new BaseResponse<>(SUCCESS);
} catch (BaseException e) {
return new BaseResponse<>(e.getStatus());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
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.dto.AuthService;
import com.ewhatever.qna.post.entity.Post;
import com.ewhatever.qna.post.repository.PostRepository;
import com.ewhatever.qna.user.entity.User;
Expand All @@ -25,10 +26,12 @@ public class AnswerService {
private final AnswerRepository answerRepository;
private final UserService userService;

private final AuthService authService;

@Transactional(rollbackFor = Exception.class)
public void addAnswer(PostAnswerReq postAnswerReq) throws BaseException {
public void addAnswer(String token, PostAnswerReq postAnswerReq) throws BaseException {
try {
User user = userRepository.findByUserIdxAndStatusEquals(userService.getUserIdx(), ACTIVE).orElseThrow(() -> new BaseException(INVALID_USER));
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)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.ewhatever.qna.common.Base.BaseException;
import com.ewhatever.qna.common.Base.BaseResponse;
import com.ewhatever.qna.common.Base.BaseResponseStatus;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.hibernate.persister.entity.mutation.UpdateCoordinator;
import org.springframework.web.bind.annotation.*;
Expand All @@ -21,8 +22,9 @@ public class CommentController {

@ResponseBody
@PostMapping
public BaseResponse<String> addComment(@RequestBody PostCommentReq postCommentReq) throws BaseException {
commentService.addComment(postCommentReq);
public BaseResponse<String> addComment(HttpServletRequest request,
@RequestBody PostCommentReq postCommentReq) throws BaseException {
commentService.addComment(request.getHeader("Authorization"), postCommentReq);
return new BaseResponse<>(SUCCESS);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
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.dto.AuthService;
import com.ewhatever.qna.post.entity.Post;
import com.ewhatever.qna.post.repository.PostRepository;
import com.ewhatever.qna.user.repository.UserRepository;
Expand All @@ -22,19 +23,21 @@ public class CommentService {
private final PostRepository postRepository;
private final UserRepository userRepository;
private final CommentRepository commentRepository;
private final AuthService authService;
@Transactional
public void addComment(PostCommentReq postCommentReq) throws BaseException {//TODO : service를 어디서 호출해야할지..
public void addComment(String token, PostCommentReq postCommentReq) throws BaseException {//TODO : service를 어디서 호출해야할지..
Post post = postRepository.findById(postCommentReq.getPostIdx()).orElseThrow(()-> new BaseException(INVALID_POST_IDX));
Comment comment = Comment.builder().content(postCommentReq.getContent())
.post(post)
.writer(userRepository.findById(1L).orElseThrow(()-> new BaseException(INVALID_USER))).build();//TODO : getUserIdx로 수정하기
.writer(userRepository.findById(authService.getUserIdx(token)).orElseThrow(()-> new BaseException(INVALID_USER))).build();
commentRepository.save(comment).getCommentIdx();
post.setCommentCount(post.getCommentCount()+1L);
}

@Transactional
public void deleteComment(Long commentIdx) throws BaseException {
//commentRepository.deleteById(commentIdx);
//TODO : 작성자가 아니면 Exception 발생
Comment comment = commentRepository.findById(commentIdx).orElseThrow(()-> new BaseException(INVALID_COMMENT_IDX));
comment.setStatus(INACTIVE);
Post post = postRepository.findById(comment.getPost().getPostIdx()).orElseThrow(() -> new BaseException(INVALID_POST_IDX));
Expand All @@ -43,6 +46,7 @@ public void deleteComment(Long commentIdx) throws BaseException {

@Transactional
public void updateComment(Long commentIdx, UpdateCommentReq updateCommentReq) throws BaseException {
//TODO : 작성자가 아니면 Exception 발생
Comment comment = commentRepository.findById(commentIdx).orElseThrow(()-> new BaseException(INVALID_COMMENT_IDX));
comment.setContent(updateCommentReq.getContent());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public enum BaseResponseStatus {
NO_SENIOR_ROLE(false, 2400, "시니가 아닙니다."),
INVALID_USER(false, 2401, "존재하지 않는 사용자에 대한 요청입니다."),//TODO : IDX 로 수정하기
NO_JUNIOR_ROLE(false, 2402, "쥬니가 아닙니다."),
NO_VALID_TOKEN(false, 2403, "유효한 토큰이 아닙니다"),

// question(2500-2501)

Expand All @@ -47,6 +48,9 @@ public enum BaseResponseStatus {
NULL_POST(false, 3300, "쥬시글이 없습니다."),

// user(3400-3499)
//TODO : 4000번대에 가야하는지 생각해보기
NAVER_READ_BODY_FAILED(false, 3400, "네이버 회원 정보 조회 API 응답 바디를 읽는데 실패했습니다."),
NAVER_ACCESS_FAILED(false, 3401, "네이버 회원 프로필 접근에 실패하였습니다."),

// question(3500-3599)
NULL_QUESTION(false, 3500, "질문이 없습니다."),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.ewhatever.qna.common.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfiguration implements WebMvcConfigurer {

@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:3000") //TODO : 프론트 베포 URL 추가
.allowedMethods("GET", "POST", "PATCH", "PUT", "DELETE");
}
}
6 changes: 5 additions & 1 deletion src/main/java/com/ewhatever/qna/common/enums/Role.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package com.ewhatever.qna.common.enums;

public enum Role {
SINY, JUNY
SINY, JUNY;
private static final String PREFIX = "ROLE_";
public String getAuthority(){
return PREFIX + this.name();
}
}
64 changes: 64 additions & 0 deletions src/main/java/com/ewhatever/qna/login/JwtAuthProvider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.ewhatever.qna.login;

import com.ewhatever.qna.common.Base.BaseException;
import com.ewhatever.qna.login.dto.JwtTokenDto;
import io.jsonwebtoken.Claims;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

@Component
@RequiredArgsConstructor
public class JwtAuthProvider {

//private final UserDetailsService userDetailsService;
private final JwtIssuer jwtIssuer;

// 인증용
public boolean validateToken(String token) throws BaseException {
if (!StringUtils.hasText(token)) {
return false;
}
Claims claims = jwtIssuer.getClaims(token);
if (claims == null) {
return false;
}

/*
* 추가 검증 로직
*/

return true;
}

// 재발급용
public boolean validateToken(JwtTokenDto jwtDto) throws BaseException {
if (!StringUtils.hasText(jwtDto.getAccessToken())
|| !StringUtils.hasText(jwtDto.getRefreshToken())) {
return false;
}

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 수정
/*
public Authentication getAuthentication(String token) throws BaseException {
Claims claims = jwtIssuer.getClaims(token);
String userIdxStr = jwtIssuer.getSubject(claims);
UserDetails userDetails = userDetailsService.loadUserByUsername(userIdxStr);
return new UsernamePasswordAuthenticationToken(userDetails, null,
userDetails.getAuthorities());
}*/

}
56 changes: 56 additions & 0 deletions src/main/java/com/ewhatever/qna/login/JwtFilter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*package com.ewhatever.qna.login;
import com.ewhatever.qna.common.Base.BaseException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
@Component
@RequiredArgsConstructor
public class JwtFilter extends OncePerRequestFilter {
private final JwtAuthProvider jwtAuthProvider;
public static final String JWT_HEADER_KEY = "Authorization";
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String token = resolveTokenFromRequest(request);
if (!StringUtils.hasText(token)) {
filterChain.doFilter(request, response);
return;
}
try {
if (jwtAuthProvider.validateToken(token)) {
Authentication auth = jwtAuthProvider.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(auth);
}
} catch (BaseException e) {
throw new RuntimeException(e);
}
filterChain.doFilter(request, response);
}
private String resolveTokenFromRequest(HttpServletRequest request) {
String token = request.getHeader(JWT_HEADER_KEY);
if (!ObjectUtils.isEmpty(token)) {
return token;
}
return null;
}
}*/
74 changes: 74 additions & 0 deletions src/main/java/com/ewhatever/qna/login/JwtIssuer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.ewhatever.qna.login;

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

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

@Component
@RequiredArgsConstructor
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 String ROLE = "role";

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

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

Date now = new Date();

String accessToken = Jwts.builder()
.setClaims(claims)
.setIssuedAt(now)
.setExpiration(new Date(now.getTime() + EXPIRE_TIME))
.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) {
return claims.getSubject();
}

public Claims getClaims(String token) throws BaseException {
Claims claims;
try {
claims = Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
} catch (ExpiredJwtException e) {
claims = e.getClaims();
} catch (Exception e) {
//throw new BadCredentialsException("유효한 토큰이 아닙니다.");
throw new BaseException(BaseResponseStatus.NO_VALID_TOKEN);
}
return claims;
}

}
Loading

0 comments on commit b13ca84

Please sign in to comment.