Skip to content

Commit d022adc

Browse files
authored
Merge pull request kookmin-sw#5 from capstone-maru/feat/kookmin-sw#2-OAuthKakao
Feat/kookmin-sw#2 o auth kakao
2 parents 79948fd + b4d81da commit d022adc

File tree

16 files changed

+900
-1
lines changed

16 files changed

+900
-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: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package org.capstone.maru.config;
2+
3+
4+
import org.capstone.maru.dto.security.KakaoOAuth2Response;
5+
import org.capstone.maru.dto.security.SharedPostPrincipal;
6+
import org.capstone.maru.service.MemberAccountService;
7+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
8+
import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
9+
import org.springframework.context.annotation.Bean;
10+
import org.springframework.context.annotation.Configuration;
11+
import org.springframework.http.HttpMethod;
12+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
13+
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
14+
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
15+
import org.springframework.security.crypto.password.PasswordEncoder;
16+
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
17+
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
18+
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
19+
import org.springframework.security.oauth2.core.user.OAuth2User;
20+
import org.springframework.security.web.SecurityFilterChain;
21+
22+
@Configuration
23+
public class SecurityConfig {
24+
25+
@Bean
26+
@ConditionalOnProperty(name = "spring.h2.console.enabled", havingValue = "true")
27+
public WebSecurityCustomizer configureH2ConsoleEnable() {
28+
return web -> web.ignoring()
29+
.requestMatchers(PathRequest.toH2Console());
30+
}
31+
32+
@Bean
33+
public SecurityFilterChain securityFilterChain(
34+
HttpSecurity httpSecurity,
35+
OAuth2UserService<OAuth2UserRequest, OAuth2User> oAuth2UserService
36+
) throws Exception {
37+
return httpSecurity
38+
.authorizeHttpRequests(auth -> auth
39+
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
40+
.requestMatchers(
41+
HttpMethod.GET,
42+
"/"
43+
).permitAll()
44+
.anyRequest().authenticated()
45+
)
46+
.oauth2Login(oAuth -> oAuth
47+
.userInfoEndpoint(userInfo -> userInfo
48+
.userService(oAuth2UserService))
49+
)
50+
.csrf(
51+
csrf -> csrf
52+
.ignoringRequestMatchers("/api/**")
53+
)
54+
.build();
55+
}
56+
57+
58+
@Bean
59+
public OAuth2UserService<OAuth2UserRequest, OAuth2User> oAuth2UserService(
60+
MemberAccountService memberAccountService,
61+
PasswordEncoder passwordEncoder
62+
) {
63+
final DefaultOAuth2UserService delegate = new DefaultOAuth2UserService();
64+
65+
return userRequest -> {
66+
OAuth2User oAuth2User = delegate.loadUser(userRequest);
67+
68+
KakaoOAuth2Response kakaoOAuthResponse = KakaoOAuth2Response.from(
69+
oAuth2User.getAttributes());
70+
String registrationId = userRequest.getClientRegistration()
71+
.getRegistrationId(); // "kakao"
72+
String providerId = String.valueOf(kakaoOAuthResponse.id());
73+
String userId = registrationId + "_" + providerId;
74+
75+
return memberAccountService
76+
.searchMember(userId)
77+
.map(SharedPostPrincipal::from)
78+
.orElseGet(() ->
79+
SharedPostPrincipal.from(
80+
memberAccountService.saveUser(
81+
userId,
82+
kakaoOAuthResponse.email(),
83+
kakaoOAuthResponse.nickname()
84+
)
85+
)
86+
);
87+
};
88+
}
89+
90+
@Bean
91+
public PasswordEncoder passwordEncoder() {
92+
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
93+
}
94+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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+
}
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: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
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.Getter;
11+
import lombok.NoArgsConstructor;
12+
import lombok.Setter;
13+
import lombok.ToString;
14+
15+
@Getter
16+
@NoArgsConstructor(access = AccessLevel.PROTECTED)
17+
@ToString(callSuper = true)
18+
@Table(indexes = {
19+
@Index(columnList = "memberId", unique = true),
20+
@Index(columnList = "email", unique = true),
21+
@Index(columnList = "createdAt"),
22+
@Index(columnList = "createdBy")
23+
})
24+
@Entity
25+
public class MemberAccount extends AuditingFields {
26+
27+
@Id
28+
@Column(nullable = false, length = 50)
29+
private String memberId;
30+
31+
@Setter
32+
@Column(length = 100)
33+
private String email;
34+
35+
@Setter
36+
@Column(length = 100)
37+
private String nickname;
38+
39+
private MemberAccount(
40+
String memberId,
41+
String email,
42+
String nickname,
43+
String createdBy
44+
) {
45+
this.memberId = memberId;
46+
this.email = email;
47+
this.nickname = nickname;
48+
this.createdBy = createdBy;
49+
this.modifiedBy = createdBy;
50+
}
51+
52+
public static MemberAccount of(
53+
String memberId,
54+
String email,
55+
String nickname
56+
) {
57+
return new MemberAccount(memberId, email, nickname, null);
58+
}
59+
60+
public static MemberAccount of(
61+
String memberId,
62+
String email,
63+
String nickname,
64+
String createdBy
65+
) {
66+
return new MemberAccount(memberId, email, nickname, createdBy);
67+
}
68+
69+
@Override
70+
public boolean equals(Object o) {
71+
if (this == o) {
72+
return true;
73+
}
74+
if (!(o instanceof MemberAccount that)) {
75+
return false;
76+
}
77+
return this.getMemberId() != null && this.getMemberId().equals(that.getMemberId());
78+
}
79+
80+
@Override
81+
public int hashCode() {
82+
return Objects.hash(this.getMemberId());
83+
}
84+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package org.capstone.maru.dto;
2+
3+
import java.time.LocalDateTime;
4+
import org.capstone.maru.domain.MemberAccount;
5+
6+
public record MemberAccountDto(
7+
String memberId,
8+
String email,
9+
String nickname,
10+
LocalDateTime createdAt,
11+
String createdBy,
12+
LocalDateTime modifiedAt,
13+
String modifiedBy
14+
) {
15+
16+
public static MemberAccountDto of(
17+
String memberId,
18+
String email,
19+
String nickname
20+
) {
21+
return new MemberAccountDto(
22+
memberId,
23+
email,
24+
nickname,
25+
null,
26+
null,
27+
null,
28+
null
29+
);
30+
}
31+
32+
public static MemberAccountDto of(
33+
String memberId,
34+
String email,
35+
String nickname,
36+
LocalDateTime createdAt,
37+
String createdBy,
38+
LocalDateTime modifiedAt,
39+
String modifiedBy
40+
) {
41+
return new MemberAccountDto(
42+
memberId,
43+
email,
44+
nickname,
45+
createdAt,
46+
createdBy,
47+
modifiedAt,
48+
modifiedBy
49+
);
50+
}
51+
52+
public static MemberAccountDto from(MemberAccount entity) {
53+
return new MemberAccountDto(
54+
entity.getMemberId(),
55+
entity.getEmail(),
56+
entity.getNickname(),
57+
entity.getCreatedAt(),
58+
entity.getCreatedBy(),
59+
entity.getModifiedAt(),
60+
entity.getModifiedBy()
61+
);
62+
}
63+
64+
public MemberAccount toEntity() {
65+
return MemberAccount.of(
66+
memberId,
67+
email,
68+
nickname,
69+
createdBy
70+
);
71+
}
72+
}

0 commit comments

Comments
 (0)