이 글 보고 참고했다
이 문제가 발생한 경우는 “회원 정보 수정” 기능에 User를 @AuthenticationPrincipal
로 가져와야 하는건데 자꾸 나의 User가 null으로 받아와지지 않았다 ㅠㅠ
글을 쭉 읽으면서 security 코드를 천천히 비교하며 보는데 !
내 경우의 문제는 CustomUserDetailsService의 loadByUserName 함수가 email으로 되어 있어서 그런 것이라고 처음에 접근했다
근데 사실 유저의 바뀌지 않는 값은 email이 맞으니 해당 함수는 문제가 없었다.
그래서 loadUserByUserName
의 내부 findByEmail
메소드를 findByName
메소드로 변경했다
근데 findByName
으로 바꾸니까 자꾸 존재하지 않는 계정으로 오히려 login에서 터지기 시작했다..
이유는 로그인할 때 자동으로 권한을 얻기 위해 override된 loadUserByUsername
을 자동으로 호출하기 때문에 당연히 findByEmail
부분을 findByName
으로 바꾸니까 이름으로 유저를 식별하지 못하는 것이었다 ㅇㅅㅇ → 그래서 로그인이 안 되는 것이었음 email으로 식별할 땐 가능했음
그리고 JwtTokenProvider
에서도 문제가 있었다
위의 코드를 보면 맨 처음 문제가 있던 코든데 User를 새로 만들어서 반환값에 넣어 반환하기 때문에 기존의 유저를 찾지 못하는 상황이다.(new User(claims.getSubject(), “”, authorities))
문제는 @AuthenticationPrincipal
에서 User를 찾을때 CustomerUserDetailsService
의 loadByUserName
을 호출해서 찾는다는 것이었다.
그래서 나는 다시 CustomerUserDetailsService를 해당 메소드에 넣어야 이 문제가 해결이 되겠다고 생각했다
그래서 그냥 아무 생각없이 JwtTokenProvider에 CustomerUserDetailsService를 넣고 loadByUserName를 사용하면 되겠지 라고 생각했다
ㅋㅋ 근데 자꾸 사용자를 식별할 수 없다는 문제가 뜨는것이었다~
그래서 뭐가 문제지 하고 게속 참고글을 읽고 읽고 디버그를 찍었다
디버그를 찍는 과정에서 @AuthenticationPrincipal의 user가 자꾸 null인게 보였다 그래서 과정을 따라갔다
JwtFilter에서 호출하는 JwtTokenProvider의 getAuthentication(인증 얻기) 함수를보자
혹시나..? 하고 username을 디버깅 찍었을 때 계속 email이 아닌 name으로 사용자를 식별하고 있는 것이었다… username = “장수연”
그래서 처음에는 name도 중복이 안되니까 email이 아니라 name으로 찾아버리자~ 하고
이렇게 정의를 해줬었다.
참고로 아래와 같이 loadUserByName은 @Override가 아니라 내가 정의한 메소드다.
package com.devcourse.ReviewRanger.user.application;
import static com.devcourse.ReviewRanger.common.exception.ErrorCode.*;
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.Component;
import com.devcourse.ReviewRanger.common.exception.RangerException;
import com.devcourse.ReviewRanger.user.domain.UserPrincipal;
import com.devcourse.ReviewRanger.user.repository.UserRepository;
@Component
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
public CustomUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
return userRepository.findByEmail(email)
.map(UserPrincipal::new)
.orElseThrow(() -> new RangerException(FAIL_USER_LOGIN));
}
public UserDetails loadUserByName(String name) throws UsernameNotFoundException {
return userRepository.findByName(name)
.map(UserPrincipal::new)
.orElseThrow(() -> new RangerException(FAIL_USER_LOGIN));
}
}
당연히 처음에는 문제가 없겠지
근데 그 이후가 문제였다. 사용자의 “이름”은 회원정보 수정을 통해 바뀔 수 있는 값이었다.
그래서 식별값으로서 적절하지 않아 그 다음에 변경을 하려고 할 때는 이런 오류가 났다
처음에는 장수연으로 가입하고 유저정보를 바꾸려고 하면 username이 장수연으로 식별되어 정보가 바뀔 수 있다
근데 한번 더 요청을 하면?
db에 있는 이름이 바뀌어서 auth에 있는 이름과 일치하지 않게되어 이름으로 유저를 식별할 수 없는 것이다
그러니까 무조건 바뀌지 않는 email 을 String username = claims.getSubject();
의 결과로 받아와야 한다
어떻게 바꿀 수 있을까..? 하고 계속 보다가
UserDetails user = userDetailService.loadUserByUsername(username);
의 반환 타입이 UserDetails인걸 보고 UserDetails를 구현한 UserPrincipal
에 정답이 있나..? 하고 쓱 쳐다봤다
ㅋㅋ 찾았다 똑같은 Username… 이거를 user.getEmail로 바꿔주었더니
바로 email로 식별이 가능해졌다. claims.getSubject의 반환값이 유저 email로 잘 넘어왔다 ㅎㅎㅎ
그래서 내가 정의한 바로 밑에 줄의 loadUserByName 메소드도 지우고 loadUserByUsername 변경했다
(어차피 안에 findByEmail으로 user를 찾기 때문에 email 넘겨주면 찾을 수 있음)
결론적으로 계~속 이름과 비밀번호를 바꿔도 문제없이 변경되는 것을 확인할 수 있게 되었다!
User가 아주 잘 받아와지는 모습을 확인할 수 있다!!! 만세 ㅠㅠ
'Web > Spring' 카테고리의 다른 글
[Spring Security] 스프링 시큐리티 에러 메시지 국제화 (0) | 2023.12.14 |
---|---|
[Spring Security] jwt 401 응답 + jwt 예외 핸들러 만들기 (1) | 2023.11.29 |
[인프런] 스프링부트 개념정리(이론) - 1 (1) | 2022.10.05 |
[3] 스프링부트로 웹 서비스 출시하기 - 3. SpringBoot & Handlebars로 화면 만들기 (0) | 2022.08.13 |
[JUnit4] 테스트 라이브러리 기본 사용법 (0) | 2022.08.05 |