Skip to content

Commit

Permalink
Fix: 카카오 로그인 방법 수정
Browse files Browse the repository at this point in the history
  • Loading branch information
Jeongho427 committed May 7, 2024
1 parent b20be7d commit d94f748
Show file tree
Hide file tree
Showing 13 changed files with 378 additions and 104 deletions.
7 changes: 1 addition & 6 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,8 @@ dependencies {
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'

//security
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'

//jwt
implementation 'io.jsonwebtoken:jjwt-api:0.12.3'
implementation 'io.jsonwebtoken:jjwt-impl:0.12.3'
implementation 'io.jsonwebtoken:jjwt-jackson:0.12.3'
implementation 'io.jsonwebtoken:jjwt:0.9.1'

//secrets manager
implementation 'io.awspring.cloud:spring-cloud-starter-aws-secrets-manager-config:2.4.4'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,85 +1,31 @@
package com.meetup.teame.backend.domain.auth.config;

import com.meetup.teame.backend.domain.auth.jwt.JWTFilter;
import com.meetup.teame.backend.domain.auth.jwt.JWTUtil;
import com.meetup.teame.backend.domain.auth.oauth.handler.CustomSuccessHandler;
import com.meetup.teame.backend.domain.auth.oauth.service.CustomOAuth2UserService;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;

import java.util.List;

@RequiredArgsConstructor
@Configuration
@EnableWebSecurity
public class SecurityConfig {

private final CustomOAuth2UserService customOAuth2UserService;
private final CustomSuccessHandler customSuccessHandler;
private final JWTUtil jwtUtil;

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

http
.cors(corsCustomizer -> corsCustomizer.configurationSource(new CorsConfigurationSource() {

@Override
public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {

CorsConfiguration configuration = new CorsConfiguration();

configuration.setAllowedOrigins(List.of("http://localhost:3000", "https://api.yeongjin.site", "https://ddoba.vercel.app"));
configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS")); // Include OPTIONS method
configuration.setAllowCredentials(true);
configuration.setAllowedHeaders(List.of("*"));
configuration.setMaxAge(3600L);

configuration.setExposedHeaders(List.of("Set-Cookie", "Authorization")); // Expose Set-Cookie and Authorization headers

return configuration;
}
}));

// From 로그인 방식 disable
http
.formLogin((auth) -> auth.disable());

// HTTP Basic 인증 방식 disable
http
.httpBasic((auth) -> auth.disable());

// JWTFilter 추가
http
.addFilterAfter(new JWTFilter(jwtUtil), OAuth2LoginAuthenticationFilter.class);

// oauth2
http
.oauth2Login((oauth2) -> oauth2
.userInfoEndpoint((userInfoEndpointConfig) -> userInfoEndpointConfig
.userService(customOAuth2UserService))
.successHandler(customSuccessHandler)
);

// 경로별 인가 작업
http
.authorizeHttpRequests((auth) -> auth
.anyRequest().permitAll());

// 세션 설정 : STATELESS
http
.sessionManagement((session) -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS));

.csrf(c -> c.disable())
.authorizeHttpRequests(c -> c
.anyRequest().permitAll())
.cors(c -> c.configurationSource(request -> {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(List.of("http://localhost:3000"));
config.setAllowedMethods(List.of("*"));
config.setAllowCredentials(true);
config.setAllowedHeaders(List.of("*"));
config.setMaxAge(3600L);
return config;
}))
.headers(c -> c.frameOptions(c2 -> c2.disable()));
return http.build();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.meetup.teame.backend.domain.auth.jwt;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Setter
@Getter
@Component
@ConfigurationProperties("jwt")
public class JwtProperties {
private String issuer;
private String secretKey;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.meetup.teame.backend.domain.auth.jwt;

import com.meetup.teame.backend.domain.user.entity.User;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.util.Collections;
import java.util.Date;
import java.util.Set;

@RequiredArgsConstructor
@Service
public class JwtProvider {

private final JwtProperties jwtProperties;

public String generateToken(User user, Duration expiredAt) {
Date now = new Date();
return makeToken(new Date(now.getTime() + expiredAt.toMillis()), user);
}

//Jwt 토큰 생성 메서드
private String makeToken(Date expiry, User user) {
Date now = new Date();

return Jwts.builder()
.setHeaderParam(Header.TYPE, Header.JWT_TYPE) //헤더
.setIssuer(jwtProperties.getIssuer()) //내용
.setIssuedAt(now)
.setExpiration(expiry)
.setSubject(user.getEmail())
.claim("id",user.getId())
.signWith(SignatureAlgorithm.HS256, jwtProperties.getSecretKey()) //서명
.compact();
}

//JWT 토큰 유효성 검증 메서드
public boolean validToken(String token) {
try {
Jwts.parser()
.setSigningKey(jwtProperties.getSecretKey()) //시크릿 키로 복호화
.parseClaimsJws(token);
return true;
} catch (Exception e) { //복호화 중 에러 시 유효하지 않은 토큰
return false;
}
}

//토큰 기반으로 인증 정보 가져오는 메서드
public Authentication getAuthentication(String token) {
Claims claims = getClaims(token);
Set<SimpleGrantedAuthority> authorities = Collections.singleton(new SimpleGrantedAuthority("ROLE_USER"));

return new UsernamePasswordAuthenticationToken(new org.springframework.security.core.userdetails.User(claims.getSubject()
, "", authorities), token, authorities);
}

public Long getUserId(String token) {
Claims claims = getClaims(token);
return claims.get("id", Long.class);
}

private Claims getClaims(String token) {
return Jwts.parser() //클레임 조회
.setSigningKey(jwtProperties.getSecretKey())
.parseClaimsJws(token)
.getBody();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.meetup.teame.backend.domain.auth.oauth.controller;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.meetup.teame.backend.domain.auth.oauth.dto.CreateOauthUserRequest;
import com.meetup.teame.backend.domain.auth.oauth.dto.CreateUserRequest;
import com.meetup.teame.backend.domain.auth.oauth.service.KakaoService;
import com.meetup.teame.backend.domain.user.entity.Gender;
import com.meetup.teame.backend.domain.user.entity.User;
import com.meetup.teame.backend.domain.user.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;
import java.util.Optional;


@RequiredArgsConstructor
@RestController
@RequestMapping("/api")
public class KakaoController {

private final UserService userService;
private final KakaoService kakaoService;

@GetMapping("/login/kakao")
public ResponseEntity<Object> kakaoLogin(@RequestParam String code) throws JsonProcessingException {
String kakaoAccessToken = kakaoService.getKakaoAccessToken(code); //인가코드로 카카오 엑세스 토큰 받아오기
CreateOauthUserRequest request = kakaoService.getKakaoInfo(kakaoAccessToken); //엑세스 토큰으로 카카오 사용자 정보 받아오기
if(userExists(request.getEmail())) { //이미 가입된 회원
Optional<User> userOptional = userService.findByEmail(request.getEmail());
User user = userOptional.get();
HttpHeaders headers = kakaoService.getLoginHeader(user);

return ResponseEntity.ok().headers(headers).body("login");
//로그인 처리하기
} else { //신규 회원
return ResponseEntity.ok(request);
}
}

@PostMapping("/signup")
public ResponseEntity<Object> signup(@RequestBody CreateUserRequest request) { //이미 있는 회원인지 확인해야됨
User user = User.builder()
.name(request.getName())
.email(request.getEmail())
.gender(Gender.valueOf(request.getGender().toUpperCase()))
.location(request.getLocation())
.build();
Long userId = userService.save(user);
HttpHeaders headers = kakaoService.getLoginHeader(userService.findById(userId));
return ResponseEntity.ok().headers(headers).body("OK");
}

private boolean userExists(String email) {
Optional<User> userOptional = userService.findByEmail(email);
if(userOptional.isPresent()) {
return true;
}
else {
return false;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.meetup.teame.backend.domain.auth.oauth.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
public class CreateOauthUserRequest {
private String email; // 선택 동의
private String gender; // 필수 동의
private String nickname; // 필수 동의
private String name; // 필수 동의
private String birthYear; // 필수 동의
private String profileImage; // 선택 동의
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.meetup.teame.backend.domain.auth.oauth.dto;

import com.meetup.teame.backend.domain.user.entity.Gender;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.Comment;

@AllArgsConstructor
@NoArgsConstructor
@Getter
public class CreateUserRequest {

@Comment("사용자 이름")
private String name;

@Comment("사용자 이미지")
private String imageUrl;

@Enumerated(EnumType.STRING)
private Gender gender;

private String location;

//test
private String birthyear;


private String email;
}
Loading

0 comments on commit d94f748

Please sign in to comment.