Skip to content
Merged
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
11 changes: 11 additions & 0 deletions .github/workflows/dev_deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
name: UMC Dev CI/CD // 여러분들 맘대로 이름 지으세요

on:
pull_request:
types: [closed]
workflow_dispatch: # (2).수동 실행도 가능하도록

jobs:
build:
runs-on: ubuntu-latest # (3).OS환경
if: github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'develop'
11 changes: 11 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,17 @@ dependencies {
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
implementation 'org.springframework.boot:spring-boot-starter-validation'

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

implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6:3.1.1.RELEASE'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'

implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'

implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'

}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package sw_workbook.spring.Repository;

import org.springframework.data.jpa.repository.JpaRepository;
import sw_workbook.spring.domain.FoodCategory;

public interface FoodCategoryRepository extends JpaRepository<FoodCategory,Long> {
}
18 changes: 18 additions & 0 deletions src/main/java/sw_workbook/spring/Repository/MemberRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package sw_workbook.spring.Repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import sw_workbook.spring.domain.Member;
import sw_workbook.spring.domain.enums.MemberStatus;

import java.util.List;
import java.util.Optional;

public interface MemberRepository extends JpaRepository<Member, Long> {

@Query("SELECT m FROM Member m WHERE m.name = :name AND m.status = :status")
List<Member> findByNameAndStatus(@Param("name") String name, @Param("status") MemberStatus status);

Optional<Member> findByEmail(String email);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package sw_workbook.spring.apiPayload.code.exception.handler;


import sw_workbook.spring.apiPayload.code.BaseErrorCode;
import sw_workbook.spring.apiPayload.code.exception.GeneralException;

public class FoodCategoryHandler extends GeneralException {
public FoodCategoryHandler(BaseErrorCode errorCode) {
super(errorCode);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ public enum ErrorStatus implements BaseErrorCode {
MEMBER_NOT_FOUND(HttpStatus.BAD_REQUEST, "MEMBER4001", "사용자가 없습니다."),
NICKNAME_NOT_EXIST(HttpStatus.BAD_REQUEST, "MEMBER4002", "닉네임은 필수 입니다."),

// FoodCategory Error
FOOD_CATEGORY_NOT_FOUND(HttpStatus.NOT_FOUND, "FOOD_CATEGORY4001", "음식 카테고리가 없습니다."),

// Article Error
ARTICLE_NOT_FOUND(HttpStatus.NOT_FOUND, "ARTICLE4001", "게시글이 없습니다.");

Expand All @@ -46,6 +49,7 @@ public ErrorReasonDTO getReasonHttpStatus() {
.code(code)
.isSuccess(false)
.httpStatus(httpStatus)
.build();
.build()
;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package sw_workbook.spring.config;

import lombok.RequiredArgsConstructor;
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.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;
import sw_workbook.spring.Repository.MemberRepository;
import sw_workbook.spring.domain.Member;
import sw_workbook.spring.domain.enums.Gender;
import sw_workbook.spring.domain.enums.Role;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

@Service
@RequiredArgsConstructor
public class CustomOAuth2UserService extends DefaultOAuth2UserService {//DefaultOAuth2UserService<< OAuth2 로그인시 사용자 정보 가져옴
private final MemberRepository memberRepository;
private final PasswordEncoder passwordEncoder;

@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException{

OAuth2User oAuth2User = super.loadUser(userRequest);//loadUser(userRequest): OAuth2 로그인 요청이 들어오면 실행되는 메서드입니다.
//카톡에서 제공하는 사용자 정보 오어스2 객체로 받아옴

Map<String, Object> attributes = oAuth2User.getAttributes();//attribute는 사용자의 기본정보 포함
Map<String, Object> properties = (Map<String, Object>) attributes.get("properties");
//Auth2 로그인에서 반환된 사용자 정보 중 특정 서브 정보를 추출, properties라는 속성 내에 추가적인 사용자 정보를 저장

String nickname = (String) properties.get("nickname");
String email = nickname + "@kakao.com"; // 임시 이메일 생성

// 사용자 정보 저장 또는 업데이트
Member member = saveOrUpdateUser(email, nickname);

// 이메일을 Principal로 사용하기 위해 attributes 수정
Map<String, Object> modifiedAttributes = new HashMap<>(attributes);
modifiedAttributes.put("email", email);

return new DefaultOAuth2User(
oAuth2User.getAuthorities(),
modifiedAttributes,
"email" // email Principal로 설정
);
}

private Member saveOrUpdateUser(String email, String nickname) {
Member member = memberRepository.findByEmail(email)
.orElse(Member.builder()
.email(email)
.name(nickname)
.password(passwordEncoder.encode("OAUTH_USER_" + UUID.randomUUID()))
.gender(Gender.NONE) // 기본값 설정
.address("소셜로그인") // 기본값 설정
.specAddress("소셜로그인") // 기본값 설정
.role(Role.USER)
.build());

return memberRepository.save(member);
}



}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package sw_workbook.spring.config;


import lombok.RequiredArgsConstructor;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import sw_workbook.spring.Repository.MemberRepository;
import sw_workbook.spring.domain.Member;

@Service
@RequiredArgsConstructor
public class CustomUserDetailsService implements UserDetailsService {

private final MemberRepository memberRepository;

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Member member = memberRepository.findByEmail(username)
.orElseThrow(() -> new UsernameNotFoundException("해당 이메일을 가진 유저가 존재하지 않습니다: " + username));

return org.springframework.security.core.userdetails.User
.withUsername(member.getEmail())
.password(member.getPassword())
.roles(member.getRole().name())
.build();
}
}
47 changes: 47 additions & 0 deletions src/main/java/sw_workbook/spring/config/SecurityConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package sw_workbook.spring.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@EnableWebSecurity
@Configuration
public class SecurityConfig {

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((requests) -> requests
.requestMatchers("/", "/home", "/signup", "/members/signup", "api/hello","/css/**").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.formLogin((form) -> form
.loginPage("/login")
.defaultSuccessUrl("/home", true)
.permitAll()
)
.logout((logout) -> logout
.logoutUrl("/logout")
.logoutSuccessUrl("/login?logout")
.permitAll()
)
.oauth2Login(oauth2 -> oauth2
.loginPage("/login")
.defaultSuccessUrl("/home",true)
.permitAll()
);


return http.build();
}

@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
19 changes: 19 additions & 0 deletions src/main/java/sw_workbook/spring/config/WebConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package sw_workbook.spring.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

@Override
public void addCorsMappings(CorsRegistry registry){
registry.addMapping("/**")
.allowedOrigins("http://localhost:5173")
.allowedMethods("GET","POST","PUT","DELETE")
.allowedHeaders("*")
.allowCredentials(true);
}

}
46 changes: 46 additions & 0 deletions src/main/java/sw_workbook/spring/converter/MemberConverter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package sw_workbook.spring.converter;

import sw_workbook.spring.domain.Member;
import sw_workbook.spring.domain.enums.Gender;
import sw_workbook.spring.web.dto.MemberRequestDTO;
import sw_workbook.spring.web.dto.MemberResponseDTO;

import java.time.LocalDateTime;
import java.util.ArrayList;

public class MemberConverter {

public static MemberResponseDTO.JoinResultDTO toJoinResultDTO(Member member){
return MemberResponseDTO.JoinResultDTO.builder()
.memberId(member.getId())
.createdAt(LocalDateTime.now())
.build();
}
public static Member toMember(MemberRequestDTO.JoinDto request){

Gender gender = null;

switch (request.getGender()){
case 1:
gender = Gender.MALE;
break;
case 2:
gender = Gender.FEMALE;
break;
case 3:
gender = Gender.NONE;
break;
}

return Member.builder()
.name(request.getName())
.email(request.getEmail()) // 추가된 코드
.password(request.getPassword()) // 추가된 코드
.gender(gender)
.address(request.getAddress())
.specAddress(request.getSpecAddress())
.role(request.getRole()) // 추가된 코드
.memberPreferList(new ArrayList<>())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package sw_workbook.spring.converter;

import sw_workbook.spring.domain.FoodCategory;
import sw_workbook.spring.domain.mapping.MemberPrefer;

import java.util.List;
import java.util.stream.Collectors;

public class MemberPreferConverter {

public static List<MemberPrefer> toMemberPreferList(List<FoodCategory> foodCategoryList){

return foodCategoryList.stream()
.map(foodCategory ->
MemberPrefer.builder()
.foodCategory(foodCategory)
.build()
).collect(Collectors.toList());
}
}
15 changes: 14 additions & 1 deletion src/main/java/sw_workbook/spring/domain/Member.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import sw_workbook.spring.domain.common.BaseEntity;
import sw_workbook.spring.domain.enums.Gender;
import sw_workbook.spring.domain.enums.MemberStatus;
import sw_workbook.spring.domain.enums.Role;
import sw_workbook.spring.domain.enums.SocialType;
import sw_workbook.spring.domain.mapping.MemberAgree;
import sw_workbook.spring.domain.mapping.MemberMission;
Expand Down Expand Up @@ -53,9 +54,19 @@ public class Member extends BaseEntity {

private LocalDate inactiveDate;

// @Column(nullable = false, length = 50)
@Column(unique = true, nullable = false)
private String email;

@Column(nullable = false)
private String password;

@Enumerated(EnumType.STRING)
private Role role;

public void encodePassword(String password){
this.password = password;
}

@ColumnDefault("0")
private Integer point;

Expand All @@ -70,4 +81,6 @@ public class Member extends BaseEntity {

@OneToMany(mappedBy = "member", cascade = CascadeType.ALL)
private List<MemberMission> memberMissionList = new ArrayList<>();


}
5 changes: 5 additions & 0 deletions src/main/java/sw_workbook/spring/domain/enums/Role.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package sw_workbook.spring.domain.enums;

public enum Role {
ADMIN, USER
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package sw_workbook.spring.service.memberservice;

import sw_workbook.spring.domain.Member;
import sw_workbook.spring.web.dto.MemberRequestDTO;

public interface MemberCommandService {

Member joinMember(MemberRequestDTO.JoinDto request);
}
Loading