Skip to content

Commit

Permalink
Merge pull request #29 from jwpark1211/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
jwpark1211 authored May 24, 2024
2 parents 13b58da + bd1f4b2 commit a7b8561
Show file tree
Hide file tree
Showing 15 changed files with 399 additions and 29 deletions.
5 changes: 5 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-webflux' //webClient
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2' //swagger
implementation 'org.springframework.boot:spring-boot-starter-data-redis' //redis
Expand All @@ -37,6 +38,10 @@ dependencies {
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.mockito:mockito-core'
testImplementation 'org.mockito:mockito-junit-jupiter'
//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'
}

tasks.named('test') {
Expand Down
56 changes: 56 additions & 0 deletions src/main/java/capstone/bookitty/common/CustomUserDetails.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package capstone.bookitty.common;

import capstone.bookitty.domain.entity.Member;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.ArrayList;
import java.util.Collection;

public class CustomUserDetails implements UserDetails {
private final Member member;

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

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> authorities = new ArrayList<>();
String roles = member.getAuthority().toString();
for (String role : roles.split(",")) {
authorities.add(() -> role);
}
return authorities;
}

@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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package capstone.bookitty.common;

import capstone.bookitty.domain.entity.Member;
import capstone.bookitty.domain.repository.MemberRepository;
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 org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Transactional
public class CustomUserDetailsService implements UserDetailsService {
private final MemberRepository memberRepository;

@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
Member member = memberRepository.findByEmail(email)
.orElseThrow(() -> new UsernameNotFoundException("User not found with email: " + email));
return new CustomUserDetails(member);
}
}
66 changes: 66 additions & 0 deletions src/main/java/capstone/bookitty/common/SecurityConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package capstone.bookitty.common;

import capstone.bookitty.jwt.JwtFilter;
import capstone.bookitty.jwt.JwtProperties;
import capstone.bookitty.jwt.JwtTokenProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
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.annotation.web.configurers.HeadersConfigurer;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher;

@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final JwtTokenProvider jwtTokenProvider;
private final JwtProperties jwtProperties;

public SecurityConfig(JwtTokenProvider jwtTokenProvider, JwtProperties jwtProperties) {
this.jwtTokenProvider = jwtTokenProvider;
this.jwtProperties = jwtProperties;
}

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
.httpBasic(AbstractHttpConfigurer::disable)
.headers((headersConfig) ->
headersConfig.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable))
.authorizeHttpRequests((authorizeRequests) ->
authorizeRequests
.requestMatchers(antMatcher("/"),
antMatcher("/members/test"),
antMatcher("/members/login"),
antMatcher("/members/new"),
antMatcher("/members/email/**")).permitAll()
//.requestMatchers(antMatcher("")).authenticated()
.anyRequest().permitAll())
.formLogin(AbstractHttpConfigurer::disable)
.logout((logout) ->
logout
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.invalidateHttpSession(true)
.logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler(HttpStatus.OK))
.deleteCookies("JSESSIONID")
)
.addFilterBefore(new JwtFilter(jwtTokenProvider, jwtProperties), UsernamePasswordAuthenticationFilter.class);
return http.build();
}

