Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
# Ignore Gradle build output directory
build/

# application config
application-oauth.yml

### intelliJ
.idea/

Expand Down
9 changes: 9 additions & 0 deletions .idea/compiler.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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'
}
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/java/mentorview/App.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
*/
package mentorview;

import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class App {
public String getGreeting() {
return "Hello World!";
Expand Down
7 changes: 5 additions & 2 deletions app/src/main/java/mentorview/Infra/UserRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
import mentorview.domain.User;
import org.springframework.data.jpa.repository.JpaRepository;

public interface SignUpRepository extends JpaRepository<User, String> {
User findByEmail(String email);
import java.util.Optional;

public interface UserRepository extends JpaRepository<User, Long> {
// email을 통해 가입된 회원 인지를 확인
Optional<User> findByEmail(String email);
}
Original file line number Diff line number Diff line change
@@ -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<OAuth2UserRequest, OAuth2User> {
private final UserRepository userRepository;
private final HttpSession httpSession;

@Override
public OAuth2User loadUser(OAuth2UserRequest oAuth2UserRequest) throws OAuth2AuthenticationException {
OAuth2UserService<OAuth2UserRequest, OAuth2User> 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);
}
}
8 changes: 8 additions & 0 deletions app/src/main/java/mentorview/application/UserService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package mentorview.application;

import org.springframework.stereotype.Service;

@Service
public class UserService {

}
21 changes: 21 additions & 0 deletions app/src/main/java/mentorview/controllers/LoginController.java
Original file line number Diff line number Diff line change
@@ -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
}
36 changes: 33 additions & 3 deletions app/src/main/java/mentorview/domain/BaseTimeEntity.java
Original file line number Diff line number Diff line change
@@ -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);
}
15 changes: 14 additions & 1 deletion app/src/main/java/mentorview/domain/Role.java
Original file line number Diff line number Diff line change
@@ -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;
}
15 changes: 11 additions & 4 deletions app/src/main/java/mentorview/domain/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -47,4 +52,6 @@ public User update(String name, Role role) {
public String getRoleKey() {
return this.role.getKey();
}


}
56 changes: 56 additions & 0 deletions app/src/main/java/mentorview/dto/OAuthAttributes.java
Original file line number Diff line number Diff line change
@@ -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<String, Object> attributes;
private String nameAttributeKey;
private String name;
private String email;
private String picture;

@Builder
public OAuthAttributes(Map<String, Object> 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<String, Object> attributes
) {
return ofGoogle(userNameAttributeName, attributes);
}

private static OAuthAttributes ofGoogle(
String userNameAttributeName, Map<String, Object> 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();
}
}
20 changes: 20 additions & 0 deletions app/src/main/java/mentorview/dto/SessionUser.java
Original file line number Diff line number Diff line change
@@ -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;
}
}
41 changes: 41 additions & 0 deletions app/src/main/java/mentorview/security/SecurityConfig.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
11 changes: 11 additions & 0 deletions app/src/main/resources/templates/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>mentorview</title>
</head>
<body>
<meta name="google-signin-client_id" content="43640499580-o53kqsicb746knel82o6biu41bn9jqfu.apps.googleusercontent.com">
<div class="g-signin2" data-onsuccess="onSignIn"></div>
</body>
</html>
Loading