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
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ public class QPhotographer extends EntityPathBase<Photographer> {

public final NumberPath<Long> id = createNumber("id", Long.class);

public final BooleanPath isFirst = createBoolean("isFirst");

public final EnumPath<MyUniv> myUniv = createEnum("myUniv", MyUniv.class);

public final StringPath password = createString("password");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,24 @@


import ceos.phototoground.domain.customer.dto.CustomUserDetails;
import ceos.phototoground.domain.customer.dto.PasswordUpdateDto;
import ceos.phototoground.domain.photographer.dto.PhotographerBottomDTO;
import ceos.phototoground.domain.photographer.dto.PhotographerIdDTO;
import ceos.phototoground.domain.photographer.dto.PhotographerIntroDTO;
import ceos.phototoground.domain.photographer.dto.PhotographerListDTO;
import ceos.phototoground.domain.photographer.dto.PhotographerResponseDTO;
import ceos.phototoground.domain.photographer.dto.PhotographerSearchListDTO;
import ceos.phototoground.domain.photographer.service.PhotographerService;
import ceos.phototoground.global.dto.SuccessResponseDto;
import jakarta.validation.Valid;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
Expand All @@ -26,6 +31,17 @@ public class PhotographerController {

private final PhotographerService photographerService;

// 작가 비밀번호 수정
@PatchMapping("/password")
public ResponseEntity<SuccessResponseDto<String>> updatePassword(@AuthenticationPrincipal CustomUserDetails userDetails,
@Valid @RequestBody PasswordUpdateDto passwordUpdateDto) {
Long photographerId = userDetails.getPhotographer().getId();

photographerService.updatePassword(photographerId, passwordUpdateDto);

return ResponseEntity.ok(SuccessResponseDto.successMessage("비밀번호가 성공적으로 변경되었습니다."));
}

// 작가 리스트 조회 (필터링 포함)
@GetMapping
public ResponseEntity<PhotographerListDTO> getPhotographerList(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ public class Photographer extends BaseTimeEntity {
@NotNull
private int bornYear;

// 첫 로그인 여부 확인
@Builder.Default
private boolean isFirst = true;

@NotNull
@Enumerated(EnumType.STRING)
private MyUniv myUniv; //작가의 대학교
Expand All @@ -58,4 +62,16 @@ public class Photographer extends BaseTimeEntity {
@OneToOne(mappedBy = "photographer", cascade = CascadeType.ALL, orphanRemoval = true)
private PhotoProfile photoProfile; //양방향 연관관계 설정

// 첫 로그인 상태를 false로 변경하는 메서드
public void markAsLoggedIn() {
if (this.isFirst) {
this.isFirst = false;
}
}

// 비밀번호 수정 메서드
public void updatePassword(String password) {
this.password = password;
System.out.println("업데이트 완료");
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package ceos.phototoground.domain.photographer.service;

import ceos.phototoground.domain.customer.dto.CustomUserDetails;
import ceos.phototoground.domain.customer.dto.PasswordUpdateDto;
import ceos.phototoground.domain.customer.entity.Customer;
import ceos.phototoground.domain.customer.entity.UserRole;
import ceos.phototoground.domain.follow.entity.Follow;
import ceos.phototoground.domain.follow.service.FollowService;
Expand Down Expand Up @@ -30,6 +32,7 @@
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand All @@ -44,14 +47,31 @@ public class PhotographerService {
private final PhotoStyleService photoStyleService;
private final PostService postService;
private final FollowService followService;
private final BCryptPasswordEncoder bCryptPasswordEncoder;


@Transactional
public Photographer findPhotographerById(Long photographerId) {
return photographerRepository.findById(photographerId)
.orElseThrow(() -> new CustomException(ErrorCode.PHOTOGRAPHER_NOT_FOUND));
}

// 작가 비밀번호 수정
@Transactional
public void updatePassword(Long photographerId, PasswordUpdateDto passwordUpdateDto) {
// 고객 엔티티 조회
Photographer photographer = photographerRepository.findById(photographerId)
.orElseThrow(() -> new CustomException(ErrorCode.PHOTOGRAPHER_NOT_FOUND));

// 새 비밀번호 검증
validateNewPassword(passwordUpdateDto.getPassword(), photographer.getPassword());

// 새 비밀번호 암호화 후 저장
String encryptedPassword = bCryptPasswordEncoder.encode(passwordUpdateDto.getPassword());
photographer.updatePassword(encryptedPassword);

// 작가 정보 저장
photographerRepository.save(photographer);
}

public PhotographerListDTO getPhotographerList(Long cursor, int size, String univ, String gender) {

Expand Down Expand Up @@ -206,6 +226,33 @@ public Photographer findById(Long photographerId) {
.orElseThrow(() -> new CustomException(ErrorCode.PHOTOGRAPHER_NOT_FOUND));
}

// 비밀번호 유효성 검증
private void validateNewPassword(String newPassword, String existingPassword) {
if (newPassword == null || newPassword.length() < 8 || newPassword.length() > 12) {
throw new CustomException(ErrorCode.INVALID_PASSWORD, "새 비밀번호는 8자 이상, 12자 이하로 설정해야 합니다.");
}

StringBuilder errorMessages = new StringBuilder();

if (!newPassword.matches(".*[a-zA-Z].*")) {
errorMessages.append("영문자가 포함되어야 합니다. ");
}
if (!newPassword.matches(".*\\d.*")) {
errorMessages.append("숫자가 포함되어야 합니다. ");
}
if (!newPassword.matches(".*[\\$!@%&\\*].*")) {
errorMessages.append("특수문자가 포함되어야 합니다.");
}

if (errorMessages.length() > 0) {
throw new CustomException(ErrorCode.INVALID_PASSWORD, errorMessages.toString().trim());
}

if (bCryptPasswordEncoder.matches(newPassword, existingPassword)) {
throw new CustomException(ErrorCode.REUSED_PASSWORD);
}
}

public PhotographerIdDTO getMyId(CustomUserDetails customUserDetails) {
Long myId = customUserDetails.getPhotographer().getId();
return PhotographerIdDTO.from(myId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

// 로그인 필터 설정 (/login)
http.addFilterAt(
new LoginFilter(authenticationManager(authenticationConfiguration), jwtUtil, refreshRepository),
new LoginFilter(authenticationManager(authenticationConfiguration), jwtUtil, refreshRepository, photographerRepository),
UsernamePasswordAuthenticationFilter.class);

// 로그아웃 필터 설정 (/logout)
Expand Down
19 changes: 19 additions & 0 deletions src/main/java/ceos/phototoground/global/jwt/LoginFilter.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package ceos.phototoground.global.jwt;

import ceos.phototoground.domain.customer.dto.LoginRequestDto;
import ceos.phototoground.domain.photographer.repository.PhotographerRepository;
import ceos.phototoground.global.dto.ErrorResponseDto;
import ceos.phototoground.global.dto.SuccessResponseDto;
import ceos.phototoground.global.entity.RefreshEntity;
import ceos.phototoground.global.entity.RefreshRepository;
import ceos.phototoground.global.util.SpringContext;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.FilterChain;
import jakarta.servlet.http.Cookie;
Expand All @@ -30,6 +32,7 @@ public class LoginFilter extends UsernamePasswordAuthenticationFilter {
private final AuthenticationManager authenticationManager;
private final JWTUtil jwtUtil;
private final RefreshRepository refreshRepository;
private final PhotographerRepository photographerRepository;

@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
Expand Down Expand Up @@ -88,6 +91,22 @@ protected void successfulAuthentication(HttpServletRequest request, HttpServletR
userData.put("username", username);
userData.put("role", role); // 단일 권한 추가

// 첫 로그인 여부 확인 및 응답 추가 (Photographer만 해당)
if (role.equals("ROLE_PHOTOGRAPHER")) { // Photographer 역할인 경우
PhotographerRepository photographerRepository = // Repository 주입
SpringContext.getBean(PhotographerRepository.class);

photographerRepository.findByEmail(username).ifPresent(photographer -> {
userData.put("isFirst", photographer.isFirst());

// 첫 로그인이라면 isFirst 업데이트
if (photographer.isFirst()) {
photographer.markAsLoggedIn(); // 첫 로그인 이후 false로 변경
photographerRepository.save(photographer);
}
});
}

// 성공 응답 생성
SuccessResponseDto<Map<String, Object>> successResponse = SuccessResponseDto.success(
200,
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/ceos/phototoground/global/util/SpringContext.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package ceos.phototoground.global.util;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

@Component
public class SpringContext {
private static ApplicationContext context;

public SpringContext(ApplicationContext applicationContext) {
SpringContext.context = applicationContext;
}

public static <T> T getBean(Class<T> beanClass) {
return context.getBean(beanClass);
}
}
Loading