Skip to content

Commit

Permalink
Merge pull request #32 from 9oormthon-univ/test/#30
Browse files Browse the repository at this point in the history
로그인 확인 및 JWT 생성 테스트 완료
  • Loading branch information
BinarySstar authored Jan 20, 2025
2 parents 6c64a27 + e7c2ad1 commit 8509422
Show file tree
Hide file tree
Showing 18 changed files with 308 additions and 106 deletions.
6 changes: 3 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,6 @@ dependencyManagement {
}
}

tasks.named('test') {
enabled = false
}
test {
useJUnitPlatform()
}
2 changes: 0 additions & 2 deletions src/main/java/univ/goormthon/kongju/KongjuApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@EnableJpaAuditing
@SpringBootApplication
public class KongjuApplication {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,17 @@ public class MemberService {
private final MemberRepository memberRepository;

@Transactional
public Member findOrRegisterMember(String email, Map<String, Object> attributes) {
return memberRepository.findByEmail(email)
.orElseGet(() -> registerMember(attributes));
public Member findOrRegisterMember(Map<String, Object> userInfo) {
return memberRepository.findByEmail((String) userInfo.get("email"))
.orElseGet(() -> registerMember(userInfo));
}

private Member registerMember(Map<String, Object> attributes) {
Map<String, Object> kakaoAccount = (Map<String, Object>) attributes.get("kakao_account");
Map<String, Object> properties = (Map<String, Object>) attributes.get("properties");

private Member registerMember(Map<String, Object> userInfo) {
Member member = Member.builder()
.kakaoId(Long.valueOf(attributes.get("id").toString()))
.email((String) kakaoAccount.get("email"))
.nickname((String) properties.get("nickname"))
.profileImage((String) properties.get("profile_image"))
.kakaoId(Long.valueOf(userInfo.get("id").toString()))
.email((String) userInfo.get("email"))
.nickname((String) userInfo.get("nickname"))
.profileImage((String) userInfo.get("profile_image"))
.build();

return memberRepository.save(member);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package univ.goormthon.kongju.global.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@EnableJpaAuditing
@Configuration
public class JpaAuditingConfig {
}
16 changes: 16 additions & 0 deletions src/main/java/univ/goormthon/kongju/global/config/KeyConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package univ.goormthon.kongju.global.config;

import io.jsonwebtoken.Jwts;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.crypto.SecretKey;

@Configuration
public class KeyConfig {

@Bean
public SecretKey secretKey() {
return Jwts.SIG.HS256.key().build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti

// 인증 경로 설정
http.authorizeHttpRequests(requests -> requests
.requestMatchers("/h2-console/**","/api/v1/jwt").permitAll()
.requestMatchers("/h2-console/**","/api/v1/auth/**").permitAll()
.anyRequest().authenticated())
.headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable));

Expand Down
24 changes: 0 additions & 24 deletions src/main/java/univ/goormthon/kongju/global/config/WebConfig.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package univ.goormthon.kongju.global.exception;

import univ.goormthon.kongju.global.exception.dto.ErrorCode;

public class UnsupportedProviderException extends KongjuException {
public UnsupportedProviderException(ErrorCode errorCode) {
super(errorCode);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public enum ErrorCode {
INVALID_INPUT_VALUE(HttpStatus.BAD_REQUEST, "잘못된 입력 값입니다."),
FAILED_TO_RESERVE(HttpStatus.BAD_REQUEST, "예약에 실패했습니다."),
FAILED_TO_UPLOAD(HttpStatus.BAD_REQUEST, "이미지 업로드에 실패했습니다."),
UNSUPPORTED_PROVIDER(HttpStatus.BAD_REQUEST, "지원하지 않는 소셜 서비스 로그인입니다."),

UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "서비스 회원이 아닙니다.");

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package univ.goormthon.kongju.global.jwt.dto.request;

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

import java.util.List;

@Builder
public record TokenRequest(
@JsonProperty("accessToken") String accessToken,
@JsonProperty("refreshToken") String refreshToken,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,37 +1,21 @@
package univ.goormthon.kongju.global.jwt.service;

import io.jsonwebtoken.Jwts;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpHeaders;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import univ.goormthon.kongju.domain.member.entity.Member;
import univ.goormthon.kongju.domain.member.service.MemberService;
import univ.goormthon.kongju.global.jwt.dto.request.TokenRequest;
import univ.goormthon.kongju.global.jwt.dto.response.TokenResponse;

import javax.crypto.SecretKey;
import java.util.Date;
import java.util.Map;

@Service
@RequiredArgsConstructor
public class JwtProvider {
private final SecretKey secretKey = Jwts.SIG.HS256.key().build();
private final MemberService memberService;
private final WebClient webClient;

public JwtProvider(MemberService memberService, WebClient.Builder webClientBuilder) {
this.memberService = memberService;
this.webClient = webClientBuilder.baseUrl("https://kapi.kakao.com").build();
}

@Bean
public SecretKey secretKey() {
return this.secretKey;
}
private final SecretKey secretKey;

public TokenResponse issueJwtToken(TokenRequest tokenRequest) {
String accessToken = generateAccessToken(tokenRequest.accessToken());
public TokenResponse issueJwtToken(String email) {
String accessToken = generateAccessToken(email);
String refreshToken = generateRefreshToken();

return TokenResponse.builder()
Expand All @@ -41,9 +25,7 @@ public TokenResponse issueJwtToken(TokenRequest tokenRequest) {
}


private String generateAccessToken(String kakaoAccessToken) {
String email = extractEmailFromKakaoAccessToken(kakaoAccessToken);

private String generateAccessToken(String email) {
return Jwts.builder()
.issuer("shared-parking-service-Kongju")
.issuedAt(new Date())
Expand All @@ -54,23 +36,6 @@ private String generateAccessToken(String kakaoAccessToken) {

}

private String extractEmailFromKakaoAccessToken(String kakaoAccessToken) {
var response = webClient.get()
.uri("/v2/user/me")
.header(HttpHeaders.AUTHORIZATION, "Bearer " + kakaoAccessToken)
.retrieve()
.bodyToMono(Map.class)
.block();

// 이메일 추출
Map<String, Object> kakaoAccount = (Map<String, Object>) response.get("kakao_account");
String email = (String) kakaoAccount.get("email");

Member member = memberService.findOrRegisterMember(email, response);

return email;
}

private String generateRefreshToken() {
return Jwts.builder()
.issuer("shared-parking-service-Kongju")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package univ.goormthon.kongju.global.oauth2.controller;

import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import univ.goormthon.kongju.domain.member.entity.Member;
import univ.goormthon.kongju.domain.member.service.MemberService;
import univ.goormthon.kongju.global.jwt.dto.request.TokenRequest;
import univ.goormthon.kongju.global.jwt.service.JwtProvider;
import univ.goormthon.kongju.global.oauth2.provider.OAuth2UserInfoProvider;
import univ.goormthon.kongju.global.oauth2.provider.OAuth2UserInfoProviderFactory;

import java.util.Map;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/auth")
public class OAuth2Controller {

private final JwtProvider jwtProvider;
private final OAuth2UserInfoProviderFactory oAuth2UserInfoProviderFactory;
private final MemberService memberService;

@PostMapping("/login")
public ResponseEntity<?> login(@RequestParam String provider,
@RequestBody TokenRequest tokenRequest) {
OAuth2UserInfoProvider providerInstance = oAuth2UserInfoProviderFactory.getProvider(provider);
Map<String, Object> userInfo = providerInstance.getUserInfo(tokenRequest.accessToken());

Member member = memberService.findOrRegisterMember(userInfo);
return ResponseEntity.ok(jwtProvider.issueJwtToken(member.getEmail()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package univ.goormthon.kongju.global.oauth2.provider;

import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;

import java.util.Map;

@Component
public class KakaoUserInfoProvider implements OAuth2UserInfoProvider {

private final WebClient webClient;

public KakaoUserInfoProvider(WebClient.Builder webClientBuilder) {
this.webClient = webClientBuilder.baseUrl("https://kapi.kakao.com").build();
}

@Override
public Map<String, Object> getUserInfo(String accessToken) {
var response = webClient.get()
.uri("/v2/user/me")
.header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken)
.retrieve()
.bodyToMono(Map.class)
.block();

// 카카오 사용자 정보 변환
Map<String, Object> kakaoAccount = (Map<String, Object>) response.get("kakao_account");
Map<String, Object> properties = (Map<String, Object>) response.get("properties");

return Map.of(
"id", response.get("id"),
"email", kakaoAccount.get("email"),
"nickname", properties.get("nickname"),
"profile_image", properties.get("profile_image")
);
}

@Override
public String getProviderName() {
return "kakao";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package univ.goormthon.kongju.global.oauth2.provider;

import java.util.Map;

public interface OAuth2UserInfoProvider {

Map<String, Object> getUserInfo(String accessToken);
String getProviderName(); // kakao, google, naver, ...
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package univ.goormthon.kongju.global.oauth2.provider;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import univ.goormthon.kongju.global.exception.UnsupportedProviderException;
import univ.goormthon.kongju.global.exception.dto.ErrorCode;

import java.util.List;

@Component
@RequiredArgsConstructor
public class OAuth2UserInfoProviderFactory {

private final List<OAuth2UserInfoProvider> providers;

public OAuth2UserInfoProvider getProvider(String providerName) {
return providers.stream()
.filter(provider -> provider.getProviderName().equalsIgnoreCase(providerName))
.findFirst()
.orElseThrow(() -> new UnsupportedProviderException(ErrorCode.UNSUPPORTED_PROVIDER));
}
}

30 changes: 30 additions & 0 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,33 @@ server:
secure: true # HTTPS 사용 시 true
http-only: true
same-site: none # 다른 도메인 간 쿠키 전송 허용

cloud:
aws:
s3:
bucket: mock-bucket-name
credentials:
access-key: mock-access-key
secret-key: mock-secret-key
region:
static: "ap-northeast-2"
auto: false
stack:
auto: false

datasource:
url: jdbc:h2:mem:testdb;MODE=MYSQL
driver-class-name: org.h2.Driver
username: sa
password:
h2:
console:
enabled: true
path: /h2-console
jpa:
hibernate:
ddl-auto: update
properties:
hibernate:
format_sql: true
show_sql: true
Loading

0 comments on commit 8509422

Please sign in to comment.