[회원 정보 개선] refresh_token 필드 길이 증가 및 MemberMapper에 DTO 업데이트 메서드 추가. MemberService에서 사용자 조회 및 업데이트 로직 개선, JWT 필터에서 refresh_token 저장 로직 제거.
This commit is contained in:
@@ -11,7 +11,7 @@
|
|||||||
name varchar(100) not null,
|
name varchar(100) not null,
|
||||||
password varchar(100) not null,
|
password varchar(100) not null,
|
||||||
user_id varchar(100) not null,
|
user_id varchar(100) not null,
|
||||||
refresh_token varchar(200),
|
refresh_token varchar(1024),
|
||||||
email varchar(255) not null,
|
email varchar(255) not null,
|
||||||
primary key (oid)
|
primary key (oid)
|
||||||
);
|
);
|
||||||
|
@@ -44,7 +44,7 @@ public class Member extends BaseEntity {
|
|||||||
@Builder.Default
|
@Builder.Default
|
||||||
private Boolean useFlag = true;
|
private Boolean useFlag = true;
|
||||||
|
|
||||||
@Column(name = "refresh_token", length = 200)
|
@Column(name = "refresh_token", length = 1024)
|
||||||
private String refreshToken;
|
private String refreshToken;
|
||||||
|
|
||||||
@Column(name = "last_login_at")
|
@Column(name = "last_login_at")
|
||||||
|
@@ -46,4 +46,14 @@ public interface MemberMapper {
|
|||||||
* MemberDto를 CreateMemberResponseDto로 변환
|
* MemberDto를 CreateMemberResponseDto로 변환
|
||||||
*/
|
*/
|
||||||
CreateMemberResponseDto toCreateMemberResponseDto(MemberDto memberDto);
|
CreateMemberResponseDto toCreateMemberResponseDto(MemberDto memberDto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MemberDto의 값으로 기존 Member 엔티티 업데이트 (null이 아닌 필드만)
|
||||||
|
*/
|
||||||
|
@Mapping(target = "oid", ignore = true)
|
||||||
|
@Mapping(target = "createdAt", ignore = true)
|
||||||
|
@Mapping(target = "updatedAt", ignore = true)
|
||||||
|
@Mapping(target = "createdOid", ignore = true)
|
||||||
|
@Mapping(target = "updatedOid", ignore = true)
|
||||||
|
void updateMemberFromDto(MemberDto memberDto, @org.mapstruct.MappingTarget Member member);
|
||||||
}
|
}
|
||||||
|
@@ -5,6 +5,7 @@ import org.springframework.stereotype.Repository;
|
|||||||
|
|
||||||
import com.bio.bio_backend.domain.base.member.entity.Member;
|
import com.bio.bio_backend.domain.base.member.entity.Member;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface MemberRepository extends JpaRepository<Member, Long> {
|
public interface MemberRepository extends JpaRepository<Member, Long> {
|
||||||
@@ -12,6 +13,21 @@ public interface MemberRepository extends JpaRepository<Member, Long> {
|
|||||||
// 사용자 ID로 회원 조회 (Optional 반환)
|
// 사용자 ID로 회원 조회 (Optional 반환)
|
||||||
Optional<Member> findByUserId(String userId);
|
Optional<Member> findByUserId(String userId);
|
||||||
|
|
||||||
|
// 사용자 ID로 첫 번째 회원 조회 (userId가 unique하지 않을 경우 대비)
|
||||||
|
Optional<Member> findFirstByUserId(String userId);
|
||||||
|
|
||||||
|
// 사용자 ID로 활성화된 회원 조회
|
||||||
|
Optional<Member> findByUserIdAndUseFlagTrue(String userId);
|
||||||
|
|
||||||
|
// 사용자 ID로 활성화된 회원 첫 번째 조회
|
||||||
|
Optional<Member> findFirstByUserIdAndUseFlagTrue(String userId);
|
||||||
|
|
||||||
|
// 사용자 ID 존재 여부 확인 (활성화된 회원만)
|
||||||
|
boolean existsByUserIdAndUseFlagTrue(String userId);
|
||||||
|
|
||||||
// 사용자 ID 존재 여부 확인
|
// 사용자 ID 존재 여부 확인
|
||||||
boolean existsByUserId(String userId);
|
boolean existsByUserId(String userId);
|
||||||
|
|
||||||
|
// 활성화된 회원 목록 조회
|
||||||
|
List<Member> findByUseFlagTrue();
|
||||||
}
|
}
|
@@ -1,5 +1,6 @@
|
|||||||
package com.bio.bio_backend.domain.base.member.service;
|
package com.bio.bio_backend.domain.base.member.service;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -14,8 +15,6 @@ public interface MemberService extends UserDetailsService {
|
|||||||
|
|
||||||
MemberDto createMember(MemberDto memberDTO);
|
MemberDto createMember(MemberDto memberDTO);
|
||||||
|
|
||||||
void updateRefreshToken(MemberDto memberDTO);
|
|
||||||
|
|
||||||
String getRefreshToken(String id);
|
String getRefreshToken(String id);
|
||||||
|
|
||||||
int deleteRefreshToken(String id);
|
int deleteRefreshToken(String id);
|
||||||
|
@@ -32,7 +32,7 @@ public class MemberServiceImpl implements MemberService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserDetails loadUserByUsername(String id) throws UsernameNotFoundException {
|
public UserDetails loadUserByUsername(String id) throws UsernameNotFoundException {
|
||||||
Member member = memberRepository.findByUserId(id)
|
Member member = memberRepository.findByUserIdAndUseFlagTrue(id)
|
||||||
.orElseThrow(() -> new UsernameNotFoundException("사용자를 찾을 수 없습니다: " + id));
|
.orElseThrow(() -> new UsernameNotFoundException("사용자를 찾을 수 없습니다: " + id));
|
||||||
|
|
||||||
// MapStruct를 사용하여 Member 엔티티를 MemberDto로 변환
|
// MapStruct를 사용하여 Member 엔티티를 MemberDto로 변환
|
||||||
@@ -45,7 +45,7 @@ public class MemberServiceImpl implements MemberService {
|
|||||||
@Transactional
|
@Transactional
|
||||||
public MemberDto createMember(MemberDto memberDto) {
|
public MemberDto createMember(MemberDto memberDto) {
|
||||||
// userId 중복 체크
|
// userId 중복 체크
|
||||||
if (memberRepository.existsByUserId(memberDto.getUserId())) {
|
if (memberRepository.existsByUserIdAndUseFlagTrue(memberDto.getUserId())) {
|
||||||
throw new ApiException(ApiResponseCode.USER_ID_DUPLICATE);
|
throw new ApiException(ApiResponseCode.USER_ID_DUPLICATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,19 +68,24 @@ public class MemberServiceImpl implements MemberService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public void updateRefreshToken(MemberDto memberDTO) {
|
public int updateMember(MemberDto memberDto) {
|
||||||
// JPA를 사용하여 refresh token 업데이트
|
Member member = memberRepository.findFirstByUserIdAndUseFlagTrue(memberDto.getUserId())
|
||||||
Member member = memberRepository.findByUserId(memberDTO.getUserId())
|
.orElseThrow(() -> new ApiException(ApiResponseCode.USER_NOT_FOUND));
|
||||||
.orElseThrow(() -> new RuntimeException("회원을 찾을 수 없습니다. id: " + memberDTO.getUserId()));
|
|
||||||
|
memberMapper.updateMemberFromDto(memberDto, member);
|
||||||
|
|
||||||
|
if (memberDto.getPassword() != null && !memberDto.getPassword().isEmpty()) {
|
||||||
|
member.setPassword(bCryptPasswordEncoder.encode(memberDto.getPassword()));
|
||||||
|
}
|
||||||
|
|
||||||
member.setRefreshToken(memberDTO.getRefreshToken());
|
|
||||||
memberRepository.save(member);
|
memberRepository.save(member);
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getRefreshToken(String id) {
|
public String getRefreshToken(String id) {
|
||||||
Member member = memberRepository.findByUserId(id)
|
Member member = memberRepository.findFirstByUserIdAndUseFlagTrue(id)
|
||||||
.orElseThrow(() -> new RuntimeException("회원을 찾을 수 없습니다. id: " + id));
|
.orElseThrow(() -> new ApiException(ApiResponseCode.USER_NOT_FOUND));
|
||||||
|
|
||||||
return member.getRefreshToken();
|
return member.getRefreshToken();
|
||||||
}
|
}
|
||||||
@@ -88,8 +93,8 @@ public class MemberServiceImpl implements MemberService {
|
|||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public int deleteRefreshToken(String id) {
|
public int deleteRefreshToken(String id) {
|
||||||
Member member = memberRepository.findByUserId(id)
|
Member member = memberRepository.findFirstByUserIdAndUseFlagTrue(id)
|
||||||
.orElseThrow(() -> new RuntimeException("회원을 찾을 수 없습니다. id: " + id));
|
.orElseThrow(() -> new ApiException(ApiResponseCode.USER_NOT_FOUND));
|
||||||
|
|
||||||
member.setRefreshToken(null);
|
member.setRefreshToken(null);
|
||||||
memberRepository.save(member);
|
memberRepository.save(member);
|
||||||
@@ -98,10 +103,8 @@ public class MemberServiceImpl implements MemberService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<MemberDto> selectMemberList(Map<String, String> params) {
|
public List<MemberDto> selectMemberList(Map<String, String> params) {
|
||||||
// JPA를 사용하여 회원 목록 조회 (간단한 구현)
|
List<Member> members = memberRepository.findByUseFlagTrue();
|
||||||
List<Member> members = memberRepository.findAll();
|
|
||||||
|
|
||||||
// MapStruct를 사용하여 Member 엔티티 리스트를 MemberDto 리스트로 변환
|
|
||||||
return memberMapper.toMemberDtoList(members);
|
return memberMapper.toMemberDtoList(members);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,29 +118,13 @@ public class MemberServiceImpl implements MemberService {
|
|||||||
return memberMapper.toMemberDto(member);
|
return memberMapper.toMemberDto(member);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
@Transactional
|
|
||||||
public int updateMember(MemberDto memberDto) {
|
|
||||||
Member member = memberRepository.findByUserId(memberDto.getUserId())
|
|
||||||
.orElseThrow(() -> new RuntimeException("회원을 찾을 수 없습니다. id: " + memberDto.getUserId()));
|
|
||||||
|
|
||||||
// 비밀번호가 변경된 경우 암호화
|
|
||||||
if (memberDto.getPassword() != null && !memberDto.getPassword().isEmpty()) {
|
|
||||||
member.setPassword(bCryptPasswordEncoder.encode(memberDto.getPassword()));
|
|
||||||
}
|
|
||||||
|
|
||||||
member.setRole(memberDto.getRole());
|
|
||||||
member.setUseFlag(memberDto.getUseFlag());
|
|
||||||
|
|
||||||
memberRepository.save(member);
|
|
||||||
return 1; // 성공 시 1 반환
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public int deleteMember(MemberDto memberDto) {
|
public int deleteMember(MemberDto memberDto) {
|
||||||
Member member = memberRepository.findByUserId(memberDto.getUserId())
|
Member member = memberRepository.findFirstByUserId(memberDto.getUserId())
|
||||||
.orElseThrow(() -> new RuntimeException("회원을 찾을 수 없습니다. id: " + memberDto.getUserId()));
|
.orElseThrow(() -> new ApiException(ApiResponseCode.USER_NOT_FOUND));
|
||||||
|
|
||||||
member.setUseFlag(false);
|
member.setUseFlag(false);
|
||||||
|
|
||||||
|
@@ -9,14 +9,15 @@ import com.bio.bio_backend.global.dto.ApiResponseDto;
|
|||||||
import com.bio.bio_backend.domain.base.member.dto.LoginRequestDto;
|
import com.bio.bio_backend.domain.base.member.dto.LoginRequestDto;
|
||||||
import com.bio.bio_backend.domain.base.member.dto.LoginResponseDto;
|
import com.bio.bio_backend.domain.base.member.dto.LoginResponseDto;
|
||||||
import com.bio.bio_backend.domain.base.member.dto.MemberDto;
|
import com.bio.bio_backend.domain.base.member.dto.MemberDto;
|
||||||
|
import com.bio.bio_backend.domain.base.member.service.MemberService;
|
||||||
import com.bio.bio_backend.global.constants.ApiResponseCode;
|
import com.bio.bio_backend.global.constants.ApiResponseCode;
|
||||||
import com.bio.bio_backend.global.utils.JwtUtils;
|
import com.bio.bio_backend.global.utils.JwtUtils;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.SneakyThrows;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
|
import org.springframework.security.authentication.AuthenticationServiceException;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
@@ -36,15 +37,20 @@ public class JwtTokenIssuanceFilter extends UsernamePasswordAuthenticationFilter
|
|||||||
private final AuthenticationManager authenticationManager;
|
private final AuthenticationManager authenticationManager;
|
||||||
private final JwtUtils jwtUtils;
|
private final JwtUtils jwtUtils;
|
||||||
private final ObjectMapper objectMapper;
|
private final ObjectMapper objectMapper;
|
||||||
|
private final MemberService memberService;
|
||||||
|
|
||||||
// 사용자 login 인증 처리
|
// 사용자 login 인증 처리
|
||||||
@SneakyThrows
|
|
||||||
@Override
|
@Override
|
||||||
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
|
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
|
||||||
LoginRequestDto requestDto = new ObjectMapper().readValue(request.getInputStream(), LoginRequestDto.class);
|
try {
|
||||||
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(requestDto.getUserId(), requestDto.getPassword());
|
LoginRequestDto requestDto = new ObjectMapper().readValue(request.getInputStream(), LoginRequestDto.class);
|
||||||
|
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
|
||||||
|
requestDto.getUserId(), requestDto.getPassword());
|
||||||
|
|
||||||
return authenticationManager.authenticate(authToken);
|
return authenticationManager.authenticate(authToken);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new AuthenticationServiceException("로그인 요청 파싱 중 오류가 발생했습니다", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 사용자 인증 성공 후 token 발급
|
// 사용자 인증 성공 후 token 발급
|
||||||
@@ -59,11 +65,9 @@ public class JwtTokenIssuanceFilter extends UsernamePasswordAuthenticationFilter
|
|||||||
String accessToken = jwtUtils.createAccessToken(member.getUserId(), member.getRole().getValue());
|
String accessToken = jwtUtils.createAccessToken(member.getUserId(), member.getRole().getValue());
|
||||||
String refreshToken = jwtUtils.createRefreshToken(member.getUserId(), member.getRole().getValue());
|
String refreshToken = jwtUtils.createRefreshToken(member.getUserId(), member.getRole().getValue());
|
||||||
|
|
||||||
// Refresh 토큰을 DB에 저장
|
|
||||||
jwtUtils.saveRefreshToken(member.getUserId(), refreshToken);
|
|
||||||
|
|
||||||
member.setRefreshToken(refreshToken);
|
member.setRefreshToken(refreshToken);
|
||||||
member.setLastLoginAt(LocalDateTime.now());
|
member.setLastLoginAt(LocalDateTime.now());
|
||||||
|
memberService.updateMember(member);
|
||||||
|
|
||||||
// Refresh 토큰 쿠키 저장
|
// Refresh 토큰 쿠키 저장
|
||||||
jwtUtils.setRefreshTokenCookie(response, refreshToken);
|
jwtUtils.setRefreshTokenCookie(response, refreshToken);
|
||||||
|
@@ -37,7 +37,7 @@ public class WebSecurity {
|
|||||||
private final SecurityPathConfig securityPathConfig;
|
private final SecurityPathConfig securityPathConfig;
|
||||||
|
|
||||||
private JwtTokenIssuanceFilter getJwtTokenIssuanceFilter(AuthenticationManager authenticationManager) throws Exception {
|
private JwtTokenIssuanceFilter getJwtTokenIssuanceFilter(AuthenticationManager authenticationManager) throws Exception {
|
||||||
JwtTokenIssuanceFilter filter = new JwtTokenIssuanceFilter(authenticationManager, jwtUtils, objectMapper);
|
JwtTokenIssuanceFilter filter = new JwtTokenIssuanceFilter(authenticationManager, jwtUtils, objectMapper, memberService);
|
||||||
filter.setFilterProcessesUrl("/login");
|
filter.setFilterProcessesUrl("/login");
|
||||||
filter.setAuthenticationFailureHandler(new CustomAuthenticationFailureHandler(objectMapper));
|
filter.setAuthenticationFailureHandler(new CustomAuthenticationFailureHandler(objectMapper));
|
||||||
return filter;
|
return filter;
|
||||||
|
@@ -105,25 +105,10 @@ public class JwtUtils {
|
|||||||
Long.parseLong(Objects.requireNonNull(env.getProperty("token.expiration_time_refresh"))));
|
Long.parseLong(Objects.requireNonNull(env.getProperty("token.expiration_time_refresh"))));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh Token을 DB에 저장
|
|
||||||
public void saveRefreshToken(String username, String refreshToken) {
|
|
||||||
MemberDto member = new MemberDto();
|
|
||||||
member.setUserId(username);
|
|
||||||
member.setRefreshToken(refreshToken);
|
|
||||||
memberService.updateRefreshToken(member);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Refresh Token 생성 및 DB 저장 (기존 호환성을 위한 메서드)
|
|
||||||
public String createAndSaveRefreshToken(String username, String role) {
|
|
||||||
String refreshToken = createRefreshToken(username, role);
|
|
||||||
saveRefreshToken(username, refreshToken);
|
|
||||||
return refreshToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Refresh Token 갱신 (Access Token 갱신 시 함께)
|
// Refresh Token 갱신 (Access Token 갱신 시 함께)
|
||||||
public String refreshTokens(String username, String role) {
|
public String refreshTokens(String username, String role) {
|
||||||
// 새로운 Refresh Token 생성 및 DB 저장
|
// 새로운 Refresh Token 생성 및 DB 저장
|
||||||
String newRefreshToken = createAndSaveRefreshToken(username, role);
|
String newRefreshToken = createRefreshToken(username, role);
|
||||||
|
|
||||||
log.info("Refresh Token 갱신 완료: {}", username);
|
log.info("Refresh Token 갱신 완료: {}", username);
|
||||||
return newRefreshToken;
|
return newRefreshToken;
|
||||||
|
Reference in New Issue
Block a user