Skip to content

Commit

Permalink
Merge pull request #78 from TRIP-Side-Project/feature/#34-social
Browse files Browse the repository at this point in the history
소셜 로그인 기능 구현
  • Loading branch information
don9m1n authored Dec 18, 2023
2 parents 2dd980f + bd2827b commit f6ac140
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 117 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import com.api.trip.common.security.jwt.JwtTokenFilter;
import com.api.trip.common.security.jwt.JwtTokenProvider;
import com.api.trip.common.security.oauth.CustomOAuth2UserService;
import com.api.trip.common.security.oauth.OAuthFailureHandler;
import com.api.trip.common.security.oauth.OAuthSuccessHandler;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
Expand All @@ -18,17 +17,15 @@
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter;


@RequiredArgsConstructor
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(securedEnabled = true)
@RequiredArgsConstructor
public class SecurityConfig {

private final JwtTokenProvider jwtTokenProvider;
private final CustomOAuth2UserService customOAuth2UserService;
private final OAuthSuccessHandler OAuthSuccessHandler;
private final OAuthFailureHandler OAuthFailureHandler;

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
Expand All @@ -43,11 +40,12 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
.addHeaderWriter(new XFrameOptionsHeaderWriter(
XFrameOptionsHeaderWriter.XFrameOptionsMode.SAMEORIGIN))
)
// .oauth2Login(oauth -> oauth
// .userInfoEndpoint(userInfoEndpoint -> userInfoEndpoint.userService(customOAuth2UserService))
// .successHandler(OAuthSuccessHandler)
// .failureHandler(OAuthFailureHandler)
// )
.oauth2Login(oauth -> oauth
.userInfoEndpoint(userInfoEndpoint -> userInfoEndpoint
.userService(customOAuth2UserService)
)
.successHandler(OAuthSuccessHandler)
)
.addFilterBefore(new JwtTokenFilter(jwtTokenProvider), UsernamePasswordAuthenticationFilter.class)
.build();
}
Expand All @@ -56,4 +54,5 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}

}
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
package com.api.trip.common.security.oauth;

import com.api.trip.domain.member.model.Member;
import com.api.trip.domain.member.repository.MemberRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
Expand All @@ -15,37 +12,23 @@

import java.util.Collections;
import java.util.Map;
import java.util.Optional;

@Slf4j
@Service
@RequiredArgsConstructor
public class CustomOAuth2UserService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {

private final MemberRepository memberRepository;

@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {

OAuth2UserService<OAuth2UserRequest, OAuth2User> oAuth2UserService = new DefaultOAuth2UserService();
OAuth2User oAuth2User = oAuth2UserService.loadUser(userRequest);

String oauthType = userRequest.getClientRegistration().getRegistrationId();
String registrationId = userRequest.getClientRegistration().getRegistrationId();
String userNameAttributeName = userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName();

OAuth2Attribute oAuth2Attribute = OAuth2Attribute.of(oauthType, userNameAttributeName, oAuth2User.getAttributes());
Map<String, Object> customAttributeMap = oAuth2Attribute.convertToMap();

String email = (String) customAttributeMap.get("email");
Optional<Member> findMember = memberRepository.findByEmail(email);

boolean exist = findMember.isPresent();
String authority = exist ? findMember.get().getRole().getValue() : "ROLE_MEMBER";

log.debug("회원 존재 여부: {}", exist);
log.debug("회원 권한 여부: {}", authority);
OAuth2Attribute oAuth2Attribute = OAuth2Attribute.of(registrationId, userNameAttributeName, oAuth2User.getAttributes());
Map<String, Object> memberAttribute = oAuth2Attribute.convertToMap();

customAttributeMap.put("exist", exist);
return new DefaultOAuth2User(Collections.singleton(new SimpleGrantedAuthority(authority)), customAttributeMap, userNameAttributeName);
return new DefaultOAuth2User(Collections.singleton(new SimpleGrantedAuthority("ROLE_MEMBER")),
memberAttribute, "email");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,66 +15,66 @@
public class OAuth2Attribute {

private Map<String, Object> attributes; // 소셜 로그인 사용자의 속성 정보를 담는 Map
private String userNameAttributeName; // 사용자 속성의 키 값
private String attributeKey; // 사용자 속성의 키 값
private String email; // 이메일
private String nickname; // 이름
private String profileImg; // 프로필 사진
private String oauthType; // 제공자 정보
private String name; // 이름
private String picture; // 프로필 사진

static OAuth2Attribute of(String oauthType, String userNameAttributeName, Map<String, Object> attributes) {
return switch (oauthType) {
case "kakao" -> kakao(oauthType, userNameAttributeName, attributes);
case "google" -> google(oauthType, userNameAttributeName, attributes);
case "naver" -> naver(oauthType, userNameAttributeName, attributes);
static OAuth2Attribute of(String provider, String attributeKey, Map<String, Object> attributes) {
// 각 플랫폼 별로 제공해주는 데이터가 조금씩 다르기 때문에 분기 처리함.
return switch (provider) {
case "google" -> google(attributeKey, attributes);
case "kakao" -> kakao(attributeKey, attributes);
case "naver" -> naver(attributeKey, attributes);
default -> throw new RuntimeException();
};
}

private static OAuth2Attribute google(String attributeKey, Map<String, Object> attributes) {
log.debug("google: {}", attributes);
return OAuth2Attribute.builder()
.email((String) attributes.get("email"))
.name((String) attributes.get("name"))
.picture((String)attributes.get("picture"))
.attributes(attributes)
.attributeKey(attributeKey)
.build();
}

private static OAuth2Attribute kakao(String oauthType, String userNameAttributeName, Map<String, Object> attributes) {
private static OAuth2Attribute kakao(String attributeKey, Map<String, Object> attributes) {
Map<String, Object> kakaoAccount = (Map<String, Object>) attributes.get("kakao_account");
Map<String, Object> kakaoProfile = (Map<String, Object>) kakaoAccount.get("profile");

return OAuth2Attribute.builder()
.userNameAttributeName(userNameAttributeName)
.email("KAKAO_" + (String) kakaoAccount.get("email"))
.name((String) kakaoProfile.get("nickname"))
.picture((String) kakaoProfile.get("profile_image_url"))
.attributes(kakaoAccount)
.email((String) kakaoAccount.get("email"))
.nickname((String) kakaoProfile.get("nickname"))
.profileImg((String) kakaoProfile.get("profile_image_url"))
.oauthType(oauthType)
.build();
}

private static OAuth2Attribute google(String oauthType, String userNameAttributeName, Map<String, Object> attributes) {
return OAuth2Attribute.builder()
.userNameAttributeName(userNameAttributeName)
.attributes(attributes)
.email((String) attributes.get("email"))
.oauthType(oauthType)
.attributeKey(attributeKey)
.build();
}

private static OAuth2Attribute naver(String oauthType, String userNameAttributeName, Map<String, Object> attributes) {
private static OAuth2Attribute naver(String attributeKey, Map<String, Object> attributes) {
Map<String, Object> response = (Map<String, Object>) attributes.get("response");

return OAuth2Attribute.builder()
.email((String) response.get("email"))
.name((String) response.get("nickname"))
.picture((String) response.get("profile_image"))
.attributes(response)
.oauthType(oauthType)
.userNameAttributeName(userNameAttributeName)
.attributeKey(attributeKey)
.build();
}


// OAuth2Attribute -> Map<String, Object>
public Map<String, Object> convertToMap() {
Map<String, Object> map = new HashMap<>();

map.put("id", userNameAttributeName);
map.put("id", attributeKey);
map.put("key", attributeKey);
map.put("email", email);
map.put("nickname", nickname);
map.put("profileImg", profileImg);
map.put("oauthType", oauthType);
map.put("name", name);
map.put("picture", picture);

return map;
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,63 +1,82 @@
package com.api.trip.common.security.oauth;

import com.api.trip.common.security.jwt.JwtToken;
import com.api.trip.common.security.jwt.JwtTokenProvider;
import jakarta.servlet.ServletException;
import com.api.trip.domain.member.controller.dto.LoginResponse;
import com.api.trip.domain.member.model.Member;
import com.api.trip.domain.member.model.MemberRole;
import com.api.trip.domain.member.repository.MemberRepository;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseCookie;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import org.springframework.web.util.UriComponentsBuilder;

import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.Optional;

@Slf4j
@Component
@RequiredArgsConstructor
public class OAuthSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

private final MemberRepository memberRepository;
private final JwtTokenProvider jwtTokenProvider;
private final ObjectMapper objectMapper;

@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {

OAuth2User oAuth2User = (OAuth2User) authentication.getPrincipal();
log.debug("OAuth2User: {}", oAuth2User);

String email = oAuth2User.getAttribute("email");
Optional<Member> findMember = memberRepository.findByEmail(email);

String role = oAuth2User.getAuthorities().stream()
.findFirst()
.orElseThrow(IllegalAccessError::new)
.getAuthority();

/** 미구현
if (isExist) {
JwtToken jwtToken = jwtTokenProvider.createJwtToken(email, role);
log.debug("JWT TOKEN: {} {}", jwtToken.getAccessToken(), jwtToken.getRefreshToken());
String targetUrl = UriComponentsBuilder.fromUriString("/")
.queryParam("accessToken", jwtToken.getAccessToken())
.queryParam("refreshToken", jwtToken.getRefreshToken())
.build()
.encode(StandardCharsets.UTF_8)
.toUriString();
getRedirectStrategy().sendRedirect(request, response, targetUrl);
} else {
// 회원이 존재하는 않는 경우 회원 가입 후 토큰 발급
String targetUrl = UriComponentsBuilder.fromUriString("/")
.queryParam("email", email)
.queryParam("name", nickname)
.build()
.encode(StandardCharsets.UTF_8)
.toUriString();
getRedirectStrategy().sendRedirect(request, response, targetUrl);
// 회원이 아닌 경우에 회원 가입 진행

Long memberId = 0L;
String role = "";

if (findMember.isEmpty()) {
String name = oAuth2User.getAttribute("name");
String picture = oAuth2User.getAttribute("picture");

Member member = Member.of(email, "", name, picture);
memberRepository.save(member);

memberId = member.getId();
role = member.getRole().getValue();
}
*/

// OAuth2User 객체에서 권한 가져옴
JwtToken jwtToken = jwtTokenProvider.createJwtToken(email, role);

// 쿠키 세팅
response.addHeader(HttpHeaders.SET_COOKIE, createCookie("tokenType", "Bearer"));
response.addHeader(HttpHeaders.SET_COOKIE, createCookie("accessToken", jwtToken.getAccessToken()));
response.addHeader(HttpHeaders.SET_COOKIE, createCookie("refreshToken", jwtToken.getRefreshToken()));
response.addHeader(HttpHeaders.SET_COOKIE, createCookie("memberId", String.valueOf(memberId)));
response.sendRedirect("/home");
}

private static String createCookie(String name, String value) {
return ResponseCookie.from(name, value)
.path("/")
.httpOnly(true)
.sameSite("None")
.secure(true)
.build()
.toString();
}

}

0 comments on commit f6ac140

Please sign in to comment.