Skip to content

Commit f2beff6

Browse files
committed
Merge branch 'feat/kookmin-sw#2-OAuthNaver' of https://github.com/capstone-maru/back-end into feat/kookmin-sw#2-OAuthNaver
2 parents ebf2d45 + a349d85 commit f2beff6

24 files changed

+1195
-1
lines changed

build.gradle

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,20 @@ dependencies {
1919
implementation 'org.springframework.boot:spring-boot-starter-web'
2020
implementation 'org.springframework.boot:spring-boot-starter-actuator'
2121
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
22+
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
2223
compileOnly 'org.projectlombok:lombok'
2324
developmentOnly 'org.springframework.boot:spring-boot-devtools'
2425
runtimeOnly 'com.h2database:h2'
2526
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
2627
annotationProcessor 'org.projectlombok:lombok'
2728
testImplementation 'org.springframework.boot:spring-boot-starter-test'
29+
30+
// spring security 설정
31+
implementation 'org.springframework.boot:spring-boot-starter-security'
32+
testImplementation 'org.springframework.security:spring-security-test'
33+
34+
// OAuth 2.0 설정
35+
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
2836
}
2937

3038
tasks.named('test') {
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package org.capstone.maru.config;
2+
3+
import java.util.Optional;
4+
import org.springframework.context.annotation.Bean;
5+
import org.springframework.context.annotation.Configuration;
6+
import org.springframework.data.domain.AuditorAware;
7+
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
8+
9+
@EnableJpaAuditing
10+
@Configuration
11+
public class JpaConfig {
12+
13+
@Bean
14+
public AuditorAware<String> auditorAware() {
15+
return () -> Optional.of("tester");
16+
}
17+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package org.capstone.maru.config;
2+
3+
4+
import lombok.extern.slf4j.Slf4j;
5+
import org.capstone.maru.dto.security.KakaoOAuth2Response;
6+
import org.capstone.maru.dto.security.SharedPostPrincipal;
7+
import org.capstone.maru.service.MemberAccountService;
8+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
9+
import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
10+
import org.springframework.context.annotation.Bean;
11+
import org.springframework.context.annotation.Configuration;
12+
import org.springframework.http.HttpMethod;
13+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
14+
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
15+
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
16+
import org.springframework.security.crypto.password.PasswordEncoder;
17+
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
18+
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
19+
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
20+
import org.springframework.security.oauth2.core.user.OAuth2User;
21+
import org.springframework.security.web.SecurityFilterChain;
22+
23+
@Slf4j
24+
@Configuration
25+
public class SecurityConfig {
26+
27+
@Bean
28+
@ConditionalOnProperty(name = "spring.h2.console.enabled", havingValue = "true")
29+
public WebSecurityCustomizer configureH2ConsoleEnable() {
30+
return web -> web.ignoring()
31+
.requestMatchers(PathRequest.toH2Console());
32+
}
33+
34+
@Bean
35+
public SecurityFilterChain securityFilterChain(
36+
HttpSecurity httpSecurity,
37+
OAuth2UserService<OAuth2UserRequest, OAuth2User> oAuth2UserService
38+
) throws Exception {
39+
log.info("SecurityFilterChain 빈 등록 완료");
40+
return httpSecurity
41+
.authorizeHttpRequests(auth -> auth
42+
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
43+
.requestMatchers(
44+
HttpMethod.GET,
45+
"/"
46+
).permitAll()
47+
.anyRequest().authenticated()
48+
)
49+
.oauth2Login(oAuth -> oAuth
50+
.userInfoEndpoint(userInfo -> userInfo
51+
.userService(oAuth2UserService)
52+
)
53+
)
54+
.csrf(
55+
csrf -> csrf
56+
.ignoringRequestMatchers("/api/**")
57+
.disable()
58+
)
59+
.build();
60+
}
61+
62+
63+
@Bean
64+
public OAuth2UserService<OAuth2UserRequest, OAuth2User> oAuth2UserService(
65+
MemberAccountService memberAccountService,
66+
PasswordEncoder passwordEncoder
67+
) {
68+
final DefaultOAuth2UserService delegate = new DefaultOAuth2UserService();
69+
70+
return userRequest -> {
71+
OAuth2User oAuth2User = delegate.loadUser(userRequest);
72+
73+
KakaoOAuth2Response kakaoOAuthResponse = KakaoOAuth2Response.from(
74+
oAuth2User.getAttributes());
75+
76+
String registrationId = userRequest.getClientRegistration()
77+
.getRegistrationId(); // "kakao"
78+
79+
String providerId = String.valueOf(kakaoOAuthResponse.id());
80+
String memberId = registrationId + "_" + providerId;
81+
82+
return memberAccountService
83+
.searchMember(memberId)
84+
.map(SharedPostPrincipal::from)
85+
.orElseGet(() ->
86+
SharedPostPrincipal.from(
87+
memberAccountService.saveUser(
88+
memberId,
89+
kakaoOAuthResponse.email(),
90+
kakaoOAuthResponse.nickname()
91+
)
92+
)
93+
);
94+
};
95+
}
96+
97+
@Bean
98+
public PasswordEncoder passwordEncoder() {
99+
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
100+
}
101+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package org.capstone.maru.controller;
2+
3+
import org.capstone.maru.dto.security.SharedPostPrincipal;
4+
import org.springframework.security.core.annotation.AuthenticationPrincipal;
5+
import org.springframework.web.bind.annotation.GetMapping;
6+
import org.springframework.web.bind.annotation.RestController;
7+
8+
@RestController
9+
public class MainController {
10+
11+
@GetMapping("/")
12+
public String root() {
13+
return "home";
14+
}
15+
16+
@GetMapping("/test")
17+
public String test(@AuthenticationPrincipal SharedPostPrincipal sharedPostPrincipal) {
18+
return sharedPostPrincipal.getName();
19+
}
20+
21+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package org.capstone.maru.domain;
2+
3+
import jakarta.persistence.Column;
4+
import jakarta.persistence.EntityListeners;
5+
import jakarta.persistence.MappedSuperclass;
6+
import java.time.LocalDateTime;
7+
import lombok.Getter;
8+
import lombok.ToString;
9+
import org.springframework.data.annotation.CreatedBy;
10+
import org.springframework.data.annotation.CreatedDate;
11+
import org.springframework.data.annotation.LastModifiedBy;
12+
import org.springframework.data.annotation.LastModifiedDate;
13+
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
14+
import org.springframework.format.annotation.DateTimeFormat;
15+
16+
@Getter
17+
@ToString(callSuper = true)
18+
@MappedSuperclass
19+
@EntityListeners(AuditingEntityListener.class)
20+
public class AuditingFields {
21+
22+
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
23+
@CreatedDate
24+
@Column(nullable = false)
25+
protected LocalDateTime createdAt; // 생성일시
26+
27+
@CreatedBy
28+
@Column(nullable = false, updatable = false, length = 100)
29+
protected String createdBy; // 생성자
30+
31+
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
32+
@LastModifiedDate
33+
@Column(nullable = false)
34+
protected LocalDateTime modifiedAt; // 수정일시
35+
36+
@LastModifiedBy
37+
@Column(nullable = false, length = 100)
38+
protected String modifiedBy; // 수정자
39+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package org.capstone.maru.domain;
2+
3+
import jakarta.persistence.Column;
4+
import jakarta.persistence.Entity;
5+
import jakarta.persistence.Id;
6+
import jakarta.persistence.Index;
7+
import jakarta.persistence.Table;
8+
import java.util.Objects;
9+
import lombok.AccessLevel;
10+
import lombok.Builder;
11+
import lombok.Getter;
12+
import lombok.NoArgsConstructor;
13+
import lombok.Setter;
14+
import lombok.ToString;
15+
import org.capstone.maru.dto.Role;
16+
import org.capstone.maru.dto.SocialType;
17+
18+
@Getter
19+
@NoArgsConstructor(access = AccessLevel.PROTECTED)
20+
@ToString(callSuper = true)
21+
@Table(indexes = {
22+
@Index(columnList = "memberId", unique = true),
23+
@Index(columnList = "email", unique = true),
24+
@Index(columnList = "createdAt"),
25+
@Index(columnList = "createdBy")
26+
})
27+
@Entity
28+
public class MemberAccount extends AuditingFields {
29+
30+
@Id
31+
@Column(nullable = false, length = 50)
32+
private String memberId;
33+
34+
@Setter
35+
@Column(length = 100)
36+
private String email;
37+
38+
@Setter
39+
@Column(length = 100)
40+
private String nickname;
41+
42+
private SocialType socialType;
43+
44+
private Role role;
45+
46+
@Builder
47+
private MemberAccount(
48+
String memberId,
49+
String email,
50+
String nickname,
51+
String createdBy,
52+
SocialType socialType
53+
) {
54+
this.memberId = memberId;
55+
this.email = email;
56+
this.nickname = nickname;
57+
this.createdBy = createdBy;
58+
this.modifiedBy = createdBy;
59+
this.socialType = socialType;
60+
}
61+
62+
public static MemberAccount of(
63+
String memberId,
64+
String email,
65+
String nickname
66+
) {
67+
return new MemberAccount(memberId, email, nickname, null, null);
68+
}
69+
70+
public static MemberAccount of(
71+
String memberId,
72+
String email,
73+
String nickname,
74+
String createdBy
75+
) {
76+
return new MemberAccount(memberId, email, nickname, createdBy, null);
77+
}
78+
79+
@Override
80+
public boolean equals(Object o) {
81+
if (this == o) {
82+
return true;
83+
}
84+
if (!(o instanceof MemberAccount that)) {
85+
return false;
86+
}
87+
return this.getMemberId() != null && this.getMemberId().equals(that.getMemberId());
88+
}
89+
90+
@Override
91+
public int hashCode() {
92+
return Objects.hash(this.getMemberId());
93+
}
94+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package org.capstone.maru.dto;
2+
3+
import java.util.Collection;
4+
import java.util.Map;
5+
import org.springframework.security.core.GrantedAuthority;
6+
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
7+
8+
public class CustomOAuth2User extends DefaultOAuth2User {
9+
10+
public CustomOAuth2User(Collection<? extends GrantedAuthority> authorities,
11+
Map<String, Object> attributes, String nameAttributeKey, String email) {
12+
super(authorities, attributes, nameAttributeKey);
13+
}
14+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package org.capstone.maru.dto;
2+
3+
import java.util.Map;
4+
5+
public class KakaoOAuth2UserInfo extends OAuth2UserInfo {
6+
7+
public KakaoOAuth2UserInfo(Map<String, Object> attributes) {
8+
super(attributes);
9+
}
10+
11+
@Override
12+
public String getId() {
13+
return String.valueOf(attributes.get("id"));
14+
}
15+
16+
@Override
17+
public String getNickname() {
18+
return null;
19+
}
20+
21+
}

0 commit comments

Comments
 (0)