Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
9ba9622
feat#3/basic-entity
lemonson03 Apr 29, 2025
f141bee
feat#3/basic-repository
lemonson03 Apr 29, 2025
1a4c07a
feat#3/basic-repo2,report
lemonson03 Apr 29, 2025
894d6a8
Update src/main/java/org/groomUniv/meet/blinddate/entity/BlindDate.java
lemonson03 Apr 30, 2025
5734591
feat: (#3) Accept Comment
lemonson03 Apr 30, 2025
1ddd3c9
feat : (#3) resolve conflicts
lemonson03 Apr 30, 2025
808585a
feat: (#8) add spring security and jwt dependency
sunwoo1256 May 1, 2025
ff54eda
feat: (#8) add JwtToken.java DTO file
sunwoo1256 May 1, 2025
6144095
feat: (#8) add JwtTokenProvider
sunwoo1256 May 1, 2025
40b9c53
feat: (#8) add JwtAuthenticationFilter
sunwoo1256 May 1, 2025
a87b064
feat: (#8) add SecurityConfig
sunwoo1256 May 1, 2025
4ee30b3
feat: (#8) register PasswordEncoder and AuthenticationManager beans i…
sunwoo1256 May 4, 2025
8675b11
feat: (#8) add role
sunwoo1256 May 4, 2025
a01b363
feat: (#8) add MemberService Interface and MemberServiceImpl
sunwoo1256 May 4, 2025
36aef95
feat: (#8) add CustomUserDetails and CustomUserDetailsService
sunwoo1256 May 4, 2025
613c9b4
feat: (#8) add createUserDetails
sunwoo1256 May 9, 2025
7efa825
feat: (#8) add login
sunwoo1256 May 9, 2025
196ccfc
feat: (#8) add .csrf(AbstractHttpConfigurer::disable)
sunwoo1256 May 12, 2025
3913885
feat: (#8) add NO_AUTH_PATHS
sunwoo1256 May 12, 2025
aa6efbe
feat: (#8) add signUp
sunwoo1256 May 12, 2025
c68e927
refactor: (#8) generateToken refactoring
sunwoo1256 May 13, 2025
ca728ac
feat: (#8) add name in SignUp DTOs
sunwoo1256 May 13, 2025
2c80889
feat: (#8) add ErrorMessage
sunwoo1256 May 13, 2025
01811a9
style: (#8) remove unnecessary spaces in Meeting entity annotations
sunwoo1256 May 15, 2025
7a88dc8
feat: (#8) add MEMBER_NOT_FOUND exception
sunwoo1256 May 15, 2025
b43587f
feat: (#8) add handling for invalid token case
sunwoo1256 May 17, 2025
8b51cee
feat: (#8) modify token expiration configuration
sunwoo1256 May 17, 2025
73f0662
feat: (#8) add validation annotations to DTO
sunwoo1256 May 17, 2025
88c9401
refactor: (#8) rename method [toEntity] to [toMember]
sunwoo1256 May 17, 2025
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
10 changes: 10 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
withJavadocJar()
withSourcesJar()
}

configurations {
Expand Down Expand Up @@ -44,6 +46,14 @@ dependencies {
// 포트원
implementation 'com.github.iamport:iamport-rest-client-java:0.2.23'

// Spring Security
implementation 'org.springframework.boot:spring-boot-starter-security'

// JWT
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
implementation 'io.jsonwebtoken:jjwt-impl:0.11.5'
implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5'
Comment on lines +52 to +55
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

추후에 목적에 따라 gradle을 나눠볼 수도 있을 것 같아요!
아래 참고블로그 남겨놓을게요
gradl 분리

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

새로운 정보를 알게 되었습니다. 감사합니다!


}


Expand Down
2 changes: 2 additions & 0 deletions src/main/java/org/groomUniv/meet/MeetApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@SpringBootApplication
@EnableJpaAuditing // JPA Auditing 사용을 위한 설정
public class MeetApplication {

public static void main(String[] args) {
Expand Down
48 changes: 48 additions & 0 deletions src/main/java/org/groomUniv/meet/blinddate/entity/BlindDate.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package org.groomUniv.meet.blinddate.entity;

import jakarta.persistence.*;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.groomUniv.meet.common.entity.BaseEntity;
import org.groomUniv.meet.oauth.entity.Member;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Entity
@NoArgsConstructor
@Table(name = "blind_date")


public class BlindDate extends BaseEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long blindDateId;


@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member1_id")
private Member member1;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member2_id")
private Member member2;

// 채팅제한
private Long chatLimit;

//챗 엔티티 리스트로 관리하기
@OneToMany(mappedBy = "blindDate", cascade = CascadeType.ALL)
private List<BlindDateChat> chats = new ArrayList<>();
}
// JPA Auditing 설명
//@CreatedDate: 엔터티가 처음 생성된 날짜를 자동으로 기록합니다.
//
//@LastModifiedDate: 엔터티가 마지막으로 수정된 날짜를 자동으로 기록합니다.
//
//@CreatedBy: 엔터티를 처음 생성한 사용자를 자동으로 기록합니다.
//
//@LastModifiedBy: 엔터티를 마지막으로 수정한 사용자를 자동으로 기록합니다.
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.groomUniv.meet.blinddate.entity;


import jakarta.persistence.*;
import lombok.Data;
import org.groomUniv.meet.common.entity.BaseEntity;
import org.groomUniv.meet.oauth.entity.Member;
import org.springframework.data.annotation.CreatedDate;

import java.time.LocalDateTime;


@Entity
public class BlindDateChat extends BaseEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long blindDataChatId;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "blind_date_id")
private BlindDate blindDate;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
private Member sender;

private String message;

private Boolean readStatus;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.groomUniv.meet.blinddate.repository;

import org.groomUniv.meet.blinddate.entity.BlindDate;
import org.groomUniv.meet.blinddate.entity.BlindDateChat;
import org.groomUniv.meet.oauth.entity.Member;
import org.springframework.data.jpa.repository.JpaRepository;

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

public interface BlindDateChatRepository extends JpaRepository<BlindDateChat, Long> {

// 채팅 ID로 채팅 검색
Optional<BlindDateChat> findById(Long blindDataChatId);

// 특정 블라인드 채팅에 속한 모든 채팅 시간순으로
//List<BlindDateChat> findAllByBlindDateOrderByTimestampAsc(BlindDate blindDate);
// 읽지 않은 메세지들 가져오기
List<BlindDateChat> findAllByBlindDateAndReadStatusFalse(BlindDate blindDate);
// 특정 sender 기반으로 조회하기
List<BlindDateChat> findAllBySender(Member sender);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.groomUniv.meet.blinddate.repository;

import org.groomUniv.meet.blinddate.entity.BlindDate;
import org.groomUniv.meet.blinddate.entity.BlindDateChat;
import org.groomUniv.meet.oauth.entity.Member;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

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

public interface BlindDateRepository extends JpaRepository<BlindDate,Long> {
// 특정 두 멤버 기반의 챗 가져오기
Optional<BlindDate> findByMember1AndMember2(Member member1, Member member2);
// 특정 멤버중 하나만 있어도 가져오는 함수
@Query("SELECT b FROM BlindDate b WHERE b.member1 = :member OR b.member2 = :member")
List<BlindDate> findAllByMember(@Param("member") Member member);

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,9 @@ public ErrorMessage(ErrorType errorType) {
this.message = errorType.getMessage();
}

public ErrorMessage(String code, String message) {
this.code = code;
this.message = message;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
public enum GlobalErrorType implements ErrorType {

E500(HttpStatus.INTERNAL_SERVER_ERROR, "알 수 없는 내부 오류입니다."),
MEMBER_NOT_FOUND(HttpStatus.UNAUTHORIZED, "Member Not Found."), // "존재하지 않는 사용자" 401 Unauthorized
;

private final HttpStatus status;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,8 @@ public static ApiResponse<?> error(ErrorType error) {
return new ApiResponse<>(ResultType.ERROR, null, new ErrorMessage(error));
}

public static ApiResponse<?> error(String code, String message) {
return new ApiResponse<>(ResultType.ERROR, null, new ErrorMessage(code, message));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.groomUniv.meet.common.config;

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;

import java.util.Optional;

@Configuration
//@EnableJpaAuditing
// Spring이 CreatedBy에서 어떤 사용자를 넣을지 모르기 때문에 그걸 이 부분을 통해 찾아주려고 적는 Config 파일
public class JpaAuditingConfig {

@Bean
public AuditorAware<String> auditorProvider() {
// 실제 사용자 인증이 있는 경우엔 SecurityContext에서 꺼내서 여기 부분 변경해야함 basic으로 쓰면 안됨.
return () -> Optional.of("basic"); // 기본값
}
}
// Spring Security 사용할 때의 SecurityContextHolder 사용법
// return () -> Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication())
// .filter(Authentication::isAuthenticated)
// .map(Authentication::getName);
60 changes: 60 additions & 0 deletions src/main/java/org/groomUniv/meet/common/config/SecurityConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package org.groomUniv.meet.common.config;

import lombok.RequiredArgsConstructor;
import org.groomUniv.meet.common.security.CustomUserDetailsService;
import org.groomUniv.meet.common.security.JwtAuthenticationFilter;
import org.groomUniv.meet.common.security.JwtTokenProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import static jakarta.servlet.DispatcherType.ERROR;

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final JwtTokenProvider jwtTokenProvider;
private final CustomUserDetailsService userDetailsService;

@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}


@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.csrf(AbstractHttpConfigurer::disable)
.httpBasic(AbstractHttpConfigurer::disable) // HTTP 기본 인증 방식 비활성화(브라우저 팝업창 로그인 방지)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // 세션을 사용하지 않도록 설정(JWT는 Stateless 방식이므로 세션 필요 없음)
.authorizeHttpRequests(auth -> auth // 요청에 대한 인가(Authorization) 설정
.dispatcherTypeMatchers(ERROR).permitAll()
.requestMatchers("/api/member/signup", "/api/member/login").permitAll() // 로그인과 회원가입은 인증 없이 접근 가능
// requestMatchers를 추가해 API를 통한 필터링 가능
.anyRequest().authenticated() // 그 외 모든 요청은 인증 필요
)
//UsernamePasswordAuthenticationFilter 전에 JwtAuthenticationFilter를 실행하겠다
.addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider), UsernamePasswordAuthenticationFilter.class)
.build();

}

@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
29 changes: 29 additions & 0 deletions src/main/java/org/groomUniv/meet/common/entity/BaseEntity.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.groomUniv.meet.common.entity;

import jakarta.persistence.Column;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.MappedSuperclass;
import lombok.Getter;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import java.time.LocalDateTime;
@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class) // JPA Auditing 활성화
// 생성일자, 수정일자, 시간 관리에 대한 것은 공통적인 부분이라 BaseEntity를 통해 관리하고 상속받아서 사용하기 위한 용도
public class BaseEntity {
// create 날짜 언제인지
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdAt;

// 누가 create 했는지
@CreatedBy
@Column(updatable = false)
private String createdBy;



}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package org.groomUniv.meet.common.security;

import org.groomUniv.meet.oauth.entity.Member;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

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

public class CustomUserDetails implements UserDetails {
private final Member member;

public CustomUserDetails(Member member) {
this.member = member;
}


@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return member.getRoles().stream()
.map(role -> new SimpleGrantedAuthority(role.name()))
.collect(Collectors.toList());
}

@Override
public String getPassword() {
return member.getPassword();
}

@Override
public String getUsername() {
return member.getEmail();
}

@Override
public boolean isAccountNonExpired() {
return true;
}

@Override
public boolean isAccountNonLocked() {
return true;
}

@Override
public boolean isCredentialsNonExpired() {
return true;
}

@Override
public boolean isEnabled() {
return true;
}
}
Loading