Kuma's Curious Paradise
[이룸] 240229 JwtAuthenticationFilter.java 본문
- 클래스명의 이유
- JwtAuthenticationFilter : jwt 토큰으로 Authentication(인증)을 하는 Filter 클래스.
- Filter란? 반복적으로 들어오는 객체에서 특정 조건을 걸러내는 역할을 함. servlet 컨테이너 입구에서 인코딩, 로깅, 인증과 같은 전역 작업을 처리하며, filter와 비슷한 역할을 하는 것에는 interceptor와 AOP가 있음.
- UsernamePasswordAuthenticationFilter란? id와 password를 사용하여 인증을 처리하는 데 사용되는 필터. HTTP 요청에서 id와 password 추출해서 Authentication 객체를 생성하고, 해당 객체를 AuthenticationManager에게 전달하여 인증을 시도.
- 클래스에 붙은 어노테이션
- **@Slf4j(topic = "로그인 및 JWT 생성")** : Logger 객체를 자동으로 생성하여 log.info() 와 같은 메서드를 쓸 수 있게 해 주는 lombok 라이브러리의 일부. topic 속성을 통해 로거의 이름을 지정할 수 있음.
의존성 주입
public JwtAuthenticationFilter(JwtUtil jwtUtil) {
this.jwtUtil = jwtUtil;
setFilterProcessesUrl("/api/login");
}
- setFilterProcessesUrl("/api/login"); : 해당 필터가 요청을 처리해야 하는 url을 설정하는 메서드. 쉽게 말하면 /api/login 으로 요청이 들어오면 이 필터를 실행시킴.
- 일반적으로 필터 생성자에서 의존성 주입과 작동 url을 함께 설정함. 어떤 종류의 인증 작업을 수행하는지, 어떤 Url에서 동작하는지 한눈에 파악할 수 있기 때문이라고 함.
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
try {
LoginRequestDto requestDto = new ObjectMapper().readValue(request.getInputStream(), LoginRequestDto.class);
log.info("로그인 시도 - 사용자: {}", requestDto.getEmail());
return getAuthenticationManager().authenticate(
new UsernamePasswordAuthenticationToken(
requestDto.getEmail(),
requestDto.getPassword()
)
);
} catch (IOException e) {
log.error("로그인 시도 중 오류 발생: {}", e.getMessage());
throw new AuthenticationServiceException("로그인 시도 중 오류가 발생했습니다.", e);
}
}
- @Override: UsernamePasswordAuthenticationFilter 클래스의 attemptAuthentication() 메서드를 오버라이드
- HttpServletRequest 객체로부터 요청을 처리하고, 사용자가 제공한 로그인 정보를 추출
- **LoginRequestDto requestDto = new ObjectMapper().readValue(request.getInputStream(), LoginRequestDto.class);** :
request.getInputStream()으로 http 요청에서 body를 읽어냄. → readValue()로 해당 JSON 데이터를 Java 객체(여기서는 LoginRequestDto 클래스의 객체)로 변환. jackson 라이브러리에서 제공하는 ObjectMapper 클래스 메서드 중 하나. → 쉽게 말해, **ObjectMapper**를 사용하여 JSON을 Java 객체로 매핑하는 과정 - getAuthenticationManager()로 인증 매니저 호출. 이 매니저는 인증 객체를 생성하고 인증을 시도하는 역할. → authenticate() 메서드를 호출하여 사용자명과 비밀번호를 가진 UsernamePasswordAuthenticationToken 객체를 생성하여 인증 매니저에게 전달 → 인증 매니저는 전달된 인증 객체를 사용하여 사용자를 인증하고, 인증 결과를 반환
- 만약 로그인 시도 중에 예외가 발생하면, 해당 예외를 catch하여 로그에 오류를 기록하고**AuthenticationServiceException**을 던짐
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
String username = ((UserDetailsImpl) authResult.getPrincipal()).getUsername();
MemberRoleEnum role = ((UserDetailsImpl) authResult.getPrincipal()).getMember().getRole();
String accessToken = jwtUtil.createAccessToken(username, role);
String refreshToken = jwtUtil.createRefreshToken(username);
jwtUtil.addJwtToCookie(accessToken, response, JwtUtil.AUTHORIZATION_HEADER);
jwtUtil.addJwtToCookie(refreshToken, response, JwtUtil.REFRESH_TOKEN_HEADER);
log.info("사용자 '{}'의 로그인 성공 및 JWT 생성", username);
}
- **String username = ((UserDetailsImpl) authResult.getPrincipal()).getUsername();** : Authentication 객체인 authResult를 파라미터로 받음. → getPrincipal() 호출하여 사용자의 인증 정보, principal 객체 추출 → authentication provider는 이 principal 객체를 userDetails 객체로 바꿔줌 → (UserDetailsImpl) 객체로 업캐스팅 → 이후 getUsername() 호출하여 username 추출
이후 같은 방식으로 사용자의 역할을 추출→ accessToken과 refreshToken 생성 → 쿠키에 담기
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
log.info("로그인 실패 - {}", failed.getMessage());
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
- 로그인에 실패하면 401 반환
'이룸 프로젝트' 카테고리의 다른 글
[이룸] 240304 카카오 로그인 구현하기 (0) | 2024.03.07 |
---|---|
[이룸] 240302 JwtAuthorizationFilter.java (0) | 2024.03.07 |
[이룸] 240227 JwtUtil.java (0) | 2024.03.07 |
[이룸] 240226 intellij / cannot resolve column 노란줄 (0) | 2024.03.07 |
[이룸] 240225 refresh token 관리에 redis 도입 (0) | 2024.03.07 |