Skip to content

Commit

Permalink
Merge pull request kookmin-sw#5 from capstone-maru/feat/kookmin-sw#2-…
Browse files Browse the repository at this point in the history
…OAuthKakao

Feat/kookmin-sw#2 o auth kakao
  • Loading branch information
cheesecrust authored Mar 7, 2024
2 parents 79948fd + b4d81da commit d022adc
Show file tree
Hide file tree
Showing 16 changed files with 900 additions and 1 deletion.
8 changes: 8 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,20 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.h2database:h2'
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'

// spring security 설정
implementation 'org.springframework.boot:spring-boot-starter-security'
testImplementation 'org.springframework.security:spring-security-test'

// OAuth 2.0 설정
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
}

tasks.named('test') {
Expand Down
17 changes: 17 additions & 0 deletions src/main/java/org/capstone/maru/config/JpaConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.capstone.maru.config;

import java.util.Optional;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.domain.AuditorAware;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@EnableJpaAuditing
@Configuration
public class JpaConfig {

@Bean
public AuditorAware<String> auditorAware() {
return () -> Optional.of("tester");
}
}
94 changes: 94 additions & 0 deletions src/main/java/org/capstone/maru/config/SecurityConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package org.capstone.maru.config;


import org.capstone.maru.dto.security.KakaoOAuth2Response;
import org.capstone.maru.dto.security.SharedPostPrincipal;
import org.capstone.maru.service.MemberAccountService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
public class SecurityConfig {

@Bean
@ConditionalOnProperty(name = "spring.h2.console.enabled", havingValue = "true")
public WebSecurityCustomizer configureH2ConsoleEnable() {
return web -> web.ignoring()
.requestMatchers(PathRequest.toH2Console());
}

@Bean
public SecurityFilterChain securityFilterChain(
HttpSecurity httpSecurity,
OAuth2UserService<OAuth2UserRequest, OAuth2User> oAuth2UserService
) throws Exception {
return httpSecurity
.authorizeHttpRequests(auth -> auth
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
.requestMatchers(
HttpMethod.GET,
"/"
).permitAll()
.anyRequest().authenticated()
)
.oauth2Login(oAuth -> oAuth
.userInfoEndpoint(userInfo -> userInfo
.userService(oAuth2UserService))
)
.csrf(
csrf -> csrf
.ignoringRequestMatchers("/api/**")
)
.build();
}


@Bean
public OAuth2UserService<OAuth2UserRequest, OAuth2User> oAuth2UserService(
MemberAccountService memberAccountService,
PasswordEncoder passwordEncoder
) {
final DefaultOAuth2UserService delegate = new DefaultOAuth2UserService();

return userRequest -> {
OAuth2User oAuth2User = delegate.loadUser(userRequest);

KakaoOAuth2Response kakaoOAuthResponse = KakaoOAuth2Response.from(
oAuth2User.getAttributes());
String registrationId = userRequest.getClientRegistration()
.getRegistrationId(); // "kakao"
String providerId = String.valueOf(kakaoOAuthResponse.id());
String userId = registrationId + "_" + providerId;

return memberAccountService
.searchMember(userId)
.map(SharedPostPrincipal::from)
.orElseGet(() ->
SharedPostPrincipal.from(
memberAccountService.saveUser(
userId,
kakaoOAuthResponse.email(),
kakaoOAuthResponse.nickname()
)
)
);
};
}

@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
}
20 changes: 20 additions & 0 deletions src/main/java/org/capstone/maru/controller/MainController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.capstone.maru.controller;

import org.capstone.maru.dto.security.SharedPostPrincipal;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MainController {

@GetMapping("/")
public String root() {
return "home";
}

@GetMapping("/test")
public String test(@AuthenticationPrincipal SharedPostPrincipal sharedPostPrincipal) {
return sharedPostPrincipal.getName();
}
}
39 changes: 39 additions & 0 deletions src/main/java/org/capstone/maru/domain/AuditingFields.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.capstone.maru.domain;

import jakarta.persistence.Column;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.MappedSuperclass;
import java.time.LocalDateTime;
import lombok.Getter;
import lombok.ToString;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import org.springframework.format.annotation.DateTimeFormat;

@Getter
@ToString(callSuper = true)
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class AuditingFields {

@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
@CreatedDate
@Column(nullable = false)
protected LocalDateTime createdAt; // 생성일시

@CreatedBy
@Column(nullable = false, updatable = false, length = 100)
protected String createdBy; // 생성자

@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
@LastModifiedDate
@Column(nullable = false)
protected LocalDateTime modifiedAt; // 수정일시

@LastModifiedBy
@Column(nullable = false, length = 100)
protected String modifiedBy; // 수정자
}
84 changes: 84 additions & 0 deletions src/main/java/org/capstone/maru/domain/MemberAccount.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package org.capstone.maru.domain;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Index;
import jakarta.persistence.Table;
import java.util.Objects;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@ToString(callSuper = true)
@Table(indexes = {
@Index(columnList = "memberId", unique = true),
@Index(columnList = "email", unique = true),
@Index(columnList = "createdAt"),
@Index(columnList = "createdBy")
})
@Entity
public class MemberAccount extends AuditingFields {

@Id
@Column(nullable = false, length = 50)
private String memberId;

@Setter
@Column(length = 100)
private String email;

@Setter
@Column(length = 100)
private String nickname;

private MemberAccount(
String memberId,
String email,
String nickname,
String createdBy
) {
this.memberId = memberId;
this.email = email;
this.nickname = nickname;
this.createdBy = createdBy;
this.modifiedBy = createdBy;
}

public static MemberAccount of(
String memberId,
String email,
String nickname
) {
return new MemberAccount(memberId, email, nickname, null);
}

public static MemberAccount of(
String memberId,
String email,
String nickname,
String createdBy
) {
return new MemberAccount(memberId, email, nickname, createdBy);
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof MemberAccount that)) {
return false;
}
return this.getMemberId() != null && this.getMemberId().equals(that.getMemberId());
}

@Override
public int hashCode() {
return Objects.hash(this.getMemberId());
}
}
72 changes: 72 additions & 0 deletions src/main/java/org/capstone/maru/dto/MemberAccountDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package org.capstone.maru.dto;

import java.time.LocalDateTime;
import org.capstone.maru.domain.MemberAccount;

public record MemberAccountDto(
String memberId,
String email,
String nickname,
LocalDateTime createdAt,
String createdBy,
LocalDateTime modifiedAt,
String modifiedBy
) {

public static MemberAccountDto of(
String memberId,
String email,
String nickname
) {
return new MemberAccountDto(
memberId,
email,
nickname,
null,
null,
null,
null
);
}

public static MemberAccountDto of(
String memberId,
String email,
String nickname,
LocalDateTime createdAt,
String createdBy,
LocalDateTime modifiedAt,
String modifiedBy
) {
return new MemberAccountDto(
memberId,
email,
nickname,
createdAt,
createdBy,
modifiedAt,
modifiedBy
);
}

public static MemberAccountDto from(MemberAccount entity) {
return new MemberAccountDto(
entity.getMemberId(),
entity.getEmail(),
entity.getNickname(),
entity.getCreatedAt(),
entity.getCreatedBy(),
entity.getModifiedAt(),
entity.getModifiedBy()
);
}

public MemberAccount toEntity() {
return MemberAccount.of(
memberId,
email,
nickname,
createdBy
);
}
}
Loading

0 comments on commit d022adc

Please sign in to comment.