Skip to content

Commit

Permalink
Merge pull request #55 from kakao-tech-campus-2nd-step3/Master
Browse files Browse the repository at this point in the history
14조 코드리뷰 4회차
  • Loading branch information
leaf-upper authored Oct 28, 2024
2 parents 9580507 + 63a7a4f commit 388a31d
Show file tree
Hide file tree
Showing 24 changed files with 418 additions and 33 deletions.
5 changes: 5 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ buildscript {
spotless_version = '6.25.0'
jjwt_version = '0.11.5'
swagger_version = '2.0.2'
junit_version = '5.2.0'
}
}

Expand Down Expand Up @@ -64,6 +65,10 @@ dependencies {
runtimeOnly "io.jsonwebtoken:jjwt-jackson:${jjwt_version}"

testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
testImplementation "org.mockito:mockito-junit-jupiter:${junit_version}"
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.junit.jupiter:junit-jupiter-api'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
}

spotless {
Expand Down
15 changes: 11 additions & 4 deletions src/main/java/com/ordertogether/team14_be/auth/JwtUtil.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.ordertogether.team14_be.auth;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
Expand All @@ -8,15 +9,17 @@
import java.util.Date;
import javax.crypto.SecretKey;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

public class JwtUtil {
private final SecretKey key;
private final int expireTime;

@Value("${jwt.expire-time}")
private int expireTime;

public JwtUtil(@Value("${key.jwt.secret-key}") String secretKey) {
public JwtUtil(
@Value("${key.jwt.secret-key}") String secretKey,
@Value("${jwt.expire-time}") int expireTime) {
this.key = Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8));
this.expireTime = expireTime;
}

public String generateToken(Long data) {
Expand All @@ -30,4 +33,8 @@ public String generateToken(Long data) {
.signWith(key, SignatureAlgorithm.HS256)
.compact();
}

public Claims decodeJwt(String token) {
return Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

@ConfigurationProperties(prefix = "kakao")
public record KakaoProperties(
String clientId, String redirectUrl, String tokenUrl, String userApiUrl) {
String clientId, String redirectUrl, String authTokenUrl, String userApiUrl) {
public LinkedMultiValueMap<String, String> createBody(String code) {
LinkedMultiValueMap<String, String> body = new LinkedMultiValueMap<>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,17 @@
import com.ordertogether.team14_be.auth.JwtUtil;
import com.ordertogether.team14_be.auth.application.dto.KakaoUserInfo;
import com.ordertogether.team14_be.auth.presentation.KakaoClient;
import com.ordertogether.team14_be.common.web.response.ApiResponse;
import com.ordertogether.team14_be.member.application.service.MemberService;
import com.ordertogether.team14_be.member.persistence.entity.Member;
import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

@RequiredArgsConstructor
Expand All @@ -13,15 +22,35 @@ public class AuthService {

private final KakaoClient kakaoClient;
private final MemberService memberService;
private static JwtUtil jwtUtil;
private final JwtUtil jwtUtil;

public String kakaoLogin(String authorizationCode) {
@Value(("${FRONT_PAGE_SIGNUP}"))
String redirectPage;

public ResponseEntity<ApiResponse<String>> kakaoLogin(String authorizationCode) {
String kakaoToken = kakaoClient.getAccessToken(authorizationCode); // 인가코드로부터 카카오토큰 발급
KakaoUserInfo kakaoUserInfo = kakaoClient.getUserInfo((kakaoToken));
String userKakaoEmail = kakaoUserInfo.kakaoAccount().email();
memberService.findOrCreateMember(userKakaoEmail);
String serviceToken = jwtUtil.generateToken(memberService.getMemberId(userKakaoEmail));
String userKakaoEmail = kakaoUserInfo.kakaoAccount().email(); // 와 사용자 카카오 이메일이야

Optional<Member> existMember = memberService.findMemberByEmail(userKakaoEmail);
if (existMember.isPresent()) {
String serviceToken =
jwtUtil.generateToken(memberService.getMemberId(userKakaoEmail)); // 서비스 토큰 줘야징
return ResponseEntity.ok((ApiResponse.with(HttpStatus.OK, "로그인 성공", serviceToken)));
} else {
return ResponseEntity.status(HttpStatus.FOUND)
.location(
URI.create(redirectPage + URLEncoder.encode(userKakaoEmail, StandardCharsets.UTF_8)))
.build();
}
}

return serviceToken;
public ResponseEntity<ApiResponse<String>> register(
String email, String deliveryName, String phoneNumber) {
Member member = new Member(email, deliveryName, phoneNumber);
memberService.registerMember(member);
Long memberId = memberService.getMemberId(email);
String serviceToken = jwtUtil.generateToken(memberId);
return ResponseEntity.ok((ApiResponse.with(HttpStatus.OK, "회원가입 및 로그인 성공", serviceToken)));
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
package com.ordertogether.team14_be.auth.presentation;

import com.ordertogether.team14_be.auth.application.service.AuthService;
import com.ordertogether.team14_be.common.web.response.ApiResponse;
import com.ordertogether.team14_be.member.application.dto.MemberInfoRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RequiredArgsConstructor
Expand All @@ -15,7 +21,14 @@ public class AuthController {
private final AuthService authService;

@GetMapping("/login")
public String getToken(@RequestHeader String authorizationCode) {
public ResponseEntity<ApiResponse<String>> getToken(@RequestHeader String authorizationCode) {
return authService.kakaoLogin(authorizationCode);
}

@PostMapping("/signup")
public ResponseEntity<ApiResponse<String>> signUpMember(
@RequestParam String email, @RequestBody MemberInfoRequest memberInfoRequest) {
return authService.register(
email, memberInfoRequest.deliveryName(), memberInfoRequest.phoneNumber());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public String getAccessToken(String authorizationCode) {
var response =
restClient
.post()
.uri(URI.create(kakaoProperties.tokenUrl()))
.uri(URI.create(kakaoProperties.authTokenUrl()))
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.body(body)
.retrieve()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.ordertogether.team14_be.common.web.handler;

import com.ordertogether.team14_be.member.application.exception.NotFoundMember;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(NotFoundMember.class)
@ResponseStatus(HttpStatus.CONFLICT)
public ResponseEntity<String> handleAlreadyExistMemberException(NotFoundMember e) {
return ResponseEntity.status(HttpStatus.CONFLICT).body(e.getMessage());
}
}
21 changes: 21 additions & 0 deletions src/main/java/com/ordertogether/team14_be/config/WebConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.ordertogether.team14_be.config;

import com.ordertogether.team14_be.member.application.LoginMemberArgumentResolver;
import java.util.List;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {
private final LoginMemberArgumentResolver loginMemberArgumentResolver;

public WebConfig(LoginMemberArgumentResolver loginMemberArgumentResolver) {
this.loginMemberArgumentResolver = loginMemberArgumentResolver;
}

@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(loginMemberArgumentResolver);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.ordertogether.team14_be.member.application;

import com.ordertogether.team14_be.auth.JwtUtil;
import com.ordertogether.team14_be.member.application.service.MemberService;
import com.ordertogether.team14_be.member.persistence.MemberRepository;
import com.ordertogether.team14_be.member.presentation.LoginMember;
import lombok.RequiredArgsConstructor;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

@Component
@RequiredArgsConstructor
public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver {

private final MemberService memberService;
private final MemberRepository memberRepository;
private final JwtUtil jwtUtil;

@Override
public boolean supportsParameter(MethodParameter parameter) {
boolean hashLoginUserAnnotation = parameter.hasParameterAnnotation(LoginMember.class);
return hashLoginUserAnnotation;
}

@Override
public Object resolveArgument(
MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory)
throws Exception {

String token = webRequest.getHeader(HttpHeaders.AUTHORIZATION).substring(7);
String memberIdString = jwtUtil.decodeJwt(token).getSubject();
Long memberId = Long.parseLong(memberIdString);

return memberService.findMember(memberId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.ordertogether.team14_be.member.application.dto;

public record MemberInfoRequest(String deliveryName, String phoneNumber) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.ordertogether.team14_be.member.application.dto;

import lombok.Builder;

@Builder
public record MemberInfoResponse(String deliveryName, String phoneNumber, int point) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.ordertogether.team14_be.member.application.dto;

public record MemberSignUpRequest(String token, MemberInfoRequest memberInfoRequest) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.ordertogether.team14_be.member.application.exception;

import java.util.NoSuchElementException;

public class NotFoundMember extends NoSuchElementException {

public NotFoundMember() {
super("회원정보가 존재하지 않습니다");
}
}
Original file line number Diff line number Diff line change
@@ -1,27 +1,72 @@
package com.ordertogether.team14_be.member.application.service;

import com.ordertogether.team14_be.auth.JwtUtil;
import com.ordertogether.team14_be.member.application.dto.MemberInfoResponse;
import com.ordertogether.team14_be.member.application.exception.NotFoundMember;
import com.ordertogether.team14_be.member.persistence.MemberRepository;
import com.ordertogether.team14_be.member.persistence.entity.Member;
import jakarta.transaction.Transactional;
import java.util.NoSuchElementException;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class MemberService {

private final MemberRepository memberRepository;
private final JwtUtil jwtUtil;

@Transactional(readOnly = true)
public Long getMemberId(String email) {
return memberRepository
.findByEmail(email)
.map(Member::getId)
.orElseThrow(() -> new NoSuchElementException("Member with email " + email + " not found"));
}

@Transactional(readOnly = true)
public MemberInfoResponse findMemberInfo(Long memberId) {
Member member = findMember(memberId);

return MemberInfoResponse.builder()
.deliveryName(member.getDeliveryName())
.phoneNumber(member.getPhoneNumber())
.point(member.getPoint())
.build();
}

@Transactional
public void findOrCreateMember(String email) {
Member member =
memberRepository
.findByEmail(email)
.orElseGet(
() -> {
Member newMember = Member.from(email);
return memberRepository.saveAndFlush(newMember);
});
public MemberInfoResponse modifyMember(Long memberId, String deliveryName, String phoneNumber) {
Member member = findMember(memberId);
member.modifyMemberInfo(deliveryName, phoneNumber);
memberRepository.saveAndFlush(member);
return MemberInfoResponse.builder()
.deliveryName(member.getDeliveryName())
.phoneNumber(member.getPhoneNumber())
.point(member.getPoint())
.build();
}

@Transactional(readOnly = true)
public Member findMember(Long memberId) {
Member member = memberRepository.findById(memberId).orElseThrow(() -> new NotFoundMember());
return member;
}

@Transactional
public void deleteMember(Long memberId) {
memberRepository.deleteById(memberId);
}

@Transactional(readOnly = true)
public Optional<Member> findMemberByEmail(String email) {
return memberRepository.findByEmail(email);
}

public void registerMember(Member member) {
memberRepository.saveAndFlush(member);
}

public Long getMemberId(String email) {
Expand Down
Loading

0 comments on commit 388a31d

Please sign in to comment.