Spring은 익숙해지지 않아서 그런지 모르겠는데 절차지향이나 객체지향과는 다르게 뭐랄까 코드가 다 흩어져서 둥둥 떠다니는 느낌 흐름을 못읽겠다ㅠㅠ
Spring Security을 사용하면 Controller을 거치치 않고 로그인 절차를 할 수 있다고 한다. 아직 그렇게 해서 얻는게 뭔지는 이해 못했지만;; 아무튼 실제로 강의에서 만들어뒀던 Controller와 Service에서 로그인과 관련된 코드를 모두 주석처리 했음에도 잘 동작했다.
그런데 그럼 DB에 쿼리는 누가 날리고 있는거지??하는 생각이 들었다.
강의를 그냥 그러쿠나 끄덕끄덕하고 멍때렸더니 문제가 생겼다. 그렇게 삽질이 시작됐다.
// 필터 관리
http.addFilterBefore(jwtAuthorizationFilter(), JwtAuthenticationFilter.class);
http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
WebSecurityCongig class에서 아래 두줄을 추가했으므로 필터 순서는
1. JwtAuthorizationFilter
2. JwtAuthenticationFilter
...???(중략)
3. UsernamePasswordAuthenticationFilter이다.
@Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain filterChain) throws ServletException, IOException {
log.info("doFilterInternal " + req.getRequestURI());
String tokenValue = jwtUtil.getTokenFromRequest(req);
if (StringUtils.hasText(tokenValue)) {
// JWT 토큰 substring
tokenValue = jwtUtil.substringToken(tokenValue);
log.info(tokenValue);
if (!jwtUtil.validateToken(tokenValue)) {
log.error("Token Error");
return;
}
Claims info = jwtUtil.getUserInfoFromToken(tokenValue);
try {
setAuthentication(info.getSubject());
} catch (Exception e) {
log.error(e.getMessage());
return;
}
}
filterChain.doFilter(req, res);
}
로그인 버튼을 누르면 JwtAuthorizationFilter의 필터 부분으로 들어온다.
처음엔 토큰이 없으므로 JwtAuthenticationFilter 체인으로 넘어간다.
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
log.info("로그인 시도");
try {
LoginRequestDto requestDto = new ObjectMapper().readValue(request.getInputStream(), LoginRequestDto.class);
return getAuthenticationManager().authenticate(
new UsernamePasswordAuthenticationToken(
requestDto.getUsername(),
requestDto.getPassword(),
null
)
);
} catch (IOException e) {
log.error(e.getMessage());
throw new RuntimeException(e.getMessage());
}
}
doFilter가 아니라 이부분으로 넘어오는 것 같다. 여기서 입력된 아이디와 비밀번호를 받아서
authenticate에서 쿼리를 날린다고 한다.

아무튼간에 Detail어쩌구 하는 부분에서 쿼리를 날리니까 여기에 중단점을 걸었다.

JwtAuthenticationFilter에 authenticate함수 안으로 들어가면

ProviderManager라는 곳에서 똑같은 이름의 함수를 호출한다. 또 타고 들어가면

retrieveUser라는 함수를 호출한다. 요기로 들어가면

오.. 익숙한 함수가 보인다. LoadUserByUsername함수로 들어가면
@Service
@Slf4j(topic = "UserDetailsServiceImpl")
public class UserDetailsServiceImpl implements UserDetailsService {
private final UserRepository userRepository;
public UserDetailsServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
log.info("loadUserByUsername " + username);
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("Not Found " + username));
return new UserDetailsImpl(user);
}
}
@Bean에 등록해뒀던 UserDetailsService을 상속받아 구현한 UserDetailsServiceImpl에서 오버라이딩한 loadUserByUsername쪽으로 들어오는 모양이다.

그러니까 필터를 통과하면서 저 Proxy..?뭔가 안에서 연결해둔 Detail가서 유저 정보 뽑아오고 그걸로 로그인 검증을 하는 것 같다!
사실 아직도 두루뭉실..하고.. Spring을 잘 몰라서 @Bean이 거의 달아두면 아무튼간에 아무데서나 쓸 수 있는 마법의 Static처럼 보이는 상태라.. 반복하면서 이해하는 것 밖에 없는 것 같다.