diff --git a/.gitignore b/.gitignore
index 53870f4..4e5ab58 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,9 @@
# Ignore Gradle build output directory
build/
+# application config
+application-oauth.yml
+
### intelliJ
.idea/
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index b589d56..ca1ce44 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -1,6 +1,15 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index ff95d61..d7c4317 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -31,6 +31,10 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter:2.6.1'
testImplementation 'org.springframework.boot:spring-boot-starter-test:2.6.1'
+ // OAuth2 - 소셜 로그인 기능 구현시 필요한 의존성
+ implementation 'org.springframework.boot:spring-boot-starter-oauth2-client:2.6.2'
+
+
// Lombok
compileOnly 'org.projectlombok:lombok:1.18.22'
annotationProcessor 'org.projectlombok:lombok:1.18.22'
@@ -41,6 +45,12 @@ dependencies {
// H2-db
runtimeOnly 'com.h2database:h2'
+ // Spring Security
+ implementation 'org.springframework.boot:spring-boot-starter-security'
+
+ // Spring Developer Tools
+ developmentOnly 'org.springframework.boot:spring-boot-devtools'
+
// This dependency is used by the application.
implementation 'com.google.guava:guava:30.1.1-jre'
}
diff --git a/app/src/main/java/mentorview/App.java b/app/src/main/java/mentorview/App.java
index 7c3162c..b8851bf 100644
--- a/app/src/main/java/mentorview/App.java
+++ b/app/src/main/java/mentorview/App.java
@@ -3,6 +3,9 @@
*/
package mentorview;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
public class App {
public String getGreeting() {
return "Hello World!";
diff --git a/app/src/main/java/mentorview/Infra/UserRepository.java b/app/src/main/java/mentorview/Infra/UserRepository.java
index 8f89386..7646ba7 100644
--- a/app/src/main/java/mentorview/Infra/UserRepository.java
+++ b/app/src/main/java/mentorview/Infra/UserRepository.java
@@ -4,6 +4,9 @@
import mentorview.domain.User;
import org.springframework.data.jpa.repository.JpaRepository;
-public interface SignUpRepository extends JpaRepository {
- User findByEmail(String email);
+import java.util.Optional;
+
+public interface UserRepository extends JpaRepository {
+ // email을 통해 가입된 회원 인지를 확인
+ Optional findByEmail(String email);
}
diff --git a/app/src/main/java/mentorview/application/CustomOAuth2UserService.java b/app/src/main/java/mentorview/application/CustomOAuth2UserService.java
new file mode 100644
index 0000000..9a748e3
--- /dev/null
+++ b/app/src/main/java/mentorview/application/CustomOAuth2UserService.java
@@ -0,0 +1,56 @@
+package mentorview.application;
+
+import lombok.RequiredArgsConstructor;
+import mentorview.Infra.UserRepository;
+import mentorview.domain.User;
+import mentorview.dto.OAuthAttributes;
+import mentorview.dto.SessionUser;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+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.OAuth2AuthenticationException;
+import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
+import org.springframework.security.oauth2.core.user.OAuth2User;
+import org.springframework.stereotype.Service;
+
+import javax.servlet.http.HttpSession;
+import java.util.Collections;
+
+@Service
+@RequiredArgsConstructor
+public class CustomOAuth2UserService implements OAuth2UserService {
+ private final UserRepository userRepository;
+ private final HttpSession httpSession;
+
+ @Override
+ public OAuth2User loadUser(OAuth2UserRequest oAuth2UserRequest) throws OAuth2AuthenticationException {
+ OAuth2UserService delegate = new DefaultOAuth2UserService();
+ OAuth2User oAuth2User = delegate.loadUser(oAuth2UserRequest);
+
+ String registrationId = oAuth2UserRequest.getClientRegistration()
+ .getRegistrationId();
+
+ String userNameAttributeName = oAuth2UserRequest.getClientRegistration()
+ .getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName();
+
+ OAuthAttributes attributes = OAuthAttributes.of(
+ registrationId, userNameAttributeName, oAuth2User.getAttributes()
+ );
+
+ User user = saveOrUpdate(attributes);
+ httpSession.setAttribute("user", new SessionUser(user));
+
+ return new DefaultOAuth2User(Collections.singleton(
+ new SimpleGrantedAuthority(user.getRoleKey())),
+ attributes.getAttributes(),
+ attributes.getNameAttributeKey());
+ }
+
+ private User saveOrUpdate(OAuthAttributes attributes) {
+ User user = (User) userRepository.findByEmail(attributes.getEmail())
+ .map(entity -> entity.update(attributes.getName(), attributes.getPicture()))
+ .orElse(attributes.toEntity());
+ return userRepository.save(user);
+ }
+}
diff --git a/app/src/main/java/mentorview/application/UserService.java b/app/src/main/java/mentorview/application/UserService.java
new file mode 100644
index 0000000..ffe3ab6
--- /dev/null
+++ b/app/src/main/java/mentorview/application/UserService.java
@@ -0,0 +1,8 @@
+package mentorview.application;
+
+import org.springframework.stereotype.Service;
+
+@Service
+public class UserService {
+
+}
diff --git a/app/src/main/java/mentorview/controllers/LoginController.java b/app/src/main/java/mentorview/controllers/LoginController.java
new file mode 100644
index 0000000..dee48ba
--- /dev/null
+++ b/app/src/main/java/mentorview/controllers/LoginController.java
@@ -0,0 +1,21 @@
+package mentorview.controllers;
+
+
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpSession;
+
+
+@RestController
+public class LoginController {
+
+ private final HttpSession httpSession;
+
+ public LoginController(HttpSession httpSession) {
+ this.httpSession = httpSession;
+ }
+
+ @GetMapping("/user")
+ public
+}
diff --git a/app/src/main/java/mentorview/domain/BaseTimeEntity.java b/app/src/main/java/mentorview/domain/BaseTimeEntity.java
index 921fef1..0f57c20 100644
--- a/app/src/main/java/mentorview/domain/BaseTimeEntity.java
+++ b/app/src/main/java/mentorview/domain/BaseTimeEntity.java
@@ -1,7 +1,37 @@
package mentorview.domain;
-import javax.persistence.Entity;
+import lombok.Getter;
+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;
-@Entity
-public class BaseTimeEntity {
+import javax.persistence.Column;
+import javax.persistence.EntityListeners;
+import javax.persistence.MappedSuperclass;
+import java.time.LocalDateTime;
+
+@Getter
+@MappedSuperclass
+@EntityListeners(AuditingEntityListener.class)
+public abstract class BaseTimeEntity {
+
+ @CreatedDate
+ @Column(updatable = false)
+ private LocalDateTime createdDate;
+
+ @LastModifiedDate
+ @Column
+ private LocalDateTime modifiedDate;
+
+ @CreatedBy
+ @Column(updatable = false)
+ private String createdBy;
+
+ @LastModifiedBy
+ @Column
+ private String updatedBy;
+
+ public abstract Object update(String name, String picture);
}
\ No newline at end of file
diff --git a/app/src/main/java/mentorview/domain/Role.java b/app/src/main/java/mentorview/domain/Role.java
index b722ac6..510ad65 100644
--- a/app/src/main/java/mentorview/domain/Role.java
+++ b/app/src/main/java/mentorview/domain/Role.java
@@ -1,2 +1,15 @@
-package mentorview.domain;public class Role {
+package mentorview.domain;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@Getter
+@RequiredArgsConstructor
+public enum Role {
+ USER("ROLE_USER", "회원", true),
+ GET_TOTAL_REVIEW("ROLE_GET_TOTAL_REVIEW", "비밀답변 권한", false);
+
+ private final String key;
+ private final String title;
+ private final boolean total_review;
}
diff --git a/app/src/main/java/mentorview/domain/User.java b/app/src/main/java/mentorview/domain/User.java
index b849aee..23101b1 100644
--- a/app/src/main/java/mentorview/domain/User.java
+++ b/app/src/main/java/mentorview/domain/User.java
@@ -15,30 +15,35 @@
@Entity
@Getter
@NoArgsConstructor
-public class User extends BaseTimeEntity {
+public abstract class User extends BaseTimeEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
- @Column(nullable = false)
+ @Column(length = 20, nullable = false)
private String name;
@Column(nullable = false)
private String email;
+ @Column
+ private String picture;
+
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private Role role;
@Builder
- public User(String name, String email, Role role) {
+ public User(String name, String email, String picture, Role role) {
this.name = name;
this.email = email;
+ this.picture = picture;
this.role = role;
}
- public User update(String name, Role role) {
+ public User update(String name, String picture, Role role) {
this.name = name;
+ this.picture = picture;
this.role = role;
return this;
@@ -47,4 +52,6 @@ public User update(String name, Role role) {
public String getRoleKey() {
return this.role.getKey();
}
+
+
}
diff --git a/app/src/main/java/mentorview/dto/OAuthAttributes.java b/app/src/main/java/mentorview/dto/OAuthAttributes.java
new file mode 100644
index 0000000..eff28c0
--- /dev/null
+++ b/app/src/main/java/mentorview/dto/OAuthAttributes.java
@@ -0,0 +1,56 @@
+package mentorview.dto;
+
+import lombok.Builder;
+import lombok.Getter;
+import mentorview.domain.Role;
+import mentorview.domain.User;
+
+import java.util.Map;
+
+@Getter
+public class OAuthAttributes {
+ private Map attributes;
+ private String nameAttributeKey;
+ private String name;
+ private String email;
+ private String picture;
+
+ @Builder
+ public OAuthAttributes(Map attributes,
+ String nameAttributeKey,
+ String name, String email, String picture) {
+ this.attributes = attributes;
+ this.nameAttributeKey = nameAttributeKey;
+ this.name = name;
+ this.email = email;
+ this.picture = picture;
+ }
+
+ public static OAuthAttributes of(
+ String registrationId, String userNameAttributeName,
+ Map attributes
+ ) {
+ return ofGoogle(userNameAttributeName, attributes);
+ }
+
+ private static OAuthAttributes ofGoogle(
+ String userNameAttributeName, Map attributes
+ ) {
+ return OAuthAttributes.builder()
+ .name((String) attributes.get("name"))
+ .email((String) attributes.get("email"))
+ .picture((String) attributes.get("picture"))
+ .attributes(attributes)
+ .nameAttributeKey(userNameAttributeName)
+ .build();
+ }
+
+ public User toEntity() {
+ return User.builder()
+ .name(name)
+ .email(email)
+ .picture(picture)
+ .role(Role.USER)
+ .build();
+ }
+}
diff --git a/app/src/main/java/mentorview/dto/SessionUser.java b/app/src/main/java/mentorview/dto/SessionUser.java
new file mode 100644
index 0000000..d84e3e8
--- /dev/null
+++ b/app/src/main/java/mentorview/dto/SessionUser.java
@@ -0,0 +1,20 @@
+package mentorview.dto;
+
+import lombok.Getter;
+import mentorview.domain.User;
+
+import java.io.Serializable;
+
+@Getter
+public class SessionUser implements Serializable {
+ private String name;
+ private String email;
+ private String picture;
+
+
+ public SessionUser(User user) {
+ this.name = name;
+ this.email = email;
+ this.picture = picture;
+ }
+}
diff --git a/app/src/main/java/mentorview/security/SecurityConfig.java b/app/src/main/java/mentorview/security/SecurityConfig.java
new file mode 100644
index 0000000..b1a64f2
--- /dev/null
+++ b/app/src/main/java/mentorview/security/SecurityConfig.java
@@ -0,0 +1,41 @@
+package mentorview.security;
+
+import mentorview.application.CustomOAuth2UserService;
+import mentorview.domain.Role;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+
+@Configuration
+@EnableGlobalMethodSecurity(prePostEnabled = true)
+public class SecurityConfig extends WebSecurityConfigurerAdapter {
+
+ private final CustomOAuth2UserService customOAuth2UserService;
+
+ public SecurityConfig(CustomOAuth2UserService customOAuth2UserService) {
+ this.customOAuth2UserService = customOAuth2UserService;
+ }
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ http
+ .csrf().disable()
+ .headers().frameOptions().disable()
+ .and()
+ .authorizeRequests()
+ .antMatchers("/", "/css/**", "/images/**", "/js/**", "/h2-console/**")
+ .permitAll()
+ .antMatchers("/api/v1/**").hasRole(Role.USER.name())
+ .anyRequest().authenticated()
+ .and()
+ .logout()
+ .logoutSuccessUrl("/")
+ .and()
+ .oauth2Login()
+ .userInfoEndpoint()
+ .userService(customOAuth2UserService);
+
+ super.configure(http);
+ }
+}
diff --git a/app/src/main/resources/templates/index.html b/app/src/main/resources/templates/index.html
new file mode 100644
index 0000000..2d35e5e
--- /dev/null
+++ b/app/src/main/resources/templates/index.html
@@ -0,0 +1,11 @@
+
+
+
+
+ mentorview
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/test/resources/application-oauth.yml b/app/src/test/resources/application-oauth.yml
index e69de29..0c851d1 100644
--- a/app/src/test/resources/application-oauth.yml
+++ b/app/src/test/resources/application-oauth.yml
@@ -0,0 +1,22 @@
+spring:
+ security:
+ oauth2:
+ client:
+ registration:
+ google:
+ client-id: 43640499580-o53kqsicb746knel82o6biu41bn9jqfu.apps.googleusercontent.com
+ client-secret: "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"
+ scope: profile, email
+ kakao:
+ client-id: 4aca4b4f37fbf39105fe0d0e43d2a609
+ redirect-uri: "https://localhost:8080/login/oauth2/code/kakao"
+ client-authentication-method: POST
+ authorization-grant-type: authorization_code
+ scope: profile_nickname, account_email
+ client-name: kakao
+ provider:
+ kakao:
+ authorization_uri: https://kauth.kakao.com/oauth/authorize
+ token_uri: https://kauth.kakao.com/oauth/token
+ user-info-uri: https://kapi.kakao.com/v2/user/me
+ user_name_attribute: id
\ No newline at end of file
diff --git a/app/src/test/resources/application.yml b/app/src/test/resources/application.yml
new file mode 100644
index 0000000..9695d78
--- /dev/null
+++ b/app/src/test/resources/application.yml
@@ -0,0 +1,3 @@
+spring:
+ profiles:
+ include: oauth
\ No newline at end of file