@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import capstone.bookitty.domain.dto.ResponseType.BasicResponse;
import capstone.bookitty.domain.dto.ResponseType.ResponseCounter;
import capstone.bookitty.domain.dto.ResponseType.ResponseString;
import capstone.bookitty.domain.dto.TokenResponseDTO;
import capstone.bookitty.domain.service.MemberService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
Expand Down Expand Up @@ -58,9 +59,8 @@ public ResponseEntity<? extends BasicResponse> isEmailUnique(
public ResponseEntity<? extends BasicResponse> login(
@RequestBody @Valid MemberLoginRequest request
){
memberService.login(request);
return ResponseEntity.ok()
.body(new ResponseString("login success!"));
.body(new ResponseCounter<TokenResponseDTO>(memberService.login(request)));
}

@Operation(summary = "id로 회원 조회")
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/capstone/bookitty/domain/dto/MemberDTO.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.security.crypto.password.PasswordEncoder;

import java.time.LocalDate;

public class MemberDTO {
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class MemberSaveRequest {
@NotBlank(message = "Email is a required entry value.")
Expand All @@ -34,6 +36,7 @@ public static class MemberSaveRequest {
private String name;
}


@Data
public static class MemberLoginRequest {
@NotBlank(message = "Email is a required entry value.")
Expand Down
19 changes: 19 additions & 0 deletions src/main/java/capstone/bookitty/domain/dto/TokenResponseDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package capstone.bookitty.domain.dto;

import capstone.bookitty.jwt.JwtToken;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;

@Data
@Getter
@Builder
public class TokenResponseDTO {
private Long idx;
private JwtToken jwtToken;

public TokenResponseDTO(Long idx, JwtToken jwtToken) {
this.idx = idx;
this.jwtToken = jwtToken;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
public enum Grade {
ADMIN("관리자"),
USER("사용자")
;

private final String grade;
public enum Authority {
ROLE_ADMIN, ROLE_USER
}
4 changes: 2 additions & 2 deletions src/main/java/capstone/bookitty/domain/entity/Member.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public class Member {
private Gender gender;

@Enumerated(EnumType.STRING)
private Grade grade;
private Authority authority;

@Builder
public Member(String name, String email, String password, String profileImg,
Expand All @@ -45,7 +45,7 @@ public Member(String name, String email, String password, String profileImg,
this.birthDate = birthDate;
this.createdAt = LocalDateTime.now();
this.profileImg = "https://bookitty-bucket.s3.ap-northeast-2.amazonaws.com/Jiji.jpeg";
this.grade = Grade.USER;
this.authority = Authority.ROLE_USER;
}

public void updateProfile(String profileImg){
Expand Down
22 changes: 18 additions & 4 deletions src/main/java/capstone/bookitty/domain/service/MemberService.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
package capstone.bookitty.domain.service;

import capstone.bookitty.domain.dto.TokenResponseDTO;
import capstone.bookitty.domain.entity.Member;
import capstone.bookitty.domain.repository.MemberRepository;
import capstone.bookitty.jwt.JwtToken;
import capstone.bookitty.jwt.JwtTokenProvider;
import jakarta.persistence.EntityNotFoundException;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartException;
Expand All @@ -21,6 +28,9 @@
@RequiredArgsConstructor
public class MemberService {
private final MemberRepository memberRepository;
private final PasswordEncoder passwordEncoder;
private final AuthenticationManagerBuilder authenticationManagerBuilder;
private final JwtTokenProvider jwtTokenProvider;
private final S3Service s3Service;

@Transactional
Expand All @@ -31,7 +41,7 @@ public IdResponse saveMember(MemberSaveRequest request) {
Member member = Member.builder()
.email(request.getEmail())
.name(request.getName())
.password(request.getPassword())
.password(passwordEncoder.encode(request.getPassword()))
.birthDate(request.getBirthdate())
.gender(request.getGender())
.build();
Expand All @@ -45,11 +55,15 @@ public BoolResponse isEmailUnique(String email) {
}

@Transactional
public void login(MemberLoginRequest request) {
public TokenResponseDTO login(MemberLoginRequest request) {
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(request.getEmail(), request.getPassword());
Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);

JwtToken jwtToken = jwtTokenProvider.generateTokenDto(authentication);
Member member = memberRepository.findByEmail(request.getEmail())
.orElseThrow(()-> new EntityNotFoundException("Member not found."));
if(!member.getPassword().equals(request.getPassword()))
throw new IllegalArgumentException("Invalid login credentials.");
return new TokenResponseDTO(member.getId(), jwtToken);
}

public MemberInfoResponse getMemberInfoWithId(Long memberId) {
Expand Down
32 changes: 17 additions & 15 deletions src/main/java/capstone/bookitty/initDB.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import jakarta.persistence.EntityManager;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

import java.time.LocalDate;
Expand All @@ -28,24 +29,25 @@ public void init(){
static class InitService{

private final EntityManager em;
private final PasswordEncoder pwEncoder;

public void dbInit(){
List<Member> members = Arrays.asList(
new Member("김민준", "[email protected]", "Wo1902!si", null, Gender.MALE, LocalDate.of(1992, 7, 21)),
new Member("이서현", "[email protected]", "Wo1902!si", null, Gender.FEMALE, LocalDate.of(2010, 12, 8)),
new Member("서진호", "[email protected]", "Wo1902!si", null, Gender.MALE, LocalDate.of(1971, 8, 28)),
new Member("이선희", "[email protected]", "Wo1902!si", null, Gender.FEMALE, LocalDate.of(1969, 2, 5)),
new Member("신준서", "[email protected]", "Wo1902!si", null, Gender.MALE, LocalDate.of(2005, 8, 20)),
new Member("문다연", "[email protected]", "Wo1902!si", null, Gender.FEMALE, LocalDate.of(1999, 1, 14)),
new Member("윤동현", "[email protected]", "Wo1902!si", null, Gender.MALE, LocalDate.of(1989, 7, 3)),
new Member("송지은", "[email protected]", "Wo1902!si", null, Gender.FEMALE, LocalDate.of(1995, 3, 18)),
new Member("김준서", "[email protected]", "Wo1902!si", null, Gender.MALE, LocalDate.of(2001, 12, 11)),
new Member("임지민", "[email protected]", "Wo1902!si", null, Gender.FEMALE, LocalDate.of(2009, 3, 14)),
new Member("안지성", "[email protected]", "Wo1902!si", null, Gender.MALE, LocalDate.of(2002, 8, 8)),
new Member("황예린", "[email protected]", "Wo1902!si", null, Gender.FEMALE, LocalDate.of(1991, 11, 28)),
new Member("송현우", "[email protected]", "Wo1902!si", null, Gender.MALE, LocalDate.of(1999, 9, 8)),
new Member("정우진", "[email protected]", "Wo1902!si", null, Gender.MALE, LocalDate.of(2004, 1, 13)),
new Member("서은우", "[email protected]", "Wo1902!si", null, Gender.MALE, LocalDate.of(2008, 2, 20))
new Member("김민준", "[email protected]", pwEncoder.encode("Wo1902!si1"), null, Gender.MALE, LocalDate.of(1992, 7, 21)),
new Member("이서현", "[email protected]", pwEncoder.encode("Wo1902!si2"), null, Gender.FEMALE, LocalDate.of(2010, 12, 8)),
new Member("서진호", "[email protected]", pwEncoder.encode("Wo1902!si3"), null, Gender.MALE, LocalDate.of(1971, 8, 28)),
new Member("이선희", "[email protected]", pwEncoder.encode("Wo1902!si4"), null, Gender.FEMALE, LocalDate.of(1969, 2, 5)),
new Member("신준서", "[email protected]", pwEncoder.encode("Wo1902!si5"), null, Gender.MALE, LocalDate.of(2005, 8, 20)),
new Member("문다연", "[email protected]", pwEncoder.encode("Wo1902!si6"), null, Gender.FEMALE, LocalDate.of(1999, 1, 14)),
new Member("윤동현", "[email protected]", pwEncoder.encode("Wo1902!si7"), null, Gender.MALE, LocalDate.of(1989, 7, 3)),
new Member("송지은", "[email protected]", pwEncoder.encode("Wo1902!si8"), null, Gender.FEMALE, LocalDate.of(1995, 3, 18)),
new Member("김준서", "[email protected]", pwEncoder.encode("Wo1902!si9"), null, Gender.MALE, LocalDate.of(2001, 12, 11)),
new Member("임지민", "[email protected]", pwEncoder.encode("Wo1902!si10"), null, Gender.FEMALE, LocalDate.of(2009, 3, 14)),
new Member("안지성", "[email protected]", pwEncoder.encode("Wo1902!si11"), null, Gender.MALE, LocalDate.of(2002, 8, 8)),
new Member("황예린", "[email protected]", pwEncoder.encode("Wo1902!si12"), null, Gender.FEMALE, LocalDate.of(1991, 11, 28)),
new Member("송현우", "[email protected]", pwEncoder.encode("Wo1902!si13"), null, Gender.MALE, LocalDate.of(1999, 9, 8)),
new Member("정우진", "[email protected]", pwEncoder.encode("Wo1902!si14"), null, Gender.MALE, LocalDate.of(2004, 1, 13)),
new Member("서은우", "[email protected]", pwEncoder.encode("Wo1902!si15"), null, Gender.MALE, LocalDate.of(2008, 2, 20))
);

for (Member member : members) {
Expand Down
Loading

0 comments on commit a7b8561

Please sign in to comment.