Skip to content

Commit

Permalink
fix: kakao accessToken으로 로그인, Server jwt 발급 (#51)
Browse files Browse the repository at this point in the history
  • Loading branch information
toychip committed Feb 12, 2024
1 parent c07695c commit 04b1b3b
Show file tree
Hide file tree
Showing 19 changed files with 141 additions and 319 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,24 @@ public AuditorAware<String> auditorProvider() {
return () -> {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
return Optional.of("Anonymous");
return Optional.of("AnonymousNULL");
}

System.out.println("authentication = " + authentication);
System.out.println("----------- 클래스 타입" + authentication.getClass());
System.out.println("----------- 클래스 타입" + authentication.getPrincipal().getClass());


Object principal = authentication.getPrincipal();
if (principal instanceof Member) {
Member member = (Member) principal;
String email = member.getEmail();
return Optional.ofNullable(email);
} else {
// principal이 Member 타입이 아닌 경우의 처리
return Optional.of("AnonymousNOT_TYPE");
}

Member member = (Member) authentication.getPrincipal();
String originName = member.getOriginName();
return Optional.of(originName);
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,6 @@ public class MemberService {
private final MemberRepository memberRepository;
private final MemberOAuthRepository memberOAuthRepository;

public Member findByIdOfToken(final Long memberId) {
return memberRepository.findById(memberId)
.orElseThrow(() -> new ApiException(_USER_NOT_FOUND_BY_TOKEN));
}

public Member findByIdWithProfile(final Long memberId) {
return memberOAuthRepository.findByIdWithProfile(memberId);
}
Expand All @@ -41,4 +36,9 @@ public Optional<Member> findByEmailOptional(final String email) {
return memberRepository.findByEmail(email);

}

@Transactional
public void register(final Member member) {
memberRepository.save(member);
}
}
42 changes: 9 additions & 33 deletions src/main/java/com/api/ttoklip/global/config/SecurityConfig.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package com.api.ttoklip.global.config;

import com.api.ttoklip.global.security.auth.handler.TokenErrorHandler;
import com.api.ttoklip.global.security.jwt.JwtAuthenticationFilter;
import com.api.ttoklip.global.security.oauth.service.CustomOAuth2UserService;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
Expand All @@ -22,56 +21,34 @@
@RequiredArgsConstructor
public class SecurityConfig {

private final CustomOAuth2UserService customOAuth2UserService;
private final JwtAuthenticationFilter jwtAuthenticationFilter;
private final CustomOAuthSuccessHandler customOAuthSuccessHandler;
private final TokenErrorHandler tokenErrorHandler;

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.httpBasic(HttpBasicConfigurer::disable)
.csrf(CsrfConfigurer::disable)
.cors(corsCustomizer -> corsCustomizer.configurationSource(request -> {
CorsConfiguration cors = new CorsConfiguration();
cors.setAllowedOrigins(List.of("*", "http://localhost:3000", "http://localhost:8080"));
cors.setAllowedMethods(List.of("GET", "POST", "PATCH", "DELETE"));
// cookie 비활성화
cors.setAllowCredentials(false);
// Authorization Header 노출
cors.addExposedHeader("Authorization");
return cors;
}))
.cors()
.and()
.sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(authorize ->
authorize
.requestMatchers(
"/favicon.ico"
,"/health"
,"/swagger-ui/**"
,"/oauth/**"
,"/login/**"
, "/health"
, "/swagger-ui/**"
, "/oauth/**"
, "/login/**"
, "/**"
).permitAll()
.anyRequest().permitAll());

// ToDo oauth 설정, filter, Handler 등

http .oauth2Login()
.authorizationEndpoint().baseUri("/oauth/authorize")
.and()
.redirectionEndpoint().baseUri("/oauth/callback")
.and()
.userInfoEndpoint() // oauth2 로그인 성공후에 사용자 정보를 바로 가져온다.
.userService(customOAuth2UserService)
.and()
.successHandler(customOAuthSuccessHandler);

http.exceptionHandling(e -> e.accessDeniedHandler(tokenErrorHandler));
http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);

return http.build();
}

// CORS
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
Expand All @@ -84,5 +61,4 @@ public CorsFilter corsFilter() {
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ public enum ErrorType {
// ------------------------------------------ Auth ------------------------------------------
OAUTH_INVALID_PROVIDER(INTERNAL_SERVER_ERROR, "OAUTH_5000", "올바르지 않은 Provider입니다."),
OAUTH_NOTFOUND_NAME(INTERNAL_SERVER_ERROR, "OAUTH_5001", "Oauth 제공자로부터 name을 받을 수 없습니다."),
OAUTH_NOTFOUND_EMAIL(INTERNAL_SERVER_ERROR, "OAUTH_5002", "Oauth 제공자로부터 email을 받을 수 없습니다."),


// ------------------------------------------ USER ------------------------------------------
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

import com.api.ttoklip.global.security.auth.dto.LoginRequest;
import com.api.ttoklip.global.security.auth.dto.LoginResponse;
import com.api.ttoklip.global.security.auth.service.AuthService;
import com.api.ttoklip.global.success.SuccessResponse;
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;
Expand All @@ -14,10 +14,11 @@
@RequiredArgsConstructor
@RequestMapping("/api/v1/auth")
public class AuthController {
private final AuthService authService;

@PostMapping
public SuccessResponse<LoginResponse> loginWithKakao(@RequestBody LoginRequest request) {

return null;
public SuccessResponse<LoginResponse> login(final @RequestBody LoginRequest request) {
LoginResponse loginResponse = authService.authenticate(request);
return new SuccessResponse<>(loginResponse);
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
package com.api.ttoklip.global.security.auth.dto;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class LoginResponse {
private String jwtToken;
private boolean ifFirstLogin;
public record LoginResponse(String jwtToken, boolean ifFirstLogin) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.api.ttoklip.global.security.auth.handler;

import com.api.ttoklip.global.exception.ApiException;
import com.api.ttoklip.global.exception.ErrorType;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;

@Component
public class AuthFailureHandler implements AuthenticationFailureHandler {

@Override
public void onAuthenticationFailure(final HttpServletRequest request, final HttpServletResponse response,
final AuthenticationException authException) throws IOException {
ApiException apiException = (ApiException) authException.getCause();
ErrorType errorType = apiException.getErrorType();
setResponse(response, errorType);
}

private void setResponse(HttpServletResponse response, ErrorType errorType) throws IOException {

response.setContentType("application/json;charset=UTF-8");

int status = Integer.parseInt(String.valueOf(errorType.getStatus()).substring(0,3));
response.setStatus(status);

response.getWriter().println(
"{\"status\" : \"" + status + "\"," +
"\"errorCode\" : \"" + errorType.getErrorCode() + "\"," +
" \"message\" : \"" + errorType.getMessage() +
"\"}");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.api.ttoklip.global.security.auth.handler;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;

@Component
public class TokenErrorHandler implements AccessDeniedHandler {
// 권한 부족시 호출되는 핸들러
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
response.sendError(HttpServletResponse.SC_FORBIDDEN);
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package com.api.ttoklip.global.security.auth.service;

import com.api.ttoklip.domain.member.domain.Member;
import com.api.ttoklip.domain.member.domain.Role;
import com.api.ttoklip.domain.member.service.MemberService;
import com.api.ttoklip.domain.privacy.domain.Profile;
import com.api.ttoklip.domain.privacy.service.ProfileService;
import com.api.ttoklip.global.security.auth.dto.LoginRequest;
import com.api.ttoklip.global.security.auth.dto.LoginResponse;
import com.api.ttoklip.global.security.jwt.JwtProvider;
import com.api.ttoklip.global.security.oauth.userInfo.OAuth2UserInfo;
import com.api.ttoklip.global.security.auth.userInfo.OAuth2UserInfo;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

Expand All @@ -16,6 +20,7 @@ public class AuthService {
private final MemberService memberService;
private final OAuth2UserInfoFactory oAuth2UserInfoFactory;
private final JwtProvider jwtProvider;
private final ProfileService profileService;

public LoginResponse authenticate(final LoginRequest request) {
String provider = request.getProvider();
Expand All @@ -24,15 +29,39 @@ public LoginResponse authenticate(final LoginRequest request) {
OAuth2UserInfo userInfo = oAuth2UserInfoFactory.getUserInfo(provider, accessToken);
String email = userInfo.getEmail();

Member member = memberService.findByEmailOptional(email)
// .orElseGet() // 회원가입 or 로그인 처리
Optional<Member> memberOptional = memberService.findByEmailOptional(email);
if (memberOptional.isPresent()) {
Member member = memberOptional.get();
return getLoginResponse(member, false);
}

Member member = registerNewMember(userInfo, provider);
return getLoginResponse(member, true);
}

private LoginResponse getLoginResponse(final Member member, final boolean ifFirstLogin) {
// Server JWT Token
String jwtToken = jwtProvider.generateJwtToken(member.getEmail());

return LoginResponse.builder()
.jwtToken(jwtToken)
// .ifFirstLogin()
.ifFirstLogin(ifFirstLogin)
.build();
}

private Member registerNewMember(final OAuth2UserInfo userInfo, final String provider) {
System.out.println("AuthService.registerNewMember");
System.out.println("userInfo.getName() = " + userInfo.getName());
Member newMember = Member.builder()
.email(userInfo.getEmail())
.originName(userInfo.getName())
.provider(provider)
.role(Role.CLIENT)
.build();
memberService.register(newMember);

Profile profile = Profile.of(newMember, userInfo.getProfile());
profileService.register(profile);

return newMember;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

import com.api.ttoklip.global.exception.ApiException;
import com.api.ttoklip.global.exception.ErrorType;
import com.api.ttoklip.global.security.oauth.userInfo.KakaoUserInfo;
import com.api.ttoklip.global.security.oauth.userInfo.OAuth2UserInfo;
import com.api.ttoklip.global.security.auth.userInfo.KakaoUserInfo;
import com.api.ttoklip.global.security.auth.userInfo.OAuth2UserInfo;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand Down Expand Up @@ -46,7 +46,7 @@ private KakaoUserInfo getKakaoUserInfo(String token) {
.bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {
})
.block();

System.out.println("---------------------------------------- attributes = " + attributes);
return new KakaoUserInfo(attributes);
}
}
Loading

0 comments on commit 04b1b3b

Please sign in to comment.