Skip to content

Commit

Permalink
Merge pull request #29 from 9oormthon-univ/refactor/#27
Browse files Browse the repository at this point in the history
Feat: 로그인 방식 개선
  • Loading branch information
BinarySstar authored Jan 13, 2025
2 parents 11aa25a + 19d5300 commit fb08784
Show file tree
Hide file tree
Showing 13 changed files with 213 additions and 217 deletions.
8 changes: 8 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'
testImplementation 'org.springframework.security:spring-security-test'

// JWT
implementation 'io.jsonwebtoken:jjwt-api:0.12.6'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.6'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.6'

compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,25 @@
package univ.goormthon.kongju.domain.member.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpSession;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import univ.goormthon.kongju.domain.member.dto.response.ProfileInfo;
import univ.goormthon.kongju.domain.member.entity.Member;
import univ.goormthon.kongju.domain.member.service.MemberService;
import univ.goormthon.kongju.global.exception.dto.ErrorResponse;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/kongju/member")
@RequestMapping("/api/v1/member")
@Tag(name = "Member", description = "회원 정보 API")
public class MemberController {

Expand All @@ -30,12 +28,13 @@ public class MemberController {
@Operation(summary = "프로필 조회", description = "로그인한 회원의 프로필을 조회합니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "프로필 조회 성공", content = @Content(schema = @Schema(implementation = ProfileInfo.class))),
@ApiResponse(responseCode = "404", description = "회원 세션이 존재하지 않음", content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
@ApiResponse(responseCode = "404", description = "회원이 존재하지 않음", content = @Content(schema = @Schema(implementation = ErrorResponse.class)))

})
@GetMapping("/profile")
public ResponseEntity<?> getProfile(@RequestParam String memberId) {
ProfileInfo profileInfo = memberService.getProfile(memberId);
public ResponseEntity<?> getProfile(@AuthenticationPrincipal Jwt jwt) {
String email = jwt.getClaimAsString("sub");
ProfileInfo profileInfo = memberService.getProfile(email);
return ResponseEntity.ok(profileInfo);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@
@Repository
public interface MemberRepository extends JpaRepository<Member, Long> {
Optional<Member> findByEmail(String email);

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,58 +6,40 @@
import univ.goormthon.kongju.domain.member.dto.response.ProfileInfo;
import univ.goormthon.kongju.domain.member.entity.Member;
import univ.goormthon.kongju.domain.member.repository.MemberRepository;
import univ.goormthon.kongju.global.auth.kakao.dto.KakaoProfileInfoResponse;
import univ.goormthon.kongju.global.exception.NotFoundException;
import univ.goormthon.kongju.global.exception.dto.ErrorCode;

import java.util.Map;

@Service
@RequiredArgsConstructor
public class MemberService {

private final MemberRepository memberRepository;

@Transactional
public Member loadProfile(KakaoProfileInfoResponse profileInfo) {
Member member = findOrRegisterMember(profileInfo.kakaoAccount().email(), profileInfo);
return updateProfileIfChanged(member, profileInfo);
}

public Member findOrRegisterMember(String email, KakaoProfileInfoResponse response) {
public Member findOrRegisterMember(String email, Map<String, Object> attributes) {
return memberRepository.findByEmail(email)
.orElseGet(() -> registerMember(response));
.orElseGet(() -> registerMember(attributes));
}

private Member registerMember(KakaoProfileInfoResponse response) {
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");

Member member = Member.builder()
.kakaoId(response.id())
.email(response.kakaoAccount().email())
.nickname(response.kakaoAccount().profile().nickname())
.profileImage(response.kakaoAccount().profile().profileImageUrl())
.kakaoId(Long.valueOf(attributes.get("id").toString()))
.email((String) kakaoAccount.get("email"))
.nickname((String) properties.get("nickname"))
.profileImage((String) properties.get("profile_image"))
.build();
return memberRepository.save(member);
}

private Member updateProfileIfChanged(Member member, KakaoProfileInfoResponse profileInfo) {
boolean isChanged = false;
String nickname = profileInfo.kakaoAccount().profile().nickname();
String profileImage = profileInfo.kakaoAccount().profile().profileImageUrl();

if(!member.getNickname().equals(nickname)) {
member.setNickname(nickname);
isChanged = true;
}

if(!member.getProfileImage().equals(profileImage)) {
member.setProfileImage(profileImage);
isChanged = true;
}

return isChanged ? memberRepository.save(member) : member;
return memberRepository.save(member);
}

@Transactional
public ProfileInfo getProfile(String memberId) {
Member currentMember = memberRepository.findById(Long.parseLong(memberId))
public ProfileInfo getProfile(String email) {
Member currentMember = memberRepository.findByEmail(email)
.orElseThrow(() -> new NotFoundException(ErrorCode.MEMBER_NOT_FOUND));

return ProfileInfo.builder()
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

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

import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.web.SecurityFilterChain;

import javax.crypto.SecretKey;

@Configuration
@RequiredArgsConstructor
public class SecurityConfig {

private final SecretKey secretKey;

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

// 브라우저 환경이 아니므로 CSRF, BASIC 인증, FormLogin 비활성화
http.csrf(AbstractHttpConfigurer::disable)
.httpBasic(AbstractHttpConfigurer::disable)
.formLogin(AbstractHttpConfigurer::disable);

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

// OAuth2 Resource Server 설정
http.oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()));

return http.build();
}

@Bean
public JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withSecretKey(this.secretKey).build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package univ.goormthon.kongju.global.jwt.controller;

import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import univ.goormthon.kongju.global.jwt.dto.request.TokenRequest;
import univ.goormthon.kongju.global.jwt.dto.response.TokenResponse;
import univ.goormthon.kongju.global.jwt.service.JwtProvider;

@RestController
@RequestMapping("/api/v1/jwt")
@RequiredArgsConstructor
public class JwtController {

private final JwtProvider jwtProvider;

@PostMapping
public ResponseEntity<TokenResponse> issueJwtToken(@RequestBody TokenRequest tokenRequest) {
return ResponseEntity.ok(jwtProvider.issueJwtToken(tokenRequest));
}
}
Loading

0 comments on commit fb08784

Please sign in to comment.