Feat: 로그인 기능 구현 - S11P21S002-15

This commit is contained in:
김용수 2024-08-27 17:52:28 +09:00
parent c0661f03d5
commit a603ddd2b2
9 changed files with 216 additions and 0 deletions

View File

@ -0,0 +1,22 @@
package com.worlabel.domain.auth.attribute;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import java.util.Map;
@Getter
@ToString
@RequiredArgsConstructor
public abstract class OAuth2Attribute {
private final Map<String,Object> attributes;
public abstract String getId();
public abstract String getName();
public abstract String getEmail();
public abstract String getProfileImage();
}

View File

@ -0,0 +1,20 @@
package com.worlabel.domain.auth.attribute;
import com.worlabel.domain.auth.attribute.impl.GoogleAttribute;
import com.worlabel.domain.auth.entity.ProviderType;
import java.util.Map;
public class OAuth2AttributeFactory {
public static OAuth2Attribute parseAttribute(ProviderType provider, Map<String, Object> attributes){
OAuth2Attribute oAuth2Attribute = null;
switch (provider) {
case GOOGLE :
oAuth2Attribute = new GoogleAttribute(attributes);
break;
default:
throw new RuntimeException("지원하지 않는 소셜 로그인입니다.");
};
return oAuth2Attribute;
}
}

View File

@ -0,0 +1,34 @@
package com.worlabel.domain.auth.attribute.impl;
import ch.qos.logback.core.util.StringUtil;
import com.worlabel.domain.auth.attribute.OAuth2Attribute;
import org.springframework.util.StringUtils;
import java.util.Map;
public class GoogleAttribute extends OAuth2Attribute {
public GoogleAttribute(Map<String,Object> attributes) {
super(attributes);
}
@Override
public String getId() {
return super.getAttributes().get("sub").toString();
}
@Override
public String getName() {
return super.getAttributes().get("name").toString();
}
@Override
public String getEmail() {
return super.getAttributes().get("email").toString();
}
@Override
public String getProfileImage() {
return super.getAttributes().get("picture").toString();
}
}

View File

@ -0,0 +1,19 @@
package com.worlabel.domain.auth.controller;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/auth")
public class AuthController {
@GetMapping("/login")
public String login() {
return "성공";
}
}

View File

@ -0,0 +1,52 @@
package com.worlabel.domain.auth.service;
import com.worlabel.domain.auth.attribute.OAuth2Attribute;
import com.worlabel.domain.auth.attribute.OAuth2AttributeFactory;
import com.worlabel.domain.auth.entity.ProviderType;
import com.worlabel.domain.member.entity.Member;
import com.worlabel.domain.member.repository.MemberRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;
import org.springframework.stereotype.Service;
import java.util.Collections;
/**
* OAuth2 사용자 서비스 클래스
* OAuth2 인증을 통해 사용자 정보를 로드하고 처리하는 역할
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
private final MemberRepository memberRepository;
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
OAuth2User user = super.loadUser(userRequest);
log.debug("oAuth2User: {}", user.getAttributes());
ProviderType provider = ProviderType.valueOf(userRequest.getClientRegistration().getRegistrationId().toUpperCase());
OAuth2Attribute attribute = OAuth2AttributeFactory.parseAttribute(provider, user.getAttributes());
log.debug("provider: {}, user: {}", provider, user);
Member findMember = memberRepository.findByProviderMemberId(attribute.getId())
.orElseGet(() -> {
Member member = Member.of(attribute.getId(), attribute.getEmail(), attribute.getName(), attribute.getProfileImage());
memberRepository.save(member);
return member;
});
return new DefaultOAuth2User(Collections.singleton(new OAuth2UserAuthority(user.getAttributes())),
user.getAttributes(),
"sub"
);
}
}

View File

@ -38,6 +38,18 @@ public class Member extends BaseEntity {
@Enumerated(EnumType.STRING)
private ProviderType provider = ProviderType.GOOGLE;
/**
* 사용자 이메일
*/
@Column(name = "email",nullable = false, length = 40)
private String email;
/**
* 사용자 닉네임
*/
@Column(name = "nickname",nullable = false, length = 20)
private String nickname;
/**
* 사용자 역할
*/
@ -56,4 +68,13 @@ public class Member extends BaseEntity {
*/
@OneToMany(mappedBy = "member", fetch = FetchType.LAZY,cascade = CascadeType.ALL, orphanRemoval = true)
private List<Comment> commentList = new ArrayList<>();
public static Member of(String providerMemberId, String email, String nickname, String profileImage) {
Member member = new Member();
member.providerMemberId = providerMemberId;
member.email = email;
member.nickname = nickname;
member.profileImage = profileImage;
return member;
}
}

View File

@ -0,0 +1,10 @@
package com.worlabel.domain.member.repository;
import com.worlabel.domain.member.entity.Member;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface MemberRepository extends JpaRepository<Member, Integer> {
public Optional<Member> findByProviderMemberId(String providerMemberId);
}

View File

@ -0,0 +1,38 @@
package com.worlabel.global.config;
import lombok.Builder;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// HTTP 요청에 대한 인증 권한 설정
.authorizeHttpRequests(auth -> auth
// 경로는 모든 사용자가 접근 있도록 허용
.requestMatchers("/**","/favicon.ico").permitAll()
// 외의 모든 요청은 인증해야함
.anyRequest().authenticated()
)
// OAuth2 로그인 설정
.oauth2Login(oauth2 -> oauth2
.userInfoEndpoint(userInfo ->
// 사용자 정보를 처리하는 서비스로 DefaultOAuth2UserService
userInfo.userService(new DefaultOAuth2UserService()))
)
// CSRF 보호 설정
.csrf(csrf -> csrf // CSRF 보호 활성화 커스터마이징
// CSRF토큰을 쿠키에 저장하도록 설정, HTTP-only 속성을 비활성화하여 자바스크립트에서 접근 가능하게
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
);
// 설정을 바탕으로 SecurityFilterChain 빌드
return http.build();
}
}

View File