Skip to content

Commit

Permalink
Merge pull request #44 from Kakaotech-18-Ecommerce/SCRUM-15-social-login
Browse files Browse the repository at this point in the history
[Feat] JWT + social login(카카오,네이버)
  • Loading branch information
yeopyeop-82 authored Aug 19, 2024
2 parents 62dabf0 + 47832af commit dad5443
Show file tree
Hide file tree
Showing 18 changed files with 643 additions and 13 deletions.
10 changes: 10 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ repositories {
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'

// mysql
runtimeOnly 'com.mysql:mysql-connector-j'

// 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 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
compileOnly 'org.projectlombok:lombok'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,7 @@ public static void main(String[] args) {
System.out.println("hello, world!");
System.out.println("GoodBye");
}

}



Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.kakaoteck.golagola.Repository;

import com.kakaoteck.golagola.entity.UserEntity;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<UserEntity, Long> {
UserEntity findByUsername(String username); // username을 전달하여 해당하는 엔티티 가져오기(JPA)
}



17 changes: 17 additions & 0 deletions src/main/java/com/kakaoteck/golagola/config/CorsMvcConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.kakaoteck.golagola.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class CorsMvcConfig implements WebMvcConfigurer {

@Override
public void addCorsMappings(CorsRegistry corsRegistry) {

corsRegistry.addMapping("/**")
.exposedHeaders("Set-Cookie")
.allowedOrigins("http://localhost:3000");
}
}
91 changes: 90 additions & 1 deletion src/main/java/com/kakaoteck/golagola/config/SecurityConfig.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
package com.kakaoteck.golagola.config;


import com.kakaoteck.golagola.jwt.JWTFilter;
import com.kakaoteck.golagola.jwt.JWTUtil;
import com.kakaoteck.golagola.oauth2.CustomSuccessHandler;
import com.kakaoteck.golagola.service.CustomOAuth2UserService;
import jakarta.servlet.http.HttpServletRequest;
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.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.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;

import java.util.Collections;

import com.kakaoteck.golagola.security.filter.JwtAuthenticationFilter;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
Expand All @@ -20,11 +40,80 @@
@Configuration
@EnableWebSecurity
public class SecurityConfig {


private final CustomOAuth2UserService customOAuth2UserService;
private final CustomSuccessHandler customSuccessHandler;
private final JWTUtil jwtUtil;
private final JwtAuthenticationFilter jwtAuthFilter;
private final AuthenticationProvider authenticationProvider;
private final LogoutHandler logoutHandler;

public SecurityConfig(CustomOAuth2UserService customOAuth2UserService, CustomSuccessHandler customSuccessHandler, JWTUtil jwtUtil) {

this.customOAuth2UserService = customOAuth2UserService;
this.customSuccessHandler = customSuccessHandler;
this.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(Collections.singletonList("http://localhost:3000"));
configuration.setAllowedMethods(Collections.singletonList("*"));
configuration.setAllowCredentials(true);
configuration.setAllowedHeaders(Collections.singletonList("*"));
configuration.setMaxAge(3600L);

configuration.setExposedHeaders(Collections.singletonList("Set-Cookie"));
configuration.setExposedHeaders(Collections.singletonList("Authorization"));

return configuration;
}
}));

// CSRF 보호 비활성화
http.csrf(csrf -> csrf.disable());

// 폼 로그인 비활성화
http.formLogin(login -> login.disable());

// HTTP Basic 인증 비활성화
http.httpBasic(basic -> basic.disable());

//JWTFilter 추가
// http.addFilterBefore(new JWTFilter(jwtUtil), UsernamePasswordAuthenticationFilter.class);

// 재로그인 방지를 위한 JWTFilter 선행해서 실행
http.addFilterAfter(new JWTFilter(jwtUtil), OAuth2LoginAuthenticationFilter.class);


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

//경로별 인가 작업
http.authorizeHttpRequests((auth) -> auth
.requestMatchers("/").permitAll()
.anyRequest().authenticated()); // 나머지 주소는 인증

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

return http.build();
}



private static final String[] WHITE_LIST_URL = {
"/api/v1/auth/**",
"/v2/api-docs",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.kakaoteck.golagola.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class MainController {

@GetMapping("/")
@ResponseBody
public String index(){
return "main route";
}
}

16 changes: 16 additions & 0 deletions src/main/java/com/kakaoteck/golagola/controller/MyController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.kakaoteck.golagola.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class MyController {

@GetMapping("/my")
@ResponseBody
public String myApi(){
return "myApi";
}

}
49 changes: 49 additions & 0 deletions src/main/java/com/kakaoteck/golagola/dto/CustomOAuth2User.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.kakaoteck.golagola.dto;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.core.user.OAuth2User;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;

public class CustomOAuth2User implements OAuth2User{

private final UserDTO userDTO;

public CustomOAuth2User(UserDTO userDTO) {

this.userDTO = userDTO;
}

@Override
public Map<String, Object> getAttributes() {
return Map.of();
}

@Override
public Collection<? extends GrantedAuthority> getAuthorities() { //Role값을 구현해주는 형태

Collection<GrantedAuthority> collection = new ArrayList<>();
collection.add(new GrantedAuthority() {

@Override
public String getAuthority() {

return userDTO.getRole();
}
});
return collection;
}

@Override
public String getName() {

return userDTO.getName();
}

public String getUsername() {
return userDTO.getUsername();
}

}
40 changes: 40 additions & 0 deletions src/main/java/com/kakaoteck/golagola/dto/KakaoResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.kakaoteck.golagola.dto;

import java.util.Map;

public class KakaoResponse implements OAuth2Response{

private final Map<String, Object> attribute;

public KakaoResponse(Map<String, Object> attribute) {
this.attribute = attribute;
}

@Override
public String getProvider() {
return "kakao";
}

@Override
public String getProviderId() {
return attribute.get("id").toString();
}

@Override
public String getEmail() {
// "kakao_account" 필드에 포함된 "email" 값을 가져옵니다.
Map<String, Object> kakaoAccount = (Map<String, Object>) attribute.get("kakao_account");
if (kakaoAccount != null) {
return kakaoAccount.get("email").toString();
}
return null;
}

@Override
public String getName() {
// "kakao_account" 필드에 포함된 "name" 값을 가져옵니다.
Map<String, Object> kakaoAccount = (Map<String, Object>) attribute.get("kakao_account");
Map<String, Object> profile = (Map<String, Object>) kakaoAccount.get("profile");
return profile.get("nickname").toString();
}
}
34 changes: 34 additions & 0 deletions src/main/java/com/kakaoteck/golagola/dto/NaverResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.kakaoteck.golagola.dto;

import java.io.Serializable;
import java.util.Map;

public class NaverResponse implements OAuth2Response{

private final Map<String, Object> attribute;

public NaverResponse(Map<String, Object> attribute) {

this.attribute = (Map<String, Object>) attribute.get("response");
}

@Override
public String getProvider() {
return "naver";
}

@Override
public String getProviderId() {
return attribute.get("id").toString();
}

@Override
public String getEmail() {
return attribute.get("email").toString();
}

@Override
public String getName() {
return attribute.get("name").toString();
}
}
14 changes: 14 additions & 0 deletions src/main/java/com/kakaoteck/golagola/dto/OAuth2Response.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.kakaoteck.golagola.dto;

public interface OAuth2Response {

//제공자 (Ex. naver, google, ...)
String getProvider();
//제공자에서 발급해주는 아이디(번호)
String getProviderId();
//이메일
String getEmail();
//사용자 실명 (설정한 이름)
String getName();

}
13 changes: 13 additions & 0 deletions src/main/java/com/kakaoteck/golagola/dto/UserDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.kakaoteck.golagola.dto;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class UserDTO {

private String role;
private String name;
private String username;
}
26 changes: 26 additions & 0 deletions src/main/java/com/kakaoteck/golagola/entity/UserEntity.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.kakaoteck.golagola.entity;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.Getter;
import lombok.Setter;

@Entity
@Getter
@Setter
public class UserEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String username;
private String name;
private String email;
private String role;



}
Loading

0 comments on commit dad5443

Please sign in to comment